Lekce 8 - 3D bludiště v XNA - Kolize poprvé
Vítejte po osmnácté. V předchozí lekci, 3D bludiště v XNA - Krabice a koule, jsme psali pomocné třídy pro vykreslování krabic a koulí.
V dnešním díle se pokusíme o kolizní manažer. Skoro určitě se to nevejde do jednoho dílu.
Již jsem se o tom, jak to bude vše fungovat, zmínil v jednom z předchozích článků, ale přesto radši znovu vše osvětlím. Každý model, se kterým se má kolize odehrávat, si do kolizního manažeru zaregistruje svojí krabici. Pak už jen při každém pohybu pohyblivého objektu zkontrolujeme, zda-li nám koliduje a pokud ano, pohyb nepovolíme. To je vše. Nic víc, nic míň. Pusťme se tedy do toho.
Kromě toho co jsem naznačil výše bude potřeba vytvořit si speciální herní okno s kolizním manažerem. Ve všech situacích jej nebude potřeba používat, třeba pro menu a nebo pro jiné hry. Proto potřebujeme novou třídu. Stejně naložíme i s modelem. Opět vytvoříme vlastní třídu a to vše jen protože ne všechny modely potřebují, aby se s nimi kolidovalo.
Vytvoříme si složku Collision
, kam budeme třídy
související s kolizemi skladovat. Přidáme si do ní třídu
CollisionManager
. To je právě ta třída, která se nám stará o
řešení kolizí. Učiníme ji veřejnou, upravíme jí jmenný prostor.
Základem bude seznam všech krabic, se kterými budeme kolidovat. Přidáme si
jej:
protected List<BoundingBox> Boxes;
Dále bude potřeba odkaz na herní okno, ke kterému manažer náleží.
Jméno ponecháme tradiční Parent
:
public GameScreen Parent{ get; private set; }
V konstruktoru vytvoříme pole a přiřadíme herní okno:
public CollisionManager(GameScreen screen){ Parent = screen; Boxes = new List<BoundingBox>(); }
Ještě nám chybí metody pro přidávání a odebírání krabic. Není na nich nic moc neobvyklého:
public void AddBox(BoundingBox box){ if(!Boxes.Contains(box))Boxes.Add(box); } public void RemoveBox(BoundingBox box){ Boxes.Remove(box); }
Chybí už jen metoda pro řešení kolizí, ale tu si ponecháme na
později. Přidáme si další třídu CollidableGameScreen
nebo si
ji pojmenujte jakkoliv je libo. Toto bude herní okno s právě vytvořeným
kolizním manažerem. Dědíme od obecného herního okna a přidáme kolizní
manažer:
public CollisionManager CollisionManager{ get; set; }
V konstruktoru jej vytvoříme:
public CollidableGameScreen(string jmeno):base(jmeno){ CollisionManager = new CollisionManager(this); }
To je vše Opravdu!
Potřebujeme ještě jednu třídu pro model. Tu si přidáme do složky s
komponentami. Pojmenujeme si ji CollidableModel3D. Učiníme ji opět veřejnou,
upravíme jmenný prostor, ale na to jste snad již zvyklí a to že budeme
dědit od třídy Model3D je snad také jasné. Budeme potřebovat celkem dvě
proměnné pro kolizní krabice. Jednu pro základní netransformovanou,
extrahovanou z modelu a druhou už transformovanou a připravenou pro
použití.
protected BoundingBox ZakladniBox; private BoundingBox fTransformedBox;
Veřejně přístupná bude jen transformovaná krabice. Přidáme tedy getter a setter pro tuto proměnnou:
public BoundingBox TransformedBox{ get{ return fTransformedBox; } private set{ if (fTransformedBox != value){ if (Parent!=null && Parent is CollidableGameScreen){ CollidableGameScreen okno = Parent as CollidableGameScreen; okno.CollisionManager.RemoveBox(fTransformedBox); okno.CollisionManager.AddBox(value); } fTransformedBox = value; } } }
Getter je celkem tradiční. Ale v setteru se dějí nějaké čáry.
Projděme si je. Pokud se pokusíme krabici modifikovat, je potřeba starou
odebrat a novou naopak přidat do kolizního manažeru. To zajistí, že krabice
budou vždy aktuální. Přepíšeme metodu Load
, kde vytvoříme
základní krabici a poprvé ji transformujeme.
protected override void Load(){ base.Load(); ZakladniBox=Utility.VypoctiBoundingBox(Model, transformace); TransformBox(); }
Metoda TransformBox
není žádná jiná než ta, kterou jsme si
připravili posledně, ale pro úplnost ji znovu uvádím, takže jen
telegraficky:
protected void TransformBox(){ Matrix transform = Matrix.CreateScale(Meritko) * Matrix.CreateTranslation(Pozice); BoundingBox transformed = ZakladniBox; transformed.Min = Vector3.Transform(transformed.Min, transform); transformed.Max = Vector3.Transform(transformed.Max, transform); Vector3[] body = new Vector3[8]; transformed.GetCorners(body); for (int i = 0; i < body.Length; i++){ body[i] = Vector3.Transform(body[i], Rotace); } transformed = BoundingBox.CreateFromPoints(body); fTransformedBox.Min = transformed.Min; fTransformedBox.Max = transformed.Max; }
Toto metodu musíme ještě zavolat pokaždé v metodě Update
.
Není to moc ideální stav, ale kvůli animacím objektů je nutné vše
přepočítat.
public override void Update(){ base.Update(); TransformBox(); }
Další nutností jsou konstruktory, pouze jen volající jejich předky:
public CollidableModel3D(Vector3 pozice, string model): this(pozice,Matrix.Identity,model){ } public CollidableModel3D(Vector3 pozice, Matrix rotace, string model): this(pozice,rotace,Vector3.One,model){ } public CollidableModel3D(Vector3 pozice, Matrix rotace, Vector3 meritko, string model):base(pozice,rotace,meritko,model){ }
Ještě nám zbývá zaregistrovat krabici ihned, jakmile je přidáme do
herního okna. Zdálo by se, že stačí krabici přidat v metodě
Load
, ale není to pravda. Metoda Load
se volá pouze
poprvé. Je potřeba dodat speciální metody, které se zavolají pokaždé.
Otevřeme si tedy třídu se základní komponentou a přidáme tam dvě
virtuální metody.
public virtual void OnAdded(){ } public virtual void OnRemoved(GameScreen okno){ }
Metody pak v herním okně při přidání komponenty zavoláme. Hned potom co komponentu nahrajeme:
c.LoadComponent(); // stary radek
c.OnAdded();
To samé při odebírání komponenty z herního okna.
c.Parent = null; // stary radek c.OnRemoved(this);
Ve třídě s naším speciálním modelem přepíšeme obě metody a v nich přidáme krabice do kolizního manažeru:
public override void OnAdded(){ if (Parent is CollidableGameScreen){ CollidableGameScreen okno = Parent as CollidableGameScreen; okno.CollisionManager.AddBox(TransformedBox); } } public override void OnRemoved(GameScreen okno){ if (okno is CollidableGameScreen){ CollidableGameScreen okn = Parent as CollidableGameScreen; okn.CollisionManager.RemoveBox(TransformedBox); } }
Vše je nyní připraveno. I když se to tak asi nezdá, chybí pouze samotný řešitel kolizí. Dnes jsme napsali systém pro registraci krabic na jednom místě. Touto jednou větou se dají vyjádřit tři předcházející stránky. Hrozné pomyšlení. Občas si říkám že na tuto práci by se hodil nějaký stroj, který by se připojil na hlavu a jen by stačilo vybavit si co je potřeba udělat. Třeba někdy v budoucnosti.
V příštím díle, 3D bludiště v XNA - Kolize podruhé, si vytvoříme metodu pro řešení kolizí a také si je vyzkoušíme. Na to jak kolize kolidují, se můžete podívat na videu výše. Čekám na komentáře pod článkem, dotazy, náměty, stížnosti, nápady. Však to znáte.
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 153x (1.77 MB)
Aplikace je včetně zdrojových kódů v jazyce C# XNA