Lekce 1 - Struktura MonoGame hry
V úvodním dílu o MonoGame jsme si udělali úvod do MonoGame a framework jsme si nainstalovali. Dnes se podíváme na strukturu MonoGame hry a vyzkoušíme si úplné základy práce s frameworkem.
Ještě jednou upozorňuji, že tento kurz se věnuje výuce frameworku MonoGame, nikoli výuce jazyka C#. Pokud C# neovládáte, přečtěte si nejprve alespoň první 2 sekce C# tutoriálů a potom práci se soubory.
MonoGame jsme si již nainstalovali minule, spusťme si tedy Visual Studio a
založme nový projekt MonoGame (MonoGame Cross Platform Desktop Project),
který pojmenujeme Robotris
:
Pokud vám název připomněl Tetris, máte pravdu, celý kurz se bude točit okolo této hry. Postupně si vytvoříme hru, herní menu, on-line skóre tabulku a obrazovku s autory hry. Naučíte se základy práce s frameworkem a budete poté schopni vytvořit jakoukoli vlastní hru
Struktura projektu
MonoGame hra má svou specifickou strukturu. V novém solution nalezneme
složky x64/
a x86/
, které spolu s se soubory
*.dylib
zajišťují chod Cross-Platform, pro náš tutoriál je
důležitý soubor Game1.cs
, který je samotná MonoGame hra. A
složka Content/
je tzv. obsah. Tak MonoGame
(Původně Microsoft v XNA) nazval obrázky, zvuky a hudbu. Právě do této
složky přes MonoGame Pipeline Tool je budeme přidávat.
Zaměřme se nejprve na třídu Game1.cs
, kterou nám Visual
Studio vygenerovalo a popišme si její kód, který vypadá nějak takto:
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; namespace Robotris { /// <summary> /// This is the main type for your game. /// </summary> public class Game1 : Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } /// <summary> /// Allows the game to perform any initialization it needs to before starting to run. /// This is where it can query for any required services and load any non-graphic /// related content. Calling base.Initialize will enumerate through any components /// and initialize them as well. /// </summary> protected override void Initialize() { // TODO: Add your initialization logic here base.Initialize(); } /// <summary> /// LoadContent will be called once per game and is the place to load /// all of your content. /// </summary> protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here } /// <summary> /// UnloadContent will be called once per game and is the place to unload /// game-specific content. /// </summary> protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } /// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit(); // TODO: Add your update logic here base.Update(gameTime); } /// <summary> /// This is called when the game should draw itself. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here base.Draw(gameTime); } } }
Jmenné prostory
Nejprve máme deklaraci několika jmenných prostorů. Zde si můžeme
všimnout, že žádný MonoGame namespace vlastně není a že vše je
z jmenného prostoru Microsoft.Xna.Framework
. To díky postavení
MonoGame na XNA frameworku a vývojáři se rozhodli zachovat původní jmenný
prostor. Máme vysvětleno a teď přejděme ke třídě Game1
.
Třída Game
Třídu přejmenujeme na Hra
a to tak, že přemístíme kurzor
na Game1
a stiskneme F2, následující dialog též
potvrdíme. Vidíme, že třída dědí z
Microsoft.Xna.Framework.Game
.
Máme založené 2 proměnné a sice graphics
a
spriteBatch
.
graphics
je typuGraphicsDeviceManager
a poskytuje metody např. pro změnu velikosti herního okna, přepnutí fullscreen (celoobrazovkového režimu) a podobně.spriteBatch
je instance třídySpriteBatch
, která poskytuje funkcionalitu pro práci se sprity. Máme zde nový termín - Sprite. Sprite (sprajt, sprit) je vlastně obrázek. Ten je klíčovým prvkem 2D her, přes sprity se řeší v podstatě vše od pozadí, přes postavy ve hře až po písmo, kde je každé písmeno jeden sprite. Sprity mohou být i animované.
Konstruktor a metoda Initialize()
Přejděme ke konstruktoru. Zde se inicializuje proměnná
graphics
a také se nastaví kořenová složka pro
Content
. Již víme, že je to složka, ve které budou naše
herní data.
Kromě konstruktoru zde máme i metodu Initialize()
. To může
být matoucí a také, že je
MonoGame vlastně nijak neříká, kdy použít k inicializaci konstruktor a kdy
metodu Initialize()
.
Rozdíl je v tom, že konstruktor se volá hned při vytvoření hry (nebo herní komponenty, o těch až později) a měl by nastavit hru nebo komponentu tak, aby byla funkční. To je u hry vždy a proto sem nic již psát nebudeme. Další význam konstruktoru je k předání závislostí, to poznáme u komponent, ze kterých se hra potom může skládat.
Metoda Initialize()
potom slouží k načtení dat, které
nejsou obsah (Content
, tedy nejsou sprity, zvuky nebo hudba), mohou
to být např. nějaké soubory map. Také zde provedeme veškerou inicializace
hry, vytvoření potřebných objektů, nastavení proměnných na výchozí
hodnoty a podobně.
V Initialize()
máme vložený řádek kódu:
base.Initialize();
Ten se stará o spuštění metody Initialize()
na všech
komponentách hry. O komponentách ale až později. Podobný řádek nalezneme
i v dalších metodách třídy.
LoadContent()
Dále máme ve třídě metodu LoadContent()
, do té patří
načítání obsahu, tedy spritů, zvuků a hudby. Vidíme, že se zde
vytváří i spriteBatch
. Na konec metody můžeme psát logiku,
kterou potřebujeme spustit až po načtení obsahu. Např. nemůžeme v
Initialize()
zjistit výšku fontu, který ještě není načtený,
proto by kód patřil do LoadContent()
.
UnloadContent()
se volá po skončení hry, protože my budeme
vždy používat Content
(což je vlastnost hry, kde je instance
ContentManager
), která si uvolnění zdrojů řeší sama, není
pro nás metoda důležitá.
Následují 2 nejdůležitější metody.
Update()
Update()
obsahuje real-time logiku, tedy vše, co se
zpracovává v reálném čase. Většinou je to klávesnice, případně myš
nebo jiné ovladače, potom kolize a pohyb objektů ve hře. Obecně sem patří
reakce na nějaké události a posun objektů, případně jejich animace.
Metoda se vykonává 60x za vteřinu. Pokud posuneme postavu v této metodě o
1, bude chodit stejně rychle i na jinak rychlém počítači. Jiný interval
můžeme nastavit jako desetinné číslo menší než 1 pomocí vlastnosti hry
TargetElapsedTime
, ale to nebudeme potřebovat. Důležité je
vědět, že MonoGame za nás bude samo dělat optimalizace i když počítač
nebude hru stíhat a to tak, že bude vynechávat vykreslování.
V parametru metody dostaneme instaci GameTime
. Zde je uložen
herní čas, nalezneme na něm 2 užitečné vlastnosti:
ElapsedGameTime
-TimeSpan
s časem uběhnutým od posledního update.TotalGameTime
-TimeSpan
s celkovým časem běhu hry.
Díky těmto hodnotám můžeme v Update pracovat s reálným časem, udělat něco třeba každou sekundu, minutu a podobně. Vše si ukážeme během seriálu.
Další vlastnost na gameTime
je IsRunningSlowly
,
ta je true
, pokud se hra "seká" a MonoGame vynechává
vykreslování. Můžeme tak na tuto situaci reagovat, ale my takto náročné
hry zatím vytvářet nebudeme
V metodě je již doplněná jedna reakce na tlačítko Zpět na gamepadu,
které ukončí hru. To je důležitý kód proto, aby šla hra ukončit na
XBoxu, kdybychom ji tam chtěli nahrát. Mimo jiné vidíme, že ukončení hry
se provede pomocí metody Exit()
.
Draw()
Draw()
se stará o vykreslení hry (jak již název napovídá).
Vykreslování probíhá voláním metod na instanci SpriteBatch
.
Grafické zařízení musíme před každým snímkem (frame) vymazat, aby na
něm nezůstaly vykreslené objekty z minula. Toho je docíleno metodou
Clear()
a modrou barvou (na barvě vlastně nazáleží, pokud bude
mít hra pozadí). Setkáváme se zde se strukturou Color
, ta v
MonoGame slouží k ukládání barev. Má na sobě několik statických metod,
které navrátí její instanci nastavenou na určitou barvu.
Herní smyčka
Je důležité vědět, jak uvnitř hra pracuje. Herní metody se volají v tzv. herní smyčce a to v tomto pořadí:
Initialize()
LoadContent()
Update()
Draw()
UnloadContent()
Update()
a Draw()
se stále opakují, dokud není
hra ukončena. Cyklus znázorněný pomocí vývojového diagramu by vypadal
takto:
Příště, v lekci Vložení obsahu MonoGame hry, si do projektu vložíme sprity, fonty, zvuky a hudbu, půjde tedy o vložení obsahu
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 357x (29.14 kB)
Aplikace je včetně zdrojových kódů v jazyce C#