Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

6. diel - XNA tvorba v 3D - Engine druhýkrát a nie naposledy

Vitajte siedmykrát. Minule sme si pripravili triedu enginu, dnes sa pozrieme na triedu GameScreen ktorá nám reprezentuje herné okna. Už minule sme si v nej pripravili dve virtuálne metódy Update a Draw. Predovšetkým si vytvoríme konštruktor. Jeho jediným parametrom je meno okna. Meno nie je až tak systémovo dôležité, ale pre editor sa nám bude veľmi hodiť. Zároveň by bolo dobré, keby meno nebolo null a alebo prázdny reťazec.

private string fName;//vodackovina

public string Name{
  get{
    return fName;
  }
  set {
    if (!String.IsNullOrEmpty(value)){
      fName = value;
    }
    else{
      if (fName == null) fName = "Herni okno";
    }
  }
}

public GameScreen(string jmeno){
  Name = jmeno;
}

Ďalej bude potrebné zaviesť poľa pre komponenty. Opäť použijeme generický List:

private List<Component> fComponents;

public List<Component> Components{
  get{
    return fComponents;
  }
}

V konstruktoru tradične inicializujeme:

fComponents = new List<Component>();

Pridáme metódu AddComponent, cez ktorú budeme komponenty pridávať. Na mieste bude asi pridávanú komponent otestovať, či ak ju už nemáme v zaznačené.

public void AddComponent(Component c){
  if (!Components.Contains(c)){
    Components.Add(c);
  }
}

Podobne ľahko zatiaľ vytvoríme metódu RemoveComponent:

public void RemoveComponent(Component c){
  fComponents.Remove(c);
}

Teraz máme všetko pripravené a môžeme sa bližšie pozrieť na metódu Update. Tá, ako bolo už minule vysvetlené, sa stará o updatované všetkých komponentov. Nemôžeme ich ale len tak položku po položke prejsť a zavolať ich vnútorné metódu Update. Čo keď by sa komponent rozhodla zmazať sa z okna? Alebo by naopak vytvorila svoju kamarátku, ktorú nutne potrebuje. V takom prípade by nám aplikácia spadla. Obísť to možno veľmi ľahko. Vytvoríme si ďalší List a do neho súčasné komponenty prekopíruje a všetko budeme volať v ňom. Najprv si ale vytvoríme virtuálne metódy Update a Draw (nech ju máme pripravenú pre ďalšiu písanie) v triede Component:

public virtual void Update(){

}

public virtual void Draw(){

}

V metóde Update teda prvýkrát vyčistíme pomocné pole. Všetko do neho nakopírujeme a neskôr ho prejdeme:

public virtual void Update(){
  updated.Clear();

  foreach (Component c in fComponents){
    updated.Add(c);
  }

  foreach (Component c in updated){
    c.Update();
  }
}

Metóda Draw je na tom obdobne. Tu ale postačí iba prejsť komponenty a zavolať ich Draw metódu. Pri vykreslenie by sa nemalo stať, že by sa nám komponent zmazala a alebo si urobila kamarátku:

public virtual void Draw(){
  foreach (Component c in fComponents){
    c.Draw();
   }
}

Bude sa nám tiež hodiť metóda LoadGameScreen, kde môžeme vykonať nahranie dôležitých zložiek okna. Zaistíme tiež, že nebude táto metóda zavolaná viackrát ako raz. Metóda nie je virtuálne (teda nedá potomkami prepísať) je to tak schválne. Na účely potomkov je tu virtuálny chránená metóda Load, ktorá sa tu volá:

bool loaded;

public void LoadGameScreen(){
  if (loaded) return;
  loaded = true;
  // sem muzeme pozdeji pridat ncitani treba systemu svetel a dalsich veci ktere jsou pro
  // vsechna okna spolecne a musi byt vzdy nahrany

  Load();
}

protected virtual void Load(){
  // kdezto sem dame veci specificke pro kazde dalsi zdedene okno,
  //pridavani komponent a jine
}

public bool Loaded{
  get{
    return loaded;
  }
}

Mohlo by sa zdať, že to je z triedy GameScreen všetko čo potrebujeme. Nie je tomu tak. Ešte sa bude hodiť odkaz na engine, v ktorom je okno umiestnené:

internal Engine engine;

public Engine Engine{
  get{
    return engine;
  }
}

Tento odkaz budeme nastavovať pri pridanie okna v metóde PushGameScreen. Do tejto metódy ešte pridáme kontrolu či ak nie je okno už prítomné a zároveň skontrolujeme či ak dané herné okno nepoužíva treba iná inštancia. Zavoláme tiež našu metódu LoadGameScreen. Výsledok bude teda vyzerať nasledovne:

public void PushGameScreen(GameScreen okno){
  if (okno.Engine == null && !Screens.Contains(okno)){
    Screens.Add(okno);
    okno.engine = this;
    okno.LoadGameScreen();
  }
}

V metóde PopGameScreen oknu engine odstránime riadkom:

ret.engine = null;

Teraz to je konečne z triedy GameScreen všetko podstatné. Presuňme sa do triedy Component. Každý komponent by si tiež zaslúžila vlastné meno. Pre potreby editora ho vrelo uvítame. Meno komponentu by ale malo byť unikátne v danom hernom okne. Táto požiadavka si však ponecháme na inokedy. Uspokojíme sa zatiaľ s akýmkoľvek menom rovnako ako u herného okna. Takže len vytvoríme premennú Name a necháme ju zatiaľ nenastavený. Taktiež vytvoríme premennú Visible, ktorá nám bude určovať, či ak sa má komponent vykresľovať:

private string fName;

public string Name{
  get{
    return fName;
  }
  set{
    fName = value;
  }
}

public bool Visible{
  get;
  set;
}

A v konstruktoru, ktorý si tiež vytvoríme, nastavíme premennú Visible na true:

public Component(){
  Visible = true;
}

Rovnako ako okno uchováva odkaz na engine, tak komponenta si bude uchovávať odkaz na svoje herné okno.

private GameScreen fParent;

public GameScreen Parent{
  get{
    return fParent;
  }

  set{
    if (fParent == value) return;

    if (fParent != null) fParent.RemoveComponent(this);
      fParent = value;
    if (value != null) fParent.AddComponent(this);
  }
}

Zastavme sa u nastavovacie časti. Ak je nastavované okno rovnaké ako to ktoré už máme, neurobíme pochopiteľne nič. Inak okno odoberieme od starého a pridáme novému. Rovnaký trik žiaľ nebolo možné vykonať aj u herných okien. Engine nevlastní žiadnu metódu pre priame odobratie daného okna. Bude potrebné tiež prakticky rovnaký mechanizmus pre nahrávanie obsahu, aký sme si vytvorili u herného okná, takže len telegraficky:

bool loaded;

public bool Loaded{
  get{
    return loaded;
  }
}

public void LoadComponent(){
  if (loaded) return;

  loaded = true;
  Load();
}

protected virtual void Load(){

}

Pri pridaní komponenty do herného okna ešte tieto metódy zavoláme a nastavíme tiež Parent. Pri odobratí okná nastavíme naopak null. Pridal som ešte pár testov pred pridaním a odobratím:

public void AddComponent(Component c){
  if (c!=null && !Components.Contains(c)){
    Components.Add(c);
    c.Parent = this;
    c.LoadComponent();
  }
}

public void RemoveComponent(Component c){
  if (c != null && Components.Contains(c)){
    fComponents.Remove(c);
    c.Parent = null;
  }
}

Ešte zohľadníme hodnotu v premennej Visible v metóde Draw u herného okna:

if(c.Visible)c.Draw();

Skvele. Dúfam, že ste sa nestratili niekde cestou. Ak áno, nič sa nedeje. Sú tu komentáre a prípadne zachráni priložený kód pod článkom. Nabudúce si nami napísaný engine naroubujeme do našej hry. Pridáme si herné okno a skúsime si napísať prvý komponent.


 

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é 319x (1.58 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
XNA tvorba v 3D - Engine prvýkrát
Všetky články v sekcii
Základy 3D grafiky a tvorba enginu
Preskočiť článok
(neodporúčame)
XNA tvorba v 3D - Engine integrácia do hry
Č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