Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Pouze tento týden sleva až 80 % na e-learning týkající se Java. Zároveň využij akce až 80 % zdarma při nákupu e-learningu. Více informací:

Lekce 9 - 3D bludiště v XNA - Kolize podruhé

Vítejte po devatenácté. Posledně, 3D bludiště v XNA - Kolize poprvé, jsme si připravili velmi primitivní kolizní manažer, který se nám postará o základní kolize.

V tomto díle si jej dokončíme, přidáme mu metodu pro hledání kolize a také si kolize vyzkoušíme. Jdeme na to.

Ještě než přidáme kolizní metodu, tak si přidáme vykreslení všech kolizních tvarů. To se myslím bude velmi hodit. Otevřeme si třídu s manažerem a přidáme si tam proměnnou, se kterou budeme vykreslování povolovat nebo zakazovat.

public bool DebugDraw=true;

A dále metodu Draw, kde vše vykreslíme, máme-li to dovoleno.

public void Draw(Matrix View, Matrix Projection){
  if (DebugDraw){
    foreach (BoundingBox box in Boxes){
      BoundingRenderer.Render(box, View, Projection, Color.Black);
    }
  }
}

Ještě si ji v herním okně zavoláme:

public override void Draw(){
  base.Draw();
  if (CollisionManager.DebugDraw) CollisionManager.Draw(Kamera.View, Kamera.Projection);
}

A to je vše. Není to brnkačka? Celý systém se nám opět začíná vyplácet a vložená námaha vracet. Stejně jako to bylo s komponentami. Řešení celého bludiště pak bylo otázkou jen pár řádků. Poslední věcí v základním kolizním manažeru je bezesporu metoda pro řešení kolizí.

Hledání kolize

V zájmu největší jednoduchosti bude tato metoda velmi snadná. Přidáme si tedy metodu Collide. Jako parametry dovnitř vstupuje kolizní koule, u které chceme, aby se zkontrolovala kolize. Dále stará poloha koule a poloha nová. Kouli otestujeme vůči všem zaregistrovaným krabicím a kolidující si uložíme do pole. Pokud v poli nic není, nedošlo k žádné kolizi a my můžeme v klidu s koulí hnout na novou pozici. Pokud ale v poli nějaké prvky máme, tak pohyb koule nedovolíme a vrátíme starou pozici. Je to velmi primitivní způsob, nicméně pro začátek dostačuje velmi dobře. Přídáme si tedy tuto metodu:

public Vector3 Collide(BoundingSphere sphere, Vector3 old, Vector3 nova)

Projedeme všechny zaregistrované krabice a zkontrolujeme zda kolidují. Pokud ano, přidáme je do pole:

List<BoundingBox> colliding = new List<BoundingBox>();
foreach (BoundingBox box in Boxes){
  if (sphere.Intersects(box)){
    colliding.Add(box);
  }
}

A potom pouze testujeme na velikost pole:

if (colliding.Count != 0){
  return old;
}
return nova;

To je vše. Opravdu to je vše. Možná se ptáte proč dáváme kolidující krabice do pole, když pro ně zatím nemáme žádné využití. Ano nemáme pro to žádné použití. Prozatím. A nyní k tomu vytouženému bodu...

Používáme kolizní manažer

Máme vše více-méně hotovo a tak můžeme přikročit k reálné aplikaci na našem bludišti. Budeme nyní výhradně pracovat v naší hře. Třída s naším herním oknem nebude dědit od základního okna, ale od okna umožňující kolize, takže tedy z

public class MojeHerniOkno: GameScreen

převedeme na:

public class MojeHerniOkno: CollidableGameScreen

Jediný objekt, který se kterým budeme chtít kolidovat bude políčko se zdí. To uděláme tak, že pouze opět změníme třídu od které budeme dědit. Takže tedy

public class Zed: Model3D

převedeme na

public class Zed: CollidableModel3D

A to je opět vše co musíme provést. Není nyní ten život tak snadný? :-) Jistěže je. O nic se dál nestaráme, vše se vypočítá samo, vše se všude přidá samo zavoláme pak metodu pro kolizi a ono se vše vyřeší a my jen aplikujeme výsledek. Celkem fajn servis, však trvalo nějakou tu dobu, než jsme jej napsali, ale přesto to byla dobrá investice.

Pro testovací zkoušení si vytvoříme kouli, se kterou budeme hýbat třeba klasickou sadou kláves WASD. Přidáme si tedy nový soubor s komponentou. Nazveme ji třeba PokusnaKulicka a do ní si dáme proměnnou pro kouli:

BoundingSphere koule;

Přepíšeme metodu Load a v ní ji vytvoříme. Poloměr dáme 5 jednotek, krabice jsou velké 20, takže bude kolem koule bude dost místa. Pozici dáme třeba (-10,5,0):

koule = new BoundingSphere(new Vector3(-10, 5f, 0), 5);

Přidáme metodu Update, kde budeme koulí pohybovat. Vlastně můžeme tuto část okopírovat z kamery, takže telegraficky:

Vector3 posun = Vector3.Zero;
if (Parent.Engine.Input.DrzenaKlavesa(Keys.S)) posun += Vector3.Forward;
if (Parent.Engine.Input.DrzenaKlavesa(Keys.W)) posun += Vector3.Backward;
if (Parent.Engine.Input.DrzenaKlavesa(Keys.D)) posun += Vector3.Left;
if (Parent.Engine.Input.DrzenaKlavesa(Keys.A)) posun += Vector3.Right;

Ničím násobit nebudeme, rychlost je tak akorát. Je sice závislá na FPS, ale to nám v tomto zkušebním případu vadit nebude. Starou pozici středu si uložíme a kouli posuneme na nové místo:

Vector3 stara = koule.Center;
koule.Center += posun;

Pak pokud je posun nenulový, zkontrolujeme kolize a výsledek uložíme zpět do pozice středu koule. Zároveň uděláme kontrolu, zda-li je okno ve kterém je komponenta přidána herní okno, které kolize ovládá:

if (posun != Vector3.Zero && Parent is CollidableGameScreen){
  CollidableGameScreen okno = Parent as CollidableGameScreen;
  koule.Center = okno.CollisionManager.Collide(koule, stara, koule.Center);
}

Vualáá to je vše. Opravdu. Už jen tedy kouli vykreslíme, třeba žlutě:

BoundingRenderer.Render(koule, View, Projection, Color.Yellow);

Komponentu přidáme v herním okně mezi ostatní, ale až jako úplně poslední:

AddComponent(new PokusnaKulicka());

Pokud nyní hru spustíme, tak můžeme koulí po obrazovce hýbat a snad nám i koliduje se zdmi. Pokud ne, nebojte se ozvat v komentářích.

Ještě jedna drobnost. Hodilo by se kouli umístit přesně na místo startu, bude se to hodit pak později pro kameru s hráčem. K tomu bude potřeba menší změna v komponentě s mapou. Do načítání mapy přidáme pouze uložení počáteční dlaždice:

public Vector3 Start;

a do switche dáme výpočet této hodnoty:

case 99:{
  c = new StartovniPodlaha(i, j);
  Start = new Vector3(10+i*20, 0, 10+j*20);
  break;
}

Používaný vzorec je stejný jako u minulých výpočtů pozic. Vytvoříme konstruktor u třídy s pokusnou koulí a přidáme mu parametrem mapu bludiště:

Mapa mapa;

public PokusnaKulicka(Mapa map){
  mapa = map;
}

Tu si uložíme a pak ji v načítání použijeme následovně:

koule = new BoundingSphere(new Vector3(mapa.Start.X, 5f, mapa.Start.Z), 5);

Vzoreček na výpočet polohy je stejný jako u tvorby bludiště. Výšku si raději nastavíme opět na poloměr, aby nebyla koule zabořená v podlaze.

To je myslím pro dnešek všechno. Jistě jste si všimli menší mezery v našem plánu. A nebo možná ani ne, ale to nevadí.

Příště, 3D bludiště v XNA - Kolize potřetí a snad naposledy, si ji pokusíme odstranit. Do té doby můžete komentovat, kritizovat, dotazovat se třeba v komentářích pod články.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 182x (1.78 MB)
Aplikace je včetně zdrojových kódů v jazyce C# XNA

 

Předchozí článek
3D bludiště v XNA - Kolize poprvé
Všechny články v sekci
3D bludiště v XNA
Přeskočit článek
(nedoporučujeme)
3D bludiště v XNA - Kolize potřetí a snad naposledy
Článek pro vás napsal vodacek
Avatar
Uživatelské hodnocení:
2 hlasů
Vodáček dělá že umí C#, naplno se již pět let angažuje v projektu ŽvB. Nyní studuje na FEI Upa informatiku, ikdyž si připadá spíš na ekonomice. Není mu také cizí PHP a SQL. Naopak cizí mu je Java a Python.
Aktivity