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 – II. Herná logika 1/2

V prechádzajúcej časti sme si zhruba zadefinovali princíp fungovania hry a v tejto časti seriálu sa pustíme do tvorby samotnej hernej logiky.
V prechádzajúcej časti sme si zhruba zadefinovali princíp fungovania hry a v tejto časti seriálu sa pustíme do tvorby samotnej hernej logiky. Celá herná logika bude implementovaná v súbore game.js, ktorý si rovnom otvoríme a nadefinujeme si základné enumeráty, ktoré budu reprezentovať aktuálny stav hry (zastavená, prebiehajúca, načítavajúca sa), obsah pozície v hernom poli (prázdna, had, jedlo) a smer, ktorým sa had pohybuje (nahor, nadol, doprava, doľava).
Zdrojový kód:
var GameStates = {
    gs_Stop: 0,
    gs_Play: 1,
    gs_Loading: 2
};

var FieldStates = {
    fs_Empty: 0,
    fs_Snake: 1,
    fs_Food: 2
};

var SnakeDirections = {
    dir_up: 0,
    dir_down: 1,
    dir_left: 2,
    dir_right: 3
};


V nasledujúcom kroku si zadefinujeme pár dôležitých hodnôt, ktoré budeme ďalej v kóde potrebovat. Konkrétne ide o rozmery pola v ktorom sa môže had pohybovať, veľkosť jednej pozície v pixeloch, farbu pozadia a popredia (farba, ktorou budeme „kresliť“) a fonty písma, ktorými budeme zobrazovať skóre a hlášky v hre.
Zdrojový kód:
var GameArrayWidth = 17;
var GameArrayHeight = 10;
var GameFieldWidth = 15;
var BackColor = "#5ce75c";
var FrontColor = "#000000";
var ScoreTextSize = 12;
var ScoreTextFont = "bold " + ScoreTextSize.toString() +"px Arial Black";
var MsgTitleSize = 24;
var MsgTitleFont = "bold " + MsgTitleSize.toString() + "px Arial Black";
var MsgTextSize = 14;
var MsgTextFont = "bold " + MsgTextSize.toString() + "px Arial Black";

Ďalej si vytvoríme objekt Game, do ktorého postupne dorobíme samotný výkonný kód. Na začiatok do neho vložíme prvé premenné, ktoré budú obsahovať referenciu na Context vytvorený v Canvase, jeho rozmery, obrázok jedla, aktuálne skóre, stav hry a nevyhnutné informácie o aktuálnom stave hada. Spolu s nimi si vytvoríme aj funkciu LoadGame, ktorá sa bude volať z našej HTML stránky a zabezpečí inicializáciu potrebných referencií a samotnej hry.

Zdrojový kód:
var Game = {
    //Kresliaca plocha
    Context: null,

    //Rozmery kresliacej plochy
    ContextWidth: 0,
    ContextHeight: 0,

    //Herne pole
    GameArray: null,

    //Status hry
    Status: GameStates.gs_Loading,

    //Aktualne skore
    Score: 0,

    //Obrazok jedla
    img_food: null,

    //Smer hada
    Direction: SnakeDirections.dir_right,

    //Posledny smer hada
    LastDirection: SnakeDirections.dir_right,

    //Pole jednotlivych pozicii, ktore zabera had
    SnakeBlockIndexes: null,

    //Horizontalna pozicia hlavy hada
    SnakeHeadLeft: 0,

    //Vertikalna pozicia hlavy hada
    SnakeHeadTop: 0,

    LoadGame: function ()
    {
        //Inicializacia kresliaceho prostredia
        this.Canvas = document.getElementById("gamecanvas");
        this.Context = this.Canvas.getContext("2d");
        
        // Odpamatame si rozmery kresliacej plochy
        this.ContextWidth = this.Canvas.offsetWidth;
        this.ContextHeight = this.Canvas.offsetHeight;
    }
};

V súbore index.html do elementu body pridáme volanie funkcie LoadGame na udalosť onload, čím zabezpečíme, aby sa hra začala inicializovať ihneď po načítaní stránky.

Zdrojový kód:
<body onload="Game.LoadGame();">

Nasleduje vytvorenie funkcie DrawGameField, ktorá nám nakreslí stenu, slúžiacu ako ohraničenie plochy, v ktorej sa bude had pohybovať. Nakreslíme ju ako obdĺžnik ktorý pôjde stredom krajných pozícií (pod pojmom pozícia sa myslí oblasť o veľkosti 15x15 px, viď prvý diel seriálu), pri čom v spodnej časti si necháme jeden rad voľný na zobrazenie skóre. Volanie funkcie DrawGameField pridáme aj na koniec inicializačnej funkcie LoadGame.

Zdrojový kód:
var Game = {
….
LoadGame: function ()
    {
     …
     //Nakreslime hracie pole
        this.DrawGameField();
    },
DrawGameField: function ()
    {
        this.Context.fillStyle = BackColor;
        this.Context.strokeStyle = FrontColor;

        var RectX1 = Math.floor(GameFieldWidth / 2.0);
        var RectY1 = Math.floor(GameFieldWidth / 2.0);
        
        var RectX2 = GameFieldWidth * (GameArrayWidth + 1);
        var RectY2 = GameFieldWidth * (GameArrayHeight + 1);

        this.Context.strokeRect(RectX1, RectY1, RectX2, RectY2);
    }
};

Okrem hracieho poľa potrebujeme samozrejme vidieť aké je aktuálne skóre. Do objektu Game pridáme novú funkciu UpdateScore, ktorá sa postará o vykreslenie tohto údaju pod hracie pole.

Zdrojový kód:
UpdateScore: function ()
    {
        //Vyratame si pozadovanu poziciu textu
        var ScoreLeft = Math.floor(GameFieldWidth / 2);
        var ScoreTop = Math.floor(((GameArrayHeight + 2) * GameFieldWidth));

        //Vyplnime oblast pozadia farbou pozadia, cim ostranime stare udaje
        this.Context.fillStyle = BackColor;
        this.Context.fillRect(ScoreLeft, ScoreTop, this.ContextWidth - ScoreLeft, this.ContextHeight - ScoreTop);

        //Nastavime font a farbu
        this.Context.font = ScoreTextFont;
        this.Context.fillStyle = FrontColor;

        //Nakreslime samotny text
        var Text = "SCORE: " + this.Score;
        this.Context.fillText(Text, ScoreLeft, ScoreTop + ScoreTextSize);
    }

Volanie funkcie pridáme aj na koniec funkcie LoadGame.

Zdrojový kód:
LoadGame: function ()
    {
     …
        //Zobrazenie score
        this.UpdateScore();
    },

Ak ste postupovali správne, tak po zobrazení súboru index.html v prehliadači by ste mali vidieť stránku, ako na obrázku č.1

obr. 1

Vytvoríme funkciu CreateGameArray, ktorou inicializujeme premennú GameArray v objekte. Pomocou dvoch cyklov vyplníme všetky herné pozície príznakom fs_Empty.

Zdrojový kód:
var Game = {
….
CreateGameArray: function ()
    {
        var i, j;

        this.GameArray = [];
        for (i = 0; i < GameArrayWidth; i++)
        {
            this.GameArray[i] = [];
            for (j = 0; j < GameArrayHeight; j++ )
            {
                this.GameArray[i][j] = FieldStates.fs_Empty;
            }
        }
    },

};

So samotným prázdnym herným poľom by sme v hre dlho nevystačili, vytvoríme si preto funkciu na zmenu obsahu pozície v poli. Funkcia nám okrem toho zabezpečí aj prekreslenie danej pozície na obrazovke. Vstupnými parametrami funkcie bude pozícia v poli a jej nová hodnota.

Zdrojový kód:
ChangeField: function (FldLeft, FldTop, FldState)
    {
        this.GameArray[FldLeft][FldTop] = FldState;

        var RealLeft = (FldLeft + 1) * GameFieldWidth;
        var RealTop = (FldTop + 1) * GameFieldWidth;

        switch (FldState)
        {
            case FieldStates.fs_Empty:
                {
                    this.Context.fillStyle = BackColor;
                    this.Context.fillRect(RealLeft, RealTop, GameFieldWidth, GameFieldWidth);
                }
                break;
            case FieldStates.fs_Snake:
                {
                    this.Context.fillStyle = FrontColor;
                    this.Context.fillRect(RealLeft, RealTop, GameFieldWidth, GameFieldWidth);
                }
                break;
            case FieldStates.fs_Food:
                {
                    this.Context.drawImage(this.img_food, RealLeft, RealTop);
                }
                break;
        }
    },

Vo funkcii ChangeField sme využili predtým definovaný enumerát FieldStates, ktorým vieme jednoducho rozlíšiť obsah pozície a prekresliť ju. Pri rátaní koordinátov na obrazovke používame hodnotu povýšenú o 1, keďže pole na obrazovke je v skutočnosti o 1 pozíciu posunuté, kvôli stene obklopujúcej herné pole. Ak má byť pozícia prázdna, vyplníme ju farbou pozadia. V prípade, ak je na pozícii čast hada, vyplníme ju farbou popredia. Ostala nám ešte možnosť obsadenia pozície jedlom, v tom prípade na pozíciu nakreslíme obrázok img_food.

Pozornejší z vás si určite všimli, že obrázok img_food sme ešte nikde neinicializovali. Túto skutočnosť hneď napravíme, avšak je tu jedna komplikácia s ktorou sa už asi väčšina z vás stretla. Obrázky použité vo webových stránkach (resp. každý súbor mimo html stránky) sa načítavajú neskôr, ako obsah samotnej stránky. Čo je pochopiteľné, keďže prehliadač o nich nevie, dokým na zmienku o nich „nenarazí“ pri spracovaní stránky. Skutočnosť, že prehliadač nájde referencie na iné súbory však neznamená že ich aj hneď načíta, resp stiahne do cache pamäte aby boli hneď prístupné. Ak by to tak robil, načítanie stránky ktorá obsahuje odkazy na napr. na mp3 súbory by sa niekoľko násobne predĺžilo (o zvýšenej potrebe prenesených dát ani nehovorím). Robí to len pre súbory, ktoré sú skutočne nevyhnutné, ako napríklad štýly, skripty, niektoré obrázky (ktoré sú súčasťou obsahu, či už ako obrázok v texte alebo pozadie stránky).

Z tohto dôvodu je potrebné zabezpečiť, aby boli všetky externé súbory (v našom prípade obrázky a zvuky) boli pripravené ešte pred tým ako sa samotná hra spustí. Do objektu Game pridáme číselnú premennú ItemsToLoad, do ktorej si budeme značiť koľko položiek ešte nebolo načítaných, funkciu CreateImage, ktorá vytvorí objekt Image a povýši hodnotu ItemsToLoad. Po načítaní obrázku sa zavolá funkcia ImageLoaded, kde zase hodnotu ItemsToLoad znížime.

Zdrojový kód:
var Game = {

    //Pocet poloziek, ktore sa este nacitavaju
    ItemsToLoad: 0,
…,
    CreateImage: function (FileName)
    {
        var img = new Image();
        this.ItemsToLoad++ ;
        img.src = "imgs/" + FileName + ".png";
        img.onload = this.ImageLoaded;
        return img;

    },
     ImageLoaded: function ()
    {
        this.onload = null;
        Game.ItemsToLoad--;
        Game.CheckLoadedFiles();
    }
    ...
};

Na poslednom riadku ImageLoaded je volaná funkcia CheckLoadedFiles. Jej úlohou bude zistiť, či už nie je náhodou všetko načítané a je možné spustiť hru (ak je všetko ostatné už pripravené).

Samotný obrázok jedla si môžete stiahnuť s tohto odkazu > link < a uložiť do podpriečinka imgs v projekte. Samozrejme si môžete nakresliť vlastný obrázok, dôležité je zachovať rozmery 15x15 px a farbu pozadia, popredia a formát .png.

Zdrojový kód:
CheckLoadedFiles: function ()
    {
        if (Game.Status !== GameStates.gs_Loading && Game.ItemsToLoad <= 0)
        {
             alert("Hra je pripravená");
        }
    },

Volanie CheckLoadedFiles pridáme aj do LoadGame, kde nahradí pre testovacie účely vloženú UpdateScore. Pred jej volaním ešte upravíme stav hry na pozastavenú, čím dáme funkcii CheckLoadedFiles „vedieť“, že sa čaká už len na načítanie externých súborov. Okrem toho, na začiatok LoadGame pridáme ešte inicializáciu obrázku jedla.

Funkcia LoadGame by mala aktuálne vyzerať nasledovne:

Zdrojový kód:
LoadGame: function ()
    {
         //Nacitanie obrazku jedla
        this.img_food = this.CreateImage("food");
        
        //Inicializacia kresliaceho prostredia
        this.Canvas = document.getElementById('gamecanvas');
        this.Context = this.Canvas.getContext("2d");
        
        // Odpamatame si rozmery kresliacej plochy
        this.ContextWidth = this.Canvas.offsetWidth;
        this.ContextHeight = this.Canvas.offsetHeight;
        
        //Nakreslime hracie pole
        this.DrawGameField();
        
        //Stav hry je pozastaveny, inicializacia v kode je hotova,
        //caka sa uz len na nacitanie externych suborov */
        this.Status = GameStates.gs_Stop;
        
        //Skontrolujeme ci uz nebolo vsetko nahrate
        this.CheckLoadedFiles();
    },

Teraz môžeme projekt uložiť a otvoriť v prehliadači. Ak sme sa nikde nepomýlili, tak sa po načítaní stránky zobrazí hlásenie „Hra je pripravená“.

Týmto sa tento diel končí, v nasledujúcom diely budeme pokračovať v implementácii hernej logiky.

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