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).
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.
Vytvoríme funkciu CreateGameArray, ktorou inicializujeme premennú GameArray v objekte. Pomocou dvoch cyklov vyplníme všetky herné pozície príznakom fs_Empty.
Ostatné texty v seriály
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
};
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.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";
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.//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;
}
};
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.….
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);
}
};
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.{
//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);
}
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 {
…
//Zobrazenie score
this.UpdateScore();
},
obr. 1
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.….
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;
}
}
},
…
};
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.{
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;
}
},
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.…
//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();
}
...
};
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:{
if (Game.Status !== GameStates.gs_Loading && Game.ItemsToLoad <= 0)
{
alert("Hra je pripravená");
}
},
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.
{
//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();
},
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 |
Žiadne príspevky v diskusii.
Na prispievanie do diskusie musíte byť prihlásený.