3. díl - Hra JellyBox v MonoGame - Střely a Logika

C# .NET XNA game studio Hra JellyBox Hra JellyBox v MonoGame - Střely a Logika

V minulém tutoriálu o MonoGame jsme si do hry přidali želé a score. V dnešním díle doděláme vše co chybí u score, přidáme si třídu Logic, která nám bude ovládat logiku objektů, aby jsme zredukovali množství kódu ve třídě Game1 a také si přidáme střely, které bude moct hráč střílet.

Textury

V dnešním díle budeme potřebovat pouze 1 texturu a to texturu pro střelu. Texturu opět nahrajeme do složky Content.

Bullet

Jak je vidět, tak dnes se budeme převážně věnovat pouze kódu.

FadeEffect

Ještě, než se pustíme do zcela nových věcí je třeba dodělat náš FadeEffect pro score. Uděláme proto 2 úpravy ve třídě Game1. Najdeme si zde metodu Update a přidáme následujicí kód.

foreach (FadeEffect fe in fades)
        fe.Update(gameTime);

for (int i = 0; i < fades.Count; i++)
{
        if (fades[i].Faded)
                fades.RemoveAt(i);
}

Cyklus foreach slouží k updatu všech fade effectů v listu. Spodní cyklus for slouží k tomu, že projde všechny položky v listu a pokud daná položka má vlastnost Faded nastavenou na true, tak tuto položku vymaže.

Tímto máme již score plně funkční a bude nám pěkně mizet.

Bullet (střela)

Nyní si vytvoříme novou třídu pro střelu s názvem Bullet. Třída bude dědit ze třídy GameObject, ale to už snad víme, jelikož je střela pevný objekt.

class Bullet : GameObject

Ve třídě si vytvoříme privátní int speed, který bude ovládat rychlost naší střely. Pak veřejnou vlastnost bool Delete, která bude rozhodovat o tom, zda se má střela smazat. Třída bude obsahovat 3 metody. První metodou je Update, která pouze zajišťuje pohyb střely směrem nahoru. Druhou metodou je Draw, která vykreslí naši střelu. Třetí metodou je Collision, kde máme jako vstupní parametr Box. Jakmile se střela srazí s nějakým boxem (želé), tak se střela i želé vymaže a hráči se přičte score.

private int speed;
public bool Delete { get; set; }

public Bullet(Texture2D texture, Vector2 position, int speed)
        : base(texture, position)
{
        this.texture = texture;
        this.position = position;
        this.speed = speed;
        this.Delete = false;
}

public void Update(GameTime gameTime)
{
        position.Y -= speed;
}

public void Draw(SpriteBatch spriteBatch)
{
        spriteBatch.Draw(texture, position, Color.White);
}

public void Collision(Box obj, Game1 game)
{
        if (GetRectangle().Intersects(obj.GetRectangle()))
        {
                obj.Delete = true;
                this.Delete = true;
                game.AddScore(2, obj.GetCenterPosition());
        }
}

Jak je zde vidět, tak místo dlouhého určování nového vectoru středu boxu zde pouze zavoláme metodu GetCenterPosition a máme hotovo.

Player

Ještě, než se pustím do třídy Logic, tak díky tomu, že už máme boxy (želé) a střely hotové, doděláme ve třídě Player metodu Collision. Tu jsme předtím pouze definovali, ale nenastavili žádné chování.

public void Collision(Box obj)
{
        if (GetRectangle().Intersects(obj.GetRectangle()))
        {
                obj.Delete = true;
                EatedJellies++;
                game.AddScore(5, obj.GetCenterPosition());
        }
}

Nastavili jsme vstupní parametr Box. Jakmile se hráč střetne s boxem, tak se box vymaže, počet snědených želé se inkrementuje a přidáme hráči 5 score. Nyní už je vše připraveno a můžeme se vrhnout na třídu Logic a vše rozpohybovat.

Logic

Teď si vytvoříme novou třídu Logic, která bude obsahovat většinu "kouzel" naší hry. Zde bude provádět updaty prakticky všech objektů a také nějakou tu nezbytnou logiku spolu s mazáním objektů, či kontrolou kolizí. Ušetříme si tím hromadu místa ve třídě Game1, kde pak budeme řešit menu, game over a generování objektů.

Ve třídě si vytvoříme novou instanci Random, která bude sloužit k náhodnému generování boxů. Dále instanci třídy Game1, odkud budeme potřebovat naše metody. Novou instaci List<Box>, kde budeme mít všechny vygenerované boxy. 3 instance Texture2D (zelené želé, hnědé želé, želé střela). Novou instanci List<Texture2D>, kam nasypeme textury našich boxů a pak z tohoto listu budeme vybírat náhodnou texturu. Novou instanci List<Bullet>, kde budeme mít všechny vegenerované střely. Jako poslední bool keyPressed, který bude sloužit ke kontrole výstřelu, aby se vystřelilo 1 při zmáčknutí klávesy.

private Random random;
private Game1 game;
private List<Box> boxes;
private Texture2D greenBox, brownBox, jellyBullet;
private List<Texture2D> textures;
private List<Bullet> bullets;
private bool keyPressed;

public Logic(Game1 game, List<Box> boxes, List<Bullet> bullets, Texture2D greenBox, Texture2D brownBox,
        Texture2D jellyBullet)
{
        this.game = game;
        this.boxes = boxes;
        this.bullets = bullets;
        this.greenBox = greenBox;
        this.brownBox = brownBox;
        this.jellyBullet = jellyBullet;

        keyPressed = false;
        random = new Random();

        textures = new List<Texture2D>();
        textures.Add(this.greenBox);
        textures.Add(this.brownBox);
}

V konstruktoru nastavíme keyPressed na false, inicializujeme random a textures. Do textures přidíme textury, které budeme používat pro želé, což jsou v našem případe greenBox a brownBox.

Nyní do třídy přidáme metodu s názvem SpawnBox, tato metoda bude mít jako vstupní parametr List<Box>. Uvnitř skrze random vybereme náhodné číslo textury z našeho seznamu textur. Do proměnné x zadáme skrze random náhodné číslo, které bude od 0 po pravý okraj hrací plochy - šířka textury, kterou jsme vybrali. Jako poslední vložíme do listu boxů nový box s námi vybranou texturou a náhodně vygenerovanou poziční hodnotou x.

public void SpawnBox(List<Box> b)
{
        int t = random.Next(0, textures.Count);
        int x = random.Next(0, (int)game.GetScreenSize().X - textures[t].Width);

        b.Add(new Box(textures[t], new Vector2(x, -100)));
}

Takto budeme ve třídě Game1 lehce generovat nové boxy.

Nyní majoritní část třídy Logic a tou je metoda Update o které nám tu v podstatě jde. Nechal jsem v kódu popisky pro lepší hledání daných funkcí. U metody update budeme mít 3 parametry (KeyboardState pro stav klávesnice, Player jakožto instance hráče, GameTime čas hry, který používáme u různých metod).

public void Update(KeyboardState ks, Player player, GameTime gameTime)
{
        // střelba
        if (ks.IsKeyDown(Keys.Space) && !keyPressed)
        {
                bullets.Add(new Bullet(jellyBullet,
                        new Vector2(player.GetPosition().X + player.GetRectangle().Width / 2 - jellyBullet.Width / 2,
                        player.GetPosition().Y - jellyBullet.Height / 2), 3));

                keyPressed = true;
        }
        if (ks.IsKeyUp(Keys.Space) && keyPressed)
                keyPressed = false;

        // updatování střel, kontrola kolize střel a boxů
        foreach (Bullet bullet in bullets)
        {
                // update střel
                bullet.Update(gameTime);
                // kolize střel
                foreach (Box box in boxes)
                        bullet.Collision(box, game);
        }

        // updatování boxů a kontrola jejich kolize se zemí
        foreach (Box b in boxes)
        {
                b.Update(game, gameTime);
                b.Collision(game.GetGroundRect(), game, player);
        }

        // kontrola kolize hráče a boxů
        foreach (Box b in boxes)
                player.Collision(b);

        // mazaní boxů
        for (int i = 0; i < boxes.Count; i++)
        {
                if (boxes[i].Delete)
                        boxes.RemoveAt(i);
        }

        // mazání střel
        for (int i = 0; i < bullets.Count; i++)
        {
                if (bullets[i].Delete)
                        bullets.RemoveAt(i);
        }

        // mazání střel, jakmile vyjedou z hrací plochy
        for (int i = 0; i < bullets.Count; i++)
        {
                if (bullets[i].GetPosition().Y < 0 - bullets[i].GetRectangle().Height)
                        bullets.RemoveAt(i);
        }
}

Na začítku jsme si vytvořili střelbu. Pokud zmáčkneme mezerník a keyPressed je false, tak se vytvoří nová střela a keyPressed se nastaví na true (obrana proti generování více střel při 1 zmáčknutí. Níže v podmínce jakmile je keyPressed true a mezerník není zmáčknutý, tak keyPressed nastavíme na false.

Dále projíždíme cyklem foreach všechny střely, updatujeme je a pak pomocí foreach cyklu projedeme boxy a kontrolujeme zda se daná střela nestřetla s jedním z boxů.

Dále projíždíme cyklem foreach všechny boxy, updatujeme je a kontrolujeme, zda se nestřetli se zemí.

V dalších 2 for cyklech se projíždí seznam střel a boxů, jakmile je vlastnost Delete true, tak daný objekt vymažeme ze seznamu.

Dále projíždíme cyklem for seznam střel a kontrolujeme zda se střela dostala mimo hrací plochu, pokud ano, tak ji rovnou smažeme.

To by bylo zatím v naší třídě Logic vše. Místo se neodvratně krátí, takže toto je pro dnešek vše a přístě si přidáme naši třídu Logic do třídy Game1 a zprovozníme logiku a vše rozhýbeme. Snad se dnešní díl líbil a přístě už bude i se zábavnou spustitelnou verzí.


 

Stáhnout

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

 

  Aktivity (1)

Článek pro vás napsal Jakub Lásko[Saarix]
Avatar
Věnuji se programování v C#, MonoGame a Unity.

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


 


Miniatura
Všechny články v sekci
Hra JellyBox v MonoGame

 

 

Komentáře

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Vypadá to fajn. Co se týče obsluhy komponent, tak bys je mohl všechny oddědit od nějaké třídy, která má metody update a draw. Místo projiždění listů střel a listů želé a bábovky atd. bys je všechny dal do jednoho listu typu předka a na tom zavolal update. Kdybys šel ještě dál, tak bys zjistil, že samotné XNA pro toto má třídu GameComponent, ze kterých se potom výsledná hra skládá a ony se samy obsluhují. Ale u takhle jednoduché hry to nevadí. Těším se na příště, až si to zahraju :) Želé by se mohlo roztahovat a smršťovat, aby to vypadalo jako želé :P

Editováno 2.11.2013 12:56
Odpovědět  +3 2.11.2013 12:51
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Odpovídá na David Čápka
Jakub Lásko[Saarix]:

Animace budou :-) Jak na bábovku, tak střely a padajicí želé. Jinak je pravda že předka s metodami Update a i Draw jsem mohl udělat, ale trochu jsem na to zapoměl. Ještě že je to menší hra :-), jinak dnes sem konečně rozchodil logiku padajicího cukru, takže můžu jít dělat další díl.

Odpovědět 2.11.2013 14:50
Časem je vše možné.
Avatar
Odpovídá na David Čápka
Jakub Lásko[Saarix]:

Jinak co říkáš na předchozí díl, kde se dělalo efektní vykreslení přidání score?

Odpovědět 2.11.2013 14:57
Časem je vše možné.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na Jakub Lásko[Saarix]
Petr Nymsa:

Určitě GameComponent a DrawableGameCom­ponent využívej. Díky nim lze strašně snadno dělat herní správu obrazovek, jednotlivé metody Update() a Draw() se volají automaticky. Například já to mám vyřešené tak, že mám ScreenManager(správ­ce herních obrazovek), následně třídu GameScreen -> jednotlivé obrazovky, ta obsahuje List<GameCompo­ennt>. Všechny komponenty se přidají do listu Components v třídě Game. ScreenManager poté při změně obrazovky zjistí, zda je GameComponenta obsažená v obrazovce a podle toho jí vypne / zapne.

Odpovědět  +3 2.11.2013 17:54
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Jakub Lásko[Saarix]
David Čápka:

Četl jsem to trochu rychle a v ukázce pod článkem ten efekt není, nebo jsem nepřišel na to jak ho docílit :) Nezapomněl jsi to zkompilovat?

Odpovědět 3.11.2013 18:03
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Odpovídá na David Čápka
Jakub Lásko[Saarix]:

Ve výsledku ho ani docílit nešlo, protože se v tom díle nedostalo na samotný Draw, ale v dalším díle, který jsem dnes dodělal už je funkční verze a tam to je krásně vidět při kolizích :-)

Odpovědět 3.11.2013 18:08
Časem je vše možné.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Jakub Lásko[Saarix]
David Čápka:

Dobře, mrknu na to :) Ještě jsme tě chtěl poprosit, abys používal správu obrázků (tlačítko pod editorem článků), musím to jinak ručně stahovat :P

Odpovědět 3.11.2013 18:53
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Odpovědět 3.11.2013 19:28
Časem je vše možné.
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 8 zpráv z 8.