IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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í.

1. diel - Štruktúra MonoGame hry

V úvodnom diele o MonoGame sme si urobili úvod do MonoGame a framework sme si nainštalovali. Dnes sa pozrieme na štruktúru MonoGame hry a vyskúšame si úplné základy práce s frameworkom.

Ešte raz upozorňujem, že tento kurz sa venuje výučbe frameworku MonoGame, nie výučbe jazyka C #. Ak C# neovládate, prečítajte si najprv aspoň prvé 2 sekcia C# tutoriálov a potom prácu so súbormi.

MonoGame sme si už nainštalovali minule, spustite si teda Visual Studio a založme nový projekt MonoGame (MonoGame Cross Platform Desktop Project), ktorý pomenujeme Robotris:

Založenie nového projektu - Od nuly k tetrisu v MonoGame

Ak vám názov pripomenul Tetris, máte pravdu, celý kurz sa bude točiť okolo tejto hry. Postupne si vytvoríme hru, herné menu, on-line skóre tabuľku a obrazovku s autormi hry. Naučíte sa základy práce s frameworkom a budete potom schopní vytvoriť akúkoľvek vlastnú hru :)

Štruktúra projektu

MonoGame hra má svoju špecifickú štruktúru. V novom solution nájdeme zložky x64/ a x86/, ktoré spolu s so súbormi *.dylib zaisťujú chod Cross-Platform, pre náš tutoriál je dôležitý súbor Game1.cs, ktorý je samotná MonoGame hra. A zložka Content/ je tzv. Obsah. Tak MonoGame (Pôvodne Microsoft v XNA) nazval obrázky, zvuky a hudbu. Práve do tejto zložky cez MonoGame Pipeline Tool ich budeme pridávať.

Zamerajme sa najprv na triedu Game1.cs, ktorú nám Visual Studio vygenerovalo a popíšme si jej kód, ktorý vyzerá nejako 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);
        }
    }
}

Menné priestory

Najprv máme deklarácii niekoľkých menných priestorov. Tu si môžeme všimnúť, že žiadny MonoGame namespace vlastne nie je a že všetko je z menného priestoru Microsoft.Xna.Framework. To vďaka postavenie MonoGame na XNA frameworku a vývojári sa rozhodli zachovať pôvodný menný priestor. Máme vysvetlené a teraz prejdime k triede Game1.

Trieda Game

Triedu premenujeme na Hra a to tak, že premiestnime kurzor na Game1 a stlačíme F2, nasledujúci dialóg tiež potvrdíme. Vidíme, že trieda dedí z Microsoft.Xna.Framework.Game.

Máme založené 2 premenné a síce graphics a spriteBatch.

  • graphics je typu GraphicsDeviceManager a poskytuje metódy napr. pre zmenu veľkosti herného okna, prepnutie fullscreen (celoobrazovkového režimu) a podobne.
  • spriteBatch je inštancia triedy SpriteBatch, ktorá poskytuje funkcionalitu pre prácu so sprity. Máme tu nový termín - Sprite. Sprite (sprajt, sprit) je vlastne obrázok. Ten je kľúčovým prvkom 2D hier, cez sprity sa rieši v podstate všetko od pozadia, cez postavy v hre až po písmo, kde je každé písmeno jeden sprite. Sprity môžu byť aj animované.

Konštruktor a metóda Initialize ()

Prejdime ku konstruktoru. Tu sa inicializuje premenná graphics a tiež sa nastaví koreňový priečinok pre Content. Už vieme, že je to zložka, v ktorej budú naše herné dáta.

Okrem konstruktoru tu máme aj metódu Initialize(). To môže byť mätúce a tiež, že je :) MonoGame vlastne nijako nehovorí, kedy použiť k inicializácii konštruktor a kedy metódu Initialize().

Rozdiel je v tom, že konštruktor sa volá hneď pri vytvorení hry (alebo herné komponenty, o tých až neskôr) a mal by nastaviť hru alebo komponent tak, aby bola funkčná. To je u hry vždy a preto sem nič už písať nebudeme. Ďalší význam konstruktoru ich na predkladanie závislostí, to spoznáme u komponentov, z ktorých sa hra potom môže skladať.

Metóda Initialize() potom slúži na načítanie dát, ktoré nie sú obsah (Content, teda nie sú sprity, zvuky alebo hudba), môžu to byť napr. Nejaké súbory máp. Tiež tu prevedieme všetku inicializácia hry, vytvorenie potrebných objektov, nastavenie premenných na predvolené hodnoty a podobne.

V Initialize() máme vložený riadok kódu:

base.Initialize();

Ten sa stará o spustenie metódy Initialize() na všetkých komponentoch hry. O komponentoch ale až neskôr. Podobný riadok nájdeme aj v ďalších metódach triedy.

LoadContent ()

Ďalej máme v triede metódu LoadContent(), do tej patrí načítanie obsahu, teda sprites, zvukov a hudby. Vidíme, že sa tu vytvára i spriteBatch. Na koniec metódy môžeme písať logiku, ktorú potrebujeme spustiť až po načítaní obsahu. Napr. nemôžeme v Initialize() zistiť výšku fontu, ktorý ešte nie je načítaný, preto by kód patril do LoadContent().

UnloadContent() sa volá po skončení hry, pretože my budeme vždy používať Content (čo je vlastnosť hry, kde je inštancia ContentManager), ktorá si uvoľnenie zdrojov rieši sama, nie je pre nás metóda dôležitá.

Nasledujú 2 najdôležitejšie metódy.

Update ()

Update() obsahuje real-time logiku, teda všetko, čo sa spracováva v reálnom čase. Väčšinou je to klávesnica, prípadne myš alebo iné ovládače, potom kolízie a pohyb objektov v hre. Všeobecne sem patrí reakcia na nejaké udalosti a posun objektov, prípadne ich animácie. Metóda sa vykonáva 60x za sekundu. Ak posunieme postavu v tejto metóde o 1, bude chodiť rovnako rýchlo aj na inak rýchlom počítači. Iný interval môžeme nastaviť ako desatinné číslo menšie ako 1 pomocou vlastnosti hry TargetElapsedTime, ale to nebudeme potrebovať. Dôležité je vedieť, že MonoGame za nás bude samo robiť optimalizácia aj keď počítač nebude hru stíhať a to tak, že bude vynechávať vykresľovanie.

V parametri metódy dostaneme instaci GameTime. Tu je uložený herný čas, nájdeme na ňom 2 užitočné vlastnosti:

  • ElapsedGameTime - TimeSpan s časom uběhnutým od posledného update.
  • TotalGameTime - TimeSpan s celkovým časom behu hry.

Vďaka týmto hodnotám môžeme v Update pracovať s reálnym časom, urobiť niečo treba každú sekundu, minútu a podobne. Všetko si ukážeme počas seriálu.

Ďalšia vlastnosť na gameTime je IsRunningSlowly, tá je true, ak sa hra "seká" a MonoGame vynecháva vykresľovanie. Môžeme tak na túto situáciu reagovať, ale my takto náročné hry zatiaľ vytvárať nebudeme :)

V metóde je už doplnená jedna reakcia na tlačidlo Späť na gamepadu, ktoré ukončí hru. To je dôležitý kód preto, aby išla hra ukončiť na Xboxe, keby sme ju tam chceli nahrať. Okrem iného vidíme, že ukončenie hry sa vykoná pomocou metódy Exit().

Draw ()

Draw() sa stará o vykreslenie hry (ako už názov napovedá). Vykresľovanie prebieha volaním metód na inštanciu SpriteBatch. Grafické zariadenie musíme pred každým snímkou (frame) vymazať, aby na ňom nezostali vykreslené objekty z minula. Toho je docielené metódou Clear() a modrou farbou (na farbe vlastne nazáleží, pokiaľ bude mať hra pozadie). Stretávame sa tu so štruktúrou Color, tá v MonoGame slúži na ukladanie farieb. Má na sebe niekoľko statických metód, ktoré navráti jej inštanciu nastavenú na určitú farbu.

Herné slučka

Je dôležité vedieť, ako vo vnútri hra pracuje. Herný metódy sa volajú v tzv. Hernej slučke a to v tomto poradí:

  1. Initialize()
  2. LoadContent()
  3. Update()
  4. Draw()
  5. UnloadContent()

Update() a Draw() sa stále opakujú, až kým nie je hra ukončená. Cyklus znázornený pomocou vývojového diagramu by vyzeral takto:

Herný slučka v MonoGame - Od nuly k tetrisu v MonoGame

Nabudúce, v lekcii Vloženie obsahu MonoGame hry , si do projektu vložíme sprity, fonty, zvuky a hudbu, pôjde teda o vloženie obsahu :)


 

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é 357x (29.14 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Všetky články v sekcii
Od nuly k tetrisu v MonoGame
Preskočiť článok
(neodporúčame)
Vloženie obsahu MonoGame hry
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity