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í.

Lekce 3 - Hra JellyBox v MonoGame - Střely a Logika

V minulém tutoriálu o MonoGame, Hra JellyBox v MonoGame - Želé a Score, 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 - Hra JellyBox v MonoGame

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říště, Hra JellyBox v MonoGame - Logika a Nové želé, už bude i se zábavnou spustitelnou verzí.


 

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 151x (1.84 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Hra JellyBox v MonoGame - Želé a Score
Všechny články v sekci
Hra JellyBox v MonoGame
Přeskočit článek
(nedoporučujeme)
Hra JellyBox v MonoGame - Logika a Nové želé
Článek pro vás napsal Jakub Lásko[Saarix]
Avatar
Uživatelské hodnocení:
4 hlasů
Věnuji se programování v C#, MonoGame a Unity.
Aktivity