6. díl - XNA tvorba ve 3D - Engine podruhé a ne naposledy

C# .NET XNA game studio 3D grafika XNA tvorba ve 3D - Engine podruhé a ne naposledy

Vítejte posedmé. Minule 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ě si námi napsaný engine naroubujeme do naší hry. Přidáme si herní okno a zkusíme si napsat první komponentu.


 

Stáhnout

Staženo 302x (1.58 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Článek pro vás napsal vodacek
Avatar
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.

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 


Miniatura
Předchozí článek
XNA tvorba ve 3D - Engine poprvé
Miniatura
Všechny články v sekci
Základy 3D grafiky a tvorba enginu

 

 

Komentáře
Zobrazit starší komentáře (9)

Avatar
vodacek
Redaktor
Avatar
vodacek:

v javě ano v C# ne

 
Odpovědět 26.3.2013 15:05
Avatar
Kit
Redaktor
Avatar
Odpovídá na vodacek
Kit:

Bohužel to pravidelně vídám v C# i v Javě.

Odpovědět 26.3.2013 15:10
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Deastery
Člen
Avatar
Deastery:

Zdravím, mal by som jednu otázku : Ak pridáme nejaký model cez AddComponent tak ako sa to robí cez RemoveComponent ? som to skúšal vybrať odtiaľ, aby sa to prestalo vykreslovať úplne a ono to tam stále je.

 
Odpovědět 4.2.2014 0:30
Avatar
Milan Lhoták:

ahoj, nevím jestli se to neřeší někde v dalších dílech, ale nezdá se mi drobátko nastavování parent okna u komponent. Co když budu mít komponentu společnou pro více oken, která budou moci být otevřená přes sebe a bude se měnit jen nějaký pohled na ní, např. mapa vesmíru a na ní slunce, mapa soustavy slunce a zase objekt slunce a nakonec pohled z planety a opět slunce? podle mě by stačil jeden objekt a každé okno by si z něj bralo jen specifické údaje. Pokud si tedy nepletu pojmy a je větší rozdíl mezi komponentou a objektem hry.

 
Odpovědět 4. března 0:53
Avatar
vodacek
Redaktor
Avatar
Odpovídá na Milan Lhoták
vodacek:

komponenta společná pro více oken? proč?

 
Odpovědět 7. března 8:07
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Kit
Marian Benčat:

Ach java a jeji silne archaicke setNeco(a) getNeco()... To je peklo.

 
Odpovědět  -1 7. března 10:02
Avatar
abushrek
Člen
Avatar
abushrek:

Není lepší místo tohoto udělat tohle?

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

Tohle bude bych řekl asi lepší :)

private string Name;
public GameScreen(string Name = "Herní okno"){
  this.Name = Name;
}

Já bych řekl že je to správnější než se "prasit" s těma podmínkama.

Editováno 29. září 21:08
 
Odpovědět 29. září 21:06
Avatar
vodacek
Redaktor
Avatar
Odpovídá na abushrek
vodacek:
HerniOkno okno = new HerniOkno(null);

// a nebo

okno.Name=null;

a teď s tím provedeš co?

 
Odpovědět 30. září 9:09
Avatar
Odpovídá na vodacek
Luboš Běhounek (Satik):

Ja bych v tomhle pripade vyhodil ArgumentException :D

Odpovědět 30. září 9:52
:)
Avatar
abushrek
Člen
Avatar
Odpovídá na vodacek
abushrek:
public string Name { get; private set; }

        public GameScreen(string Name = "Herní okno")
        {
            this.Name = Name;
        }

Ano uznávám mělo to být takhle... Jinak privátní set bych dal na co bychom potřebovali měnit jméno herního okna za běhu o.O

 
Odpovědět 1. října 6:36
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 19. Zobrazit vše