9. díl - 3D bludiště v XNA - Kolize podruhé

C# .NET XNA game studio 3D bludiště 3D bludiště v XNA - Kolize podruhé

Vítejte po devatenácté. Posledně 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í. Pokusíme si ji příště odstranit. Do té doby můžete komentovat, kritizovat, dotazovat se třeba v komentářích pod články.


 

Stáhnout

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

 

  Aktivity (1)

Článek pro vás napsal vodacek
Avatar
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.

Jak se ti líbí článek?
Celkem (2 hlasů) :
55555


 


Miniatura
Všechny články v sekci
3D bludiště v XNA

 

 

Komentáře

Avatar
sucharda.ondra:

Výborný tutorál, velké díky autorovi. Chci se zeptat jestli lze při vykreslování Boundingboxu nastavit aby se nevykreslovala barva? Tzn. nechci aby bylo vidět že krychle je obalená do krabice, která řeší kolize.

 
Odpovědět 4.4.2013 11:02
Avatar
vodacek
Redaktor
Avatar
Odpovídá na sucharda.ondra
vodacek:

autor přijímá jakékoliv dary materiální ale i nemateriální povahy

a nějak nechápe to na co se ptáš

 
Odpovědět 4.4.2013 11:23
Avatar
Odpovídá na vodacek
sucharda.ondra:

No pokud máme udělané zdi s vyřešenou kolizí, tak kolem zdí jsou černé čáry, které ohraničují tu krabici pro kolize a vypadá to nepřirozeně. Tzn. když přidáváme do VertexPosition­Color, tak máme nastavenou černou bavu ke každému vrcholu. Jde nějak udělat aby byla barva průhledná? Když jí k vrcholům nedáme tak bude bílá a to vypadá ještě hůř.

 
Odpovědět 4.4.2013 14:29
Avatar
vodacek
Redaktor
Avatar
Odpovídá na sucharda.ondra
vodacek:

ideálně nekreslit vůbec

 
Odpovědět 4.4.2013 14:39
Avatar
magic44
Redaktor
Avatar
magic44:

Můžu se zeptat, kde je v přiloženém kódu přepsaná metoda Draw(). Ukazuješ jí jako třetí v tomto článku. Nevím, jestli jsem jí přehléhl, ale nenašel jsem jí.
Mimochodem dobrej a srozumitelnej článek.

Odpovědět 27.5.2013 9:15
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
Avatar
vodacek
Redaktor
Avatar
Odpovídá na magic44
vodacek:

je ve třídě CollisionManager

 
Odpovědět 27.5.2013 11:04
Avatar
magic44
Redaktor
Avatar
Odpovídá na vodacek
magic44:

Ne já myslel tuhle:

public override void Draw(){
  base.Draw();
  if (CollisionManager.DebugDraw) CollisionManager.Draw(Kamera.View, Kamera.Projection);
}
Odpovědět 27.5.2013 11:27
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
Avatar
vodacek
Redaktor
Avatar
Odpovídá na magic44
vodacek:

ááha ta tam fakt není, opravíme :-)

 
Odpovědět 27.5.2013 11:53
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 8 zpráv z 8.