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

Lekce 6 - XNA tvorba ve 3D - Engine podruhé a ne naposledy

Vítejte posedmé. Minule, XNA tvorba ve 3D - Engine poprvé, jsme si připravili třídu enginu.

Dnes se podíváme na třídu GameScreen která nám reprezentuje herní okna. Již minule jsme si v ní připravili dvě virtuální metody Update a Draw. Předně si vytvoříme konstruktor. Jeho jediným parametrem je jméno okna. Jméno není až tak systémově důležité, ale pro editor se nám bude velmi hodit. Zároveň by bylo dobré, kdyby jméno nebylo null a nebo prázdný řetězec.

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;
}

Dále bude potřeba zavést pole pro komponenty. Opět použijeme generický List:

private List<Component> fComponents;

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

V konstruktoru tradičně inicializujeme:

fComponents = new List<Component>();

Přidáme metodu AddComponent, skrze kterou budeme komponenty přidávat. Na místě bude asi přidávanou komponentu otestovat, zda-li ji již nemáme v přidánu.

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

Obdobně snadno prozatím vytvoříme metodu RemoveComponent:

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

Nyní máme vše připraveno a můžeme se blížeji podívat na metodu Update. Ta, jak bylo již minule vysvětleno, se stará o updatování všech komponent. Nemůžeme je ale jen tak položku po položce projet a zavolat jejich vnitřní metodu Update. Co když by se komponenta rozhodla smazat se z okna? Nebo by naopak vytvořila svou kamarádku, kterou nutně potřebuje. V takovém případě by nám aplikace spadla. Obejít to lze velmi snadno. Vytvoříme si další List a do něj současné komponenty překopírujeme a vše budeme volat v něm. Prvně si ale vytvoříme virtuální metody Update a Draw (ať ji máme připravenou pro další psaní) ve třídě Component:

public virtual void Update(){

}

public virtual void Draw(){

}

V metodě Update tedy prvně vyčistíme pomocné pole. Vše do něj nakopírujeme a posléze jej projdeme:

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

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

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

Metoda Draw je na tom obdobně. Zde ale postačí pouze projít komponenty a zavolat jejich Draw metodu. Při vykreslení by se nemělo stát, že by se nám komponenta smazala a nebo si udělala kamarádku:

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

Bude se nám také hodit metoda LoadGameScreen, kde můžeme provést nahrání důležitých složek okna. Zajistíme také, že nebude tato metoda zavolána vícekrát než jednou. Metoda není virtuální (tedy nelze potomky přepsat) je to tak schválně. Pro účely potomků je tu virtuální chráněná metoda Load, která se zde 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 se zdát, že to je z třídy GameScreen vše co potřebujeme. Není tomu tak. Ještě se bude hodit odkaz na engine, ve kterém je okno umístěno:

internal Engine engine;

public Engine Engine{
  get{
    return engine;
  }
}

Tento odkaz budeme nastavovat při přidání okna v metodě PushGameScreen. Do této metody ještě přidáme kontrolu zda-li není okno již přítomno a zároveň zkontrolujeme zda-li dané herní okno nepoužívá třeba jiná instance. Zavoláme také naši metodu LoadGameScreen. Výsledek bude tedy vypadat následovně:

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

V metodě PopGameScreen oknu engine odstraníme řádkem:

ret.engine = null;

Nyní to je konečně z třídy GameScreen vše podstatné. Přesuňme se do třídy Component. Každá komponenta by si také zasloužila vlastní jméno. Pro potřeby editoru jej vřele uvítáme. Jméno komponenty by ale mělo být unikátní v daném herním okně. Tento požadavek si ale ponecháme na jindy. Spokojíme se prozatím s jakýmkoliv jménem stejně jako u herního okna. Takže jen vytvoříme proměnnou Name a necháme ji zatím nenastavenu. Taktéž vytvoříme proměnnou Visible, která nám bude určovat, zda-li se má komponenta vykreslovat:

private string fName;

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

public bool Visible{
  get;
  set;
}

A v konstruktoru, který si také vytvoříme, nastavíme proměnnou Visible na true:

public Component(){
  Visible = true;
}

Stejně jako okno uchovává odkaz na engine, tak komponenta si bude uchovávat 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 se u nastavovací části. Pokud je nastavované okno stejné jako to které již máme, neuděláme pochopitelně nic. Jinak okno odebereme od starého a přidáme novému. Stejný trik bohužel nebylo možné provést i u herních oken. Engine nevlastní žádnou metodu pro přímé odebrání daného okna. Bude potřeba také prakticky stejný mechanizmus pro nahrávání obsahu, jaký jsme si vytvořili u herního okna, takže jen telegraficky:

bool loaded;

public bool Loaded{
  get{
    return loaded;
  }
}

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

  loaded = true;
  Load();
}

protected virtual void Load(){

}

Při přidání komponenty do herního okna ještě tyto metody zavoláme a nastavíme také Parent. Při odebrání okna nastavíme naopak null. Přidal jsem ještě pár testů před přidáním a odebrání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;
  }
}

Ještě zohledníme hodnotu v proměnné Visible v metodě Draw u herního okna:

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

Skvěle. Doufám, že jste se neztratili někde cestou. Pokud ano, nic se neděje. Jsou tu komentáře a případně zachrání přiložený kód pod článkem.

Příště, XNA tvorba ve 3D - Engine integrace do hry, si námi napsaný engine naroubujeme do naší hry. Přidáme si herní okno a zkusíme si napsat první komponentu.


 

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 319x (1.58 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
XNA tvorba ve 3D - Engine poprvé
Všechny články v sekci
Základy 3D grafiky a tvorba enginu
Přeskočit článek
(nedoporučujeme)
XNA tvorba ve 3D - Engine integrace do hry
Článek pro vás napsal vodacek
Avatar
Uživatelské hodnocení:
3 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