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í.

8. diel - 3D bludisko v XNA - Kolízia prvýkrát

Vitajte po osemnástej. V dnešnom diele sa pokúsime o kolízne manažér. Skoro určite sa to nezmestí do jedného dielu.

Už som sa o tom, ako to bude všetko fungovať, spomenul v jednom z predchádzajúcich článkov, ale napriek tomu radšej znovu všetko osvetlím. Každý model, s ktorým sa má kolízie odohrávať, si do kolízneho manažéru zaregistruje svojou krabici. Potom už len pri každom pohybe pohyblivého objektu skontrolujeme, či ak nám koliduje a ak áno, pohyb nepovolíme. To je všetko. Nič viac, nič menej. Pusťme sa teda do toho.

Okrem toho čo som naznačil vyššie bude potrebné vytvoriť si špeciálny herný okno s kolíznym manažérom. Vo všetkých situáciách ho nebude potrebné používať, treba pre menu a alebo pre iné hry. Preto potrebujeme novú triedu. Rovnako naložíme is modelom. Opäť vytvoríme vlastnú triedu a to všetko len pretože nie všetky modely potrebujú, aby sa s nimi kolidovalo.

Vytvoríme si zložku Collision, kam budeme triedy súvisiace s kolíziami skladovať. Pridáme si do nej triedu CollisionManager. To je práve tá trieda, ktorá sa nám stará o riešenie kolízií. Urobíme ju verejnú, upravíme jej menný priestor. Základom bude zoznam všetkých krabíc, s ktorými budeme kolidovať. Pridáme si ho:

protected List<BoundingBox> Boxes;

Ďalej bude potrebné odkaz na herný okno, ku ktorému manažér patrí. Meno ponecháme tradičné Parent:

public GameScreen Parent{
  get;
  private set;
}

V konstruktoru vytvoríme polia a priradíme herné okno:

public CollisionManager(GameScreen screen){
  Parent = screen;
  Boxes = new List<BoundingBox>();
}

Ešte nám chýba metódy pre pridávanie a odoberanie krabíc. Nie je na nich nič moc neobvyklé:

public void AddBox(BoundingBox box){
  if(!Boxes.Contains(box))Boxes.Add(box);
}

public void RemoveBox(BoundingBox box){
  Boxes.Remove(box);
}

Chýba už len metóda pre riešenie kolízií, ale tú si ponecháme na neskôr. Pridáme si ďalšiu triedu CollidableGameScreen alebo si ju pomenujte akokoľvek je libo. Toto bude herné okno s práve vytvoreným kolíznym manažérom. Dedíme od všeobecného herného okna a pridáme kolízne manažér:

public CollisionManager CollisionManager{
  get;
  set;
}

V konstruktoru ho vytvoríme:

public CollidableGameScreen(string jmeno):base(jmeno){
  CollisionManager = new CollisionManager(this);
}

To je všetko :-) Naozaj! Potrebujeme ešte jednu triedu pre model. Tú si pridáme do zložky s komponentmi. Pomenujeme si ju CollidableModel3D. Urobíme ju opäť verejnú, upravíme menný priestor, ale na to ste snáď už zvyknutí a to že budeme dediť od triedy Model3D je snáď tiež jasné. Budeme potrebovať celkom dve premenné pre kolízne krabice. Jednu pre základné netransformované, extrahované z modelu a druhú už transformovanú a pripravenú na použitie.

protected BoundingBox ZakladniBox;
private BoundingBox fTransformedBox;

Verejne prístupná bude len transformovaná krabice. Pridáme teda getter a setter pre túto premennú:

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 celkom tradičné. Ale v Setter sa dejú nejaké čiary. Prejdime si ich. Ak sa pokúsime krabici modifikovať, je potreba starú odobrať a novú naopak pridať do kolízneho manažérovi. To zaistí, že krabice budú vždy aktuálne. Prepíšeme metódu Load, kde vytvoríme základnej krabici a prvýkrát ju transformujeme.

protected override void Load(){
  base.Load();
  ZakladniBox=Utility.VypoctiBoundingBox(Model, transformace);
  TransformBox();
}

Metóda TransformBox nie je žiadna iná ako tá, ktorú sme si pripravili posledne, ale pre úplnosť ju znovu uvádzam, takže len 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 metódu musíme ešte zavolať zakaždým v metóde Update. Nie je to moc ideálny stav, ale kvôli animáciám objektov je nutné všetko prepočítať.

public override void Update(){
  base.Update();
  TransformBox();
}

Ďalšie nutnosťou sú konštruktory, iba len volajúci ich predkov:

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){

}

Ešte nám zostáva zaregistrovať krabici ihneď, akonáhle je pridáme do herného okna. Zdalo by sa, že stačí krabici pridať v metóde Load, ale nie je to pravda. Metóda Load sa volá len prvýkrát. Je potrebné dodať špeciálne metódy, ktoré sa zavolajú zakaždým. Otvoríme si teda triedu so základnou komponentom a pridáme tam dve virtuálne metódy.

public virtual void OnAdded(){

}

public virtual void OnRemoved(GameScreen okno){

}

Metódy potom v hernom okne pri pridaní komponenty zavoláme. Hneď potom čo komponent nahráme:

c.LoadComponent(); // stary radek
c.OnAdded();

To isté pri odoberaní komponenty z herného okna.

c.Parent = null; // stary radek
c.OnRemoved(this);

V triede s naším špeciálnym modelom prepíšeme obe metódy a v nich pridáme krabice do kolízneho manažéra:

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šetko je teraz pripravené. Aj keď sa to tak asi nezdá, chýba len samotný riešiteľ kolízií. Dnes sme napísali systém pre registráciu krabíc na jednom mieste. Touto jednou vetou sa dajú vyjadriť tri predchádzajúce stránky. Hrozné pomyslenie. Občas si hovorím že na túto prácu by sa hodil nejaký stroj, ktorý by sa pripojil na hlavu a len by stačilo vybaviť si čo je potrebné urobiť. Trebárs niekedy v budúcnosti.

V budúcom diele si vytvoríme metódu pre riešenie kolízií a tiež si ich vyskúšame. Na to ako kolízie kolidujú sa môžete pozrieť na videu dole. Čakám na komentáre pod článkom, otázky, námety, sťažnosti, nápady. Však to poznáte.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 153x (1.77 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
3D bludisko v XNA - Škatule a guľa
Všetky články v sekcii
3D bludisko v XNA
Preskočiť článok
(neodporúčame)
3D bludisko v XNA - Kolízia druhýkrát
Článok pre vás napísal vodacek
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
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