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#