IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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í.

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

 

Předchozí článek
3D bludiště v XNA - Krabice a koule
Všechny články v sekci
3D bludiště v XNA
Přeskočit článek
(nedoporučujeme)
3D bludiště v XNA - Kolize podruhé
Článek pro vás napsal vodacek
Avatar
Uživatelské hodnocení:
1 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