5. diel - Rozdelenie MonoGame hry do komponentov
V minulej lekcii, Zvuky, hudba, klávesnica a myš v MonoGame , sme si ukázali prehrávanie zvukov a prácu s klávesnicou a myšou. Dnes si povieme niečo o architektúre hry, ukážeme si, ako ju rozdeliť do niekoľkých samostatných komponentov. Po dnešnom dieli budeme mať už všetko pripravené na to, aby sme mohli začať programovať samotnú logiku hry Tetris.
Asi nám je jasné, že hra bude obsahovať niekoľko obrazoviek. Tými bude menu, samotný level, potom napríklad obrazovka s Rolujúcim textom o autoroch hry, on-line skóre tabuľka a tak podobne. Každá táto obrazovka bude potom zložená z ďalších súčastí, napr. Padajúce kocka, hracia plocha alebo tie plaviace mraky, čo sme si na začiatku seriálu naprogramovali.
Keby sme celú hru nabušili do jednej triedy, bolo by to veľmi neprehľadné. K dekompozícii hry do viacerých tried sa nám ponúka niekoľko spôsobov, všetky si počas seriálu ukážeme, aby sme videli ich výhody i nevýhody. Počas kurzu si vytvoríme jednoduchú komponentová-objektovú architektúru.
Herné komponenty
Jedným zo spôsobu, ako hru dekomponovať na niekoľko časti sú tzv. Herné komponenty. Tie pre nás sú nachystané priamo ako súčasti frameworku MonoGame. Väčšina tutoriálov o nich úplne mlčia, aj keď sa jedná o pomerne dobrý spôsob, ako si uľahčiť prácu.
Komponent je čo najviac samostatná časť hry s čo najmenším počtom
závislostí. Svojou vnútornou štruktúrou pripomína samotnú hru, teda
triedu Game
. Má metódy ako LoadContent()
,
Initialize()
, Update()
a podobne. Môžeme si ju
predstaviť ako hru v hre. Komponenta by mala byť čo najviac nezávislá,
tento predpoklad nám dokonale spĺňajú naše plaviace mraky. Presunieme ich
teda do komponenty.
Z hry vymažeme všetko nepotrebné z minulých pokusov, teda hlavne myš a
nejaké zbytočné vykresľovanie. Konkrétne odstránime premenné
mys
, obdelnikRobota
a všetko s nimi spojené, ďalej
vykresľovanie písma a reakciu na Enter. Naše 2 metódy pre prácu
s klávesnicou a hudbou ponecháme, rovnako tak idúci mraky a ďalšie 2
pozadia. Celý obsah hry ponecháme (teraz mám na mysli
Content
).
K projektu Robotris
pridáme nový komponent, ktorú pomenujeme
KomponentaMraky
(pravým na projekt Robotris
v
Solution Exploreru -> Add -> New Item -> Class):
Visual Studio nám vygenerovalo triedu, ktorú podědíme od
Microsoft.Xna.Framework.GameComponent
. V konstruktoru sa odovzdáva
inštancie našej hry, presnejšie jej predka Game
.
Komponent bude vyzerať takto:
public class KomponentaMraky : Microsoft.Xna.Framework.GameComponent { public KomponentaMraky(Game game) : base(game) { } }
Metódy LoadContent()
, Initialize()
a
Update()
sú poděděné a môžeme ich prepísať.
Template na vytvorenie komponenty v základnom balíčku MonoGame bohužiaľ nie je a teda musíme komponent vytvoriť takto ručne.
Vykreslovatelná komponenta
GameComponent
nedisponuje vykresľovaním. Aj keď sa nám v hre
aj takéto komponenty môžu hodiť, vykresľovanie u mrakov potrebovať budeme.
Našťastie MonoGame ponúka ešte jeden typ komponenty a to
DrawableGameComponent
, na tohto predka zmeníme súčasnej
GameComponent
v hlavičke triedy:
public class KomponentaMraky : Microsoft.Xna.Framework.DrawableGameComponent
Budeme potrebovať komponente nejakým spôsobom odovzdať spoločné dáta
hry, to urobíme modifikáciou konstruktoru tak, aby jeho parametrom bola
konkrétna trieda našej hry, zameníme teda Game
na
Hra
. Ďalej vytvoríme v komponente privátnej premennú
hra
typu Hra
a inštanciu odovzdanú konštruktor do
nej uložíme. Modifikovaný konštruktor komponenty vyzerá takto:
public KomponentaMraky(Hra hra) : base(hra) { this.hra = hra; }
Teraz komponente pridáme metódy LoadContent()
,
Initialize()
, Update()
a Draw()
. Stačí
začať písať public override
... a VS nám už ponúka zoznam
metód, ktoré môžeme triede dodať púhym odenterováním:
Kostra komponenty je pripravená. Pretože sa určite bude hodiť aj do budúcna (k tvorbe ďalších komponentov), vložím sem jej zdrojový kód:
public class KomponentaMraky : Microsoft.Xna.Framework.DrawableGameComponent { private Hra hra; public KomponentaMraky(Hra hra) : base(hra) { this.hra = hra; } public override void Initialize() { base.Initialize(); } protected override void LoadContent() { base.LoadContent(); } public override void Update(GameTime gameTime) { base.Update(gameTime); } public override void Draw(GameTime gameTime) { base.Draw(gameTime); } }
Až budete s týmto kódom tvoriť ďalší komponent (čo bude za moment), nezabudnite sa menom triedy zmeniť aj meno konstruktoru.
Komentáre som opäť vynechal, sú na vás. Teraz do komponenty presunieme z
hry všetko, čo sa týka mrakov. Malo by to byť jednoduché, len musíme
dodať, že k Content
v LoadContent()
pristúpime ako
k hra.Content
, podobne tomu urobíme v Draw()
s
hra.spriteBatch
. Ten sme nemali verejný, preto to v hre
napravíme:
public LepsiSpriteBatch spriteBatch;
V Draw()
nezabudneme pripísať Begin()
a
End()
, musí byť tak v Draw()
v hre, tak v
Draw()
v komponente!
Dnes to máme také veľa zdrojákové, mali by ste dospieť k niečomu podobnému:
public class KomponentaMraky : Microsoft.Xna.Framework.DrawableGameComponent { private int zmena; private int smer; private Texture2D mraky; private Vector2 pozice; private Hra hra; public KomponentaMraky(Hra hra) : base(hra) { this.hra = hra; } public override void Initialize() { pozice = new Vector2(0, 0); zmena = 0; smer = 1; base.Initialize(); } protected override void LoadContent() { mraky = hra.Content.Load<Texture2D>(@"Sprity\spr_mraky"); base.LoadContent(); } public override void Update(GameTime gameTime) { // posun mraků doleva pozice.X--; // návrat na startovní pozici po vyjetí z obrazovky if (pozice.X < -(mraky.Width)) pozice.X = 0; // změna barvy mraků dle směru zmena += smer; if (zmena >= 96) smer = -1; if (zmena <= 0) smer = 1; base.Update(gameTime); } public override void Draw(GameTime gameTime) { hra.spriteBatch.Begin(); Color barva = new Color(128 + zmena, 255 - zmena, 128 + zmena); for (int i = 0; i < 6; i++) hra.spriteBatch.Draw(mraky, new Vector2(pozice.X + i * mraky.Width, 0), barva * 0.8f); hra.spriteBatch.End(); base.Draw(gameTime); } }
Sprevádzkovanie necháme na koniec. Hre pridáme ďalšie vykreslovatelnou
komponent, pomenujeme ju KomponentaLevel
. V nej sa bude odohrávať
samotný gameplay. Ten my zatiaľ nemáme, ale vykreslíme v nej aspoň pozadí
levelu, to je to s robotom. Určite to zvládnete, postup je obdobný, ako pri
mrakov, presunúť vykreslenie pozadia z hry do tohto komponentu.
Výsledok bude vyzerať asi takto:
public class KomponentaLevel : Microsoft.Xna.Framework.DrawableGameComponent { private Hra hra; private Texture2D pozadi; public KomponentaLevel(Hra hra) : base(hra) { this.hra = hra; } public override void Initialize() { base.Initialize(); } protected override void LoadContent() { pozadi = hra.Content.Load<Texture2D>(@"Sprity\pozadi_level"); base.LoadContent(); } public override void Update(GameTime gameTime) { base.Update(gameTime); } public override void Draw(GameTime gameTime) { hra.spriteBatch.Begin(); hra.spriteBatch.Draw(pozadi, new Vector2(0, 0), Color.White); hra.spriteBatch.End(); base.Draw(gameTime); } }
Pripojenie komponentov k hre
V hre nám zostalo v Draw()
len vykreslenie pozadia kociek a v
Update()
obsluha stavu klávesnice. Presunieme sa do
Initialize()
a na začiatku vytvoríme naše komponenty, ktorým
odovzdáme inštanciu hry v konstruktoru:
KomponentaMraky mraky = new KomponentaMraky(this); KomponentaLevel level = new KomponentaLevel(this);
Teraz príde mágie! Hra má na sebe vlastnosť Components
. To
je kolekcia (presnejšie List
) komponentov, z ktorých sa skladá.
Do listu pridáme komponenty v tom poradí, v akom chceme, aby sa obnovovali a
vykresľovali:
Components.Add(mraky); Components.Add(level);
Spustíme a vidíme, že hra funguje rovnako dobre, ako predtým. Komponenty
sa totiž obsluhujú úplne samy a my sa o ne nemusíme starať. Kód hry je
prehľadný a veľmi krátky. Komponenty obsahujú len to, čo sa ich týka.
Všetko je možné vďaka volanie base.Initialize()
,
base.Update()
atď. V metódach hry, čo má v predkovi za
následok volanie metód všetkých aktívnych komponentov hry.
Ešte malé, kozmetické úpravy na záver. Súbor Game1.cs
premenujeme na Hra.cs
. Pretože komponentov bude viac, vytvoríme
na ne zložku (Pravým na Robotris -> Add -> New Folder), ktorú
pomenujeme Komponenty/
. Naše 2 komponenty do nej jednoducho
presunieme myšou.
Dospeli sme do fázy, kedy nám nič nebráni začať tetrisové : P Hneď nabudúce, v lekcii Hra tetris v MonoGame: Kostka , sa na to pozrieme.
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é 484x (11.85 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#