Táto stránka pre svoju správnu funkčnosť vyžaduje súbory cookies. Slúžia na authentifikáciu návštevníka, analýzu návštevnosti a reklamnú personalizáciu.
logo
Prihlásenie / Registrácia
mobile

Zavrieť
 

Snake v HTML5 – III. Herná logika 2/2

V predchádzajúcom diely sme začali s implementáciou hernej logiky a v tomto diely v tom budeme pokračovať.
Jednoduché hlásenie teraz nahradíme hlásením vykresleným priamo do hry. Najskôr si vytvoríme funkciu ClearGameScreen, ktorú zavoláme vždy, keď bude treba vizuálne vyčistiť hraciu plochu. Hraciu plochu jednoducho prekryjeme obdĺžnikom vyplneným farbou pozadia.
Zdrojový kód:
ClearGameScreen: function ()
    {
        this.Context.fillStyle = BackColor;
        this.Context.fillRect(GameFieldWidth, GameFieldWidth,GameArrayWidth * GameFieldWidth, GameArrayHeight * GameFieldWidth);
    },

Princíp centrovania

Na zobrazenie hlásenia zadefinujeme funkciu DisplayMsg, s parametrami titulok a správa hlásenia. Funkciu budeme volať na začiatku aj na konci hry. Keď je zobrazené hlásenie, hra samozrejme nemôže prebiehať, preto ako prvé vo funkcii zmeníme stav hry na pozastavenú a vyčistíme hraciu plochu. Do hornej ǚcasti hracej plochy vykreslíme titulok za použitia fontu definovaného v premennej MsgTitleFont. Pod titulok umiestnime samotnú správu, na ktorú použijeme font z premennej MsgTextFont. Vertikálnu pozíciu určíme priamo vo funkcii, avšak ak chceme texty vykresliť centrovane, musíme vopred vedieť, aká bude dĺžka textu, keď ho budeme vykreslovať za použitia konkrétneho fontu. Použijeme na to funkciu measureText, ktorú nám poskytuje samotný Canvas, resp. jeho inicializovaný kresliaci objekt. Požadovanú horizontálnu pozíciu textu vyrátame jednoducho. Keď od polovice dĺžky plochy ktorú máme k dispozícii, odrátame polovicu dĺžky objektu ktorý chceme umiestniť do stredu, získame potrebnú pozíciu.

Zdrojový kód:
DisplayMsg: function (Title, Msg)
    {
        this.Status = GameStates.gs_Stop;
        var TextWidth = 0;
        var x, y;

        this.ClearGameScreen();
        this.Context.fillStyle = FrontColor;

        this.Context.font = MsgTitleFont;
        TextWidth = this.Context.measureText(Title).width;
        x = Math.round(this.ContextWidth / 2 - TextWidth / 2);
        y = 40;
        this.Context.fillText(Title, x, y + MsgTextSize);

        this.Context.font = MsgTextFont;
        TextWidth = this.Context.measureText(Msg).width;
        x = Math.round(this.ContextWidth / 2 - TextWidth / 2);
        y = 100;
        this.Context.fillText(Msg, x, y + MsgTextSize);
    },

Jednoduchý alert v CheckLoadedFiles nahradíme naším hlásením.

Zdrojový kód:
CheckLoadedFiles: function ()
    {
        if (Game.Status !== GameStates.gs_Loading && Game.ItemsToLoad <= 0)
        {
            Game.DisplayMsg("Snake", "Press spacebar to start...");
        }
    },

Samotný text hlásenia už napovedá, že hra by sa mala spustiť po stlačení medzerníku. Vytvoríme si preto funkciu KeyDown, ktorú použijeme ako event handler na udalosť stlačenia klávesy. Hra sa začína po stlačení medzerníka (kód 32) a had sa bude ovládať šípkami nahor (38), nadol (40), v ľavo (37) a v pravo (39). Všetky tieto možnosti vložíme ako samostatné vetvy do switch-u a do každej z nich vložíme volanie metódy preventDefault eventu, aby sme potlačili ich štandardnú funkčnosť v prehliadači (pohyb po stránke).

Do vetvy switch-u, ktorá ošetruje stlačenie medzerníka vložíme volanie metódy NewGame objektu Game, ktorú si za chvíľu zadefinujeme. Nechceme ale, aby sa hra resetovala vždy po stlačení medzerníka, preto obmedzíme volanie NewGame len na za predpokladu, že je hra pozastavená.

Po stlačení niektorej zo šípok zmenil by sa mal smer hada. Nesmieme však dovoliť, aby sa had vybral do protismeru. Preto pred samotnou zmenou smeru overíme, či predchádzajúci smer nebol opačným k novému. Funkcia KeyDown po tejto úprave bude vyzerať nasledovne.

Zdrojový kód:
KeyDown: function (e)
    {
        switch (e.keyCode)
        {
            case 38: //Up
                {
                    if (Game.LastDirection !== SnakeDirections.dir_down)
                        Game.Direction = SnakeDirections.dir_up;

                    e.preventDefault();
                }
                break;
            case 40: //Down
                {
                    if (Game.LastDirection !== SnakeDirections.dir_up)
                        Game.Direction = SnakeDirections.dir_down;

                    e.preventDefault();
                }
                break;
            case 37: //Left
                {
                    if (Game.LastDirection !== SnakeDirections.dir_right)
                        Game.Direction = SnakeDirections.dir_left;
                    
                    e.preventDefault();
                }
                break;
            case 39: //Right
                {
                    if (Game.LastDirection !== SnakeDirections.dir_left)
                        Game.Direction = SnakeDirections.dir_right;

                    e.preventDefault();
                }
                break;
            case 32: //SpaceBar
                {
                    if (Game.Status === GameStates.gs_Stop)
                    {
                        Game.NewGame();
                    }

                    e.preventDefault();
                }
                break;
        }
    },

Funkciu KeyDown samozrejme zaregistrujeme ako event handler na začiatku LoadGame.

Zdrojový kód:
LoadGame: function ()
    {
        //Event listener na stlacene klavesy
     window.addEventListener('keydown', this.KeyDown);
     ….
},

Funkcia NewGame zabezpečí vyčistenie hernej plochy, vynulovanie herných premenných, nakreslenie hada, potravy a vytvorí požiadavku o aktualizáciu hry. Aktualizáciu hry bude zabezpečovať funkcia GameUpdateRequest, ktorá pomocou window.setTimeout oneskorene vyvolá aktualizáciu hry.

Zdrojový kód:
NewGame: function ()
    {
        this.Status = GameStates.gs_Play;

        this.ClearGameScreen();

        this.CreateGameArray();
        this.PlaceSnake();
        this.PlaceFood();

        this.Score = 0;
        this.UpdateScore();

        this.GameUpdateRequest();
    },
    GameUpdateRequest: function ()
    {
        window.setTimeout("Game.GameUpdate();", 250);
    },
    Crash: function ()
    {
        this.DisplayMsg("Game over", "Press spacebar to restart...");
    },

Vo vyššie uvedenom kóde máme použité volanie funkcii, ktoré sme ešte nedefinovali (PlaceSnake, PlaceFood a GameUpdate). PlaceSnake inicializuje pole SnakeBlockIndexes, premenné SnakeHeadLeft, SnakeHeadTop a nastaví smer hada v premennej Direction.
PlaceFood umiestni jedlo na náhodnú pozíciu v hernom poli. Funkcia GameUpdate sa zase postará o aktualizáciu hry a v prípade, ak má hra ďalej pokračovať (had nenarazil do steny ani do samého seba) opätovne zavolá GameUpdateRequest, čím sa uzavrie kruh a hra beží ďalej. Ak však hra nemôže pokračovať, zavolá funkciu Crash, ktorá zastaví hru a zobrazí hlásenie.

Zdrojový kód:
PlaceSnake: function ()
    {
        this.SnakeBlockIndexes = [];

        for (var i = 0; i < 4; i++ )
        {
            this.ChangeField(i, 1, FieldStates.fs_Snake);
            this.SnakeBlockIndexes[i] = [3 - i, 1];
        }

        this.Direction = SnakeDirections.dir_right;
        this.SnakeHeadLeft = 3;
        this.SnakeHeadTop = 1;
    },
PlaceFood: function ()
    {
        var PlaceFound = false;
        var x = 0;
        var y = 0;

        while (!PlaceFound)
        {
            x = Math.floor(Math.random() * GameArrayWidth);
            y = Math.floor(Math.random() * GameArrayHeight);

            if (this.GameArray[x][y] === FieldStates.fs_Empty)
            {
                PlaceFound = true;
                this.ChangeField(x, y, FieldStates.fs_Food);
            }
        }
    },
GameUpdate: function ()
    {
        if (this.MoveNext())
        {
            this.GameUpdateRequest();
        }
        else
        {
            this.Crash();
        }
    },

Samotný výkonný kód aktualizácie hry je relatívne dlhý, dáme ho teda do samostatnej funkcie MoveNext, ktorej časť kódu ešte presunieme do funkcie MoveSnakeBlocks, ktorá bude posúvať jednotlivé bloky hada a zaznamená aj posledný smer hada. Vráťme sa ale k samotnej funkcii MoveNext. Jej funkčnosť môžeme rozdeliť na 2 časti.

V prvej časti si „posvietime“ na to, kam sa had chce premiestniť. Ak by jeho nová pozícia bola mimo herné pole, znamená to, že had narazil do steny. V takom prípade nie je možné hada presunúť a funkcia vráti hodnotu false, čím jej beh sa ukončí (nedostaneme sa do druhej časti funkcie) a funkcia GameUpdate namiesto novej požiadavky na aktualizáciu zavolá funkciu Crash čím sa ukončí aj samotný beh hry.

Druhá časť funkcie sa zameriava na aktuálny obsah cieľovej pozície. Ak je cieľová pozícia prázdna, jednoducho presunieme hada. V prípade, ak sa na pozícii nachádza čast hada, hra sa končí, keďže had sa zahryzol sám do seba. Ale ak je tam jedlo, zvýšime skóre, zväčšime dĺžku hada a na náhodnú pozíciu umiestníme ďalšie jedlo.

Zdrojový kód:
MoveNext: function ()
    {
        var NewLeft = this.SnakeHeadLeft;
        var NewTop = this.SnakeHeadTop;

        switch (this.Direction)
        {
            case SnakeDirections.dir_up:
                {
                    if (this.SnakeHeadTop - 1 >= 0)
                    {
                        NewTop--;
                    }
                    else
                    {
                        return false;
                    }
                }
                break;
            case SnakeDirections.dir_down:
                {
                    if (this.SnakeHeadTop + 1 < GameArrayHeight)
                    {
                        NewTop++;
                    }
                    else
                    {
                        return false;
                    }
                }
                break;
            case SnakeDirections.dir_left:
                {
                    if (this.SnakeHeadLeft - 1 >= 0)
                    {
                        NewLeft--;
                    }
                    else
                    {
                        return false;
                    }
                }
                break;
            case SnakeDirections.dir_right:
                {
                    if (this.SnakeHeadLeft + 1 < GameArrayWidth)
                    {
                        NewLeft++;
                    }
                    else
                    {
                        return false;
                    }
                }
                break;
        }

        var NextFieldType = this.GameArray[NewLeft][NewTop];
        switch (NextFieldType)
        {
            case FieldStates.fs_Empty: //Pole je prazdne, mozne sa tam presunut
                {
                    this.MoveSnakeBlocks(NewLeft, NewTop, false);
                }
                break;
            case FieldStates.fs_Snake: //Zahrizneme sa sami do seba
                {
                 return false;
                }
                break;
            case FieldStates.fs_Food:
                {
                    this.MoveSnakeBlocks(NewLeft, NewTop, true);
                    this.Score++;
                    this.UpdateScore();
                    this.PlaceFood();
                }
                break;
        }
        return true;
    },
MoveSnakeBlocks: function (NewLeft, NewTop, Grow)
    {
     this.LastDirection = this.Direction;

        this.SnakeHeadLeft = NewLeft;
        this.SnakeHeadTop = NewTop;

        var SnakeLength = this.SnakeBlockIndexes.length;
        if (Grow)
        {
            //Predlzenie chvosta
            this.SnakeBlockIndexes[SnakeLength] = [];
        }
        else
        {
            //Vymaz posledneho dielu
            var LastBlock = this.SnakeBlockIndexes[SnakeLength - 1];
            this.ChangeField(LastBlock[0], LastBlock[1], FieldStates.fs_Empty);
        }

        var SnakeLength = this.SnakeBlockIndexes.length;
        for (var i = SnakeLength - 1; i >= 1; i--)
        {
            this.SnakeBlockIndexes[i] = this.SnakeBlockIndexes[i - 1];
        }

        this.SnakeBlockIndexes[0] = [NewLeft, NewTop];
        this.ChangeField(NewLeft, NewTop, FieldStates.fs_Snake);
    },

obr. 1

Týmito úpravami sme práve dokončili hernú logiku a môžeme projekt uložiť a vyskúšať v prehliadači. Po načítaní stránky by sa malo zobraziť hlásenie „Snake Press spacebar to start...“. Po stlačení medzerníka sa spustí hra. Na obrazovke by sa mal zobraziť had smerujúci zľava doprava a náhodne umiestnené jedlo. Had by mal byť ovládateľný šipkami.

V nasledujúcom diely doplníme túto minihru o zvukové efekty a tým tento seriál uzavrieme.

Codeblog
Ostatné texty v seriály

Snake v HTML5 – I. Popis riešenia a základná konštrukcia 0
Snake v HTML5 – II. Herná logika 1/2 0
Snake v HTML5 – III. Herná logika 2/2 0
Snake v HTML5 – IV. Zvukové efekty 0
Diskusia

Žiadne príspevky v diskusii.

Nový príspevok

Na prispievanie do diskusie musíte byť prihlásený.