Видео игра је направљена по узору на чувену игру Сокобан. С обзиром да је сада време новогодишњих празника, задатак играча је да помогне Деда Мразу и распореди све поклоне испод јелки, тако да наша верзија игре може да се зове Новогодишњи Сокобан. За померање Деда Мраза користите стрелице на тастатури (уколико стрелице не функционишу, кликните било где у оквиру прозора игре како би постала активна).

Мало је вероватно да ће игра постати хит и привући велики број играча. Основни циљ ове игре је анализа кода и учење програмирања. Игра представља следећи корак након програмирања Блокли Лавиринта и Лего робота. Пре него што почнете са читањем анализе изворног кода игре, предлажем Вам да решите свих десет нивоа Лавиринта. Посебно обратите пажњу на дугме за генерисање ЈаваСкрипт кода. Генерисани код у потпуности одговара графичком програму који сте креирали Ви приликом решавања лавиринта. Тако на пример за графичку наредбу:

одговарајући ЈаваСкрипт код гласи:

if (Maze.isPathLeft()) 
{
    //naredbe u ovom bloku se izvrsavaju ukoliko postoji put levo
    Maze.turnLeft();
}

 

Анализом генерисаног кода покушајте да откријете значења кључних речи: if, else, while, витичастих заграда {}, обичних заграда (), тачка запете ; тачке . У примеру приказаном изнад Maze означава објекат лавиринта, а isPathLeft и turnLeft су функције које припадају објекту Maze. Називе објеката и функција бира сам програмер. Једино правило је да име мора почињати словом, не сме садржати празнине (спејсове) и сва слова морају бити писана енглеским алфабетом. У примеру Новогодишњег Сокобана објекти су коришћени у најмањој могућој мери јер сматрам да ЈаваСкрипт није најбољи језик за учење објектно оријентисаног програмирања. Називи објеката и функција су на српском ради лакше читљивости кода.

Поред ЈаваСкрипта за развој видео игре је неопходно и основно познавање HTML-a и CSS-a.

HTML код игре:

<!--sokoban.html-->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8"/>
  <title>Сокобан</title>
  <link href="sokoban.css" rel="stylesheet">
</head>
<body>
  <div id="sokoban"></div>
  <script src="sokoban.js"></script>
</body>
</html> 

Као што се може приметити почетни HTML код је крајње једноставан. Главни део кода биће генерисан ЈаваСкрипт програмом и додат у div елемент који има идентификатор sokoban током учитавања странице.

Овом приликом биће представљен комплетан код видео игре, а у зависности од интересовања у наредним чланцима би могао бити анализиран сваки део кода и детаљније представљен програмски језик JavaScript.

ЈаваСкрипт код игре:

/*sokoban.js*/
var nivoi  = [
    [
        [0, 0, 0, 1, 1, 1, 1],
        [0, 0, 0, 1, 2, 2, 1],
        [0, 0, 1, 1, 5, 2, 1],
        [1, 1, 1, 6, 6, 2, 1],
        [1, 2, 5, 6, 6, 2, 1],
        [1, 2, 5, 1, 2, 3, 1],
        [1, 2, 5, 2, 2, 2, 1],
        [1, 2, 2, 2, 1, 1, 1],
        [1, 1, 1, 1, 1, 0, 0]	
    ],	
    [
        [0, 0, 1, 1, 1, 0, 0],
        [0, 1, 1, 2, 1, 1, 0],
        [1, 1, 6, 5, 2, 1, 0],
        [1, 6, 6, 6, 2, 1, 1],
        [1, 5, 6, 5, 2, 2, 1],
        [1, 2, 5, 2, 2, 1, 1],
        [1, 2, 5, 1, 1, 1, 0],
        [1, 3, 2, 1, 0, 0, 0],
        [1, 1, 1, 1, 0, 0, 0]	
    ]
];
//Radi lakse citljivosti programa definisane su sledece konstante
var PRAZNINA = 0;
var ZID = 1;
var PRAZNO_POLJE = 2;
var POLJE_SA_DEDA_MRAZOM = 3;
var JELKA_SA_DEDA_MRAZOM = 4;
var POLJE_SA_POKLONOM = 5;
var PRAZNA_JELKA = 6;
var JELKA_SA_POKLONOM = 7;

//promenljive
var aktivniNivo;
var brojJelki; //ukupan broj jelki
var brojPrekrivenih;//broj trenutno prekrivenih jelka. Kada taj broj bude jednak ukupnom broju jelka, zadatak je izvrsen.
var dedaMraz = {red:0, kolona:0};//trenutna pozicija dedaMraza
var mapaPoklona;

var VISINA = 9;
var SIRINA = 7;

function napraviPraznuTablu()
{
    var tabla = document.getElementById('sokoban');
    var i,j;
    var divRed;
    for(i=0; i < VISINA; i++)
    {
        divRed = document.createElement('div');
        divRed.id = 'red'+i;
        divRed.className = 'red';
        tabla.appendChild(divRed);
        for(j=0; j < SIRINA; j++)
        {
            var divKolona = document.createElement('div');
            divKolona.id = 'polje_'+i + '_' +j;
            divKolona.className = 'praznina';
            divRed.appendChild(divKolona);
        }
    }
}

function ucitajPolje(element, iRed, iKolona)
{
    //Dodajem prazan niz za svaki novi pocetak reda, tj. kada je indeks kolone 0
    if(iKolona == 0)
        mapaPoklona.push([]);
    
    var poklon = 0;//za pocetak uzimam da se poklon ne nalazi na poziciji red, kolona
    var prikaz = 'praznina';
    switch(element)
    {
        case PRAZNINA:
            prikaz = 'praznina';
            break;
        case PRAZNO_POLJE:
            prikaz = 'prazno-polje';
            break;
        case POLJE_SA_DEDA_MRAZOM:
            prikaz = 'deda-mraz';
            dedaMraz = {'red':iRed, 'kolona':iKolona};//postavljam poziciju dedaMraza
            break;
        case JELKA_SA_DEDA_MRAZOM:
            prikaz = 'deda-mraz';
            brojJelki++;
            dedaMraz = {'red':iRed, 'kolona':iKolona};
            break;
        case POLJE_SA_POKLONOM:
            prikaz = 'poklon';
            poklon = 1;//postoji poklon na poziciji red, kolona.
            break;
        case JELKA_SA_POKLONOM:
            prikaz = 'jelka-sa-poklonom';
            brojJelki++;
            brojPrekrivenih++;
            poklon = 1;
            break;			
        case PRAZNA_JELKA:
            prikaz = 'jelka';
            brojJelki++;
            break;
        case ZID:
            prikaz = 'zid';
            break;
    }
    //Upisujem vrednost u mapu poklona.
    mapaPoklona[iRed].push(poklon);
    var prikazPolja = document.getElementById('polje_'+iRed+'_'+iKolona);
    prikazPolja.className = prikaz;
}

function jelIspravnaPozicija(pozicija)
{
    if(pozicija.red < 0 || pozicija.red >= VISINA || pozicija.kolona < 0 || pozicija.kolona >= SIRINA)
        return false;
    return true;
}

function osveziPrikazPolja(pozicija)
{
    var poklon = mapaPoklona[pozicija.red][pozicija.kolona];
    var prikaz;
    if(poklon)
    {
        if(jelJelka(pozicija))
            prikaz = 'jelka-sa-poklonom';
        else
            prikaz = 'poklon';
    }
    else if(pozicija.red == dedaMraz.red && pozicija.kolona == dedaMraz.kolona)
        prikaz = 'deda-mraz';
    else if(jelJelka(pozicija))
        prikaz = 'jelka';
    else if(jelPolje(pozicija))
        prikaz = 'prazno-polje';
    else
        prikaz = 'praznina';
    var prikazPolja = document.getElementById('polje_'+pozicija.red+'_'+pozicija.kolona);
    prikazPolja.className = prikaz;		
}

function ucitajNivo(n)
{
    var i, j;	
    brojJelki = 0;
    aktivniNivo = n;
    brojPrekrivenih = 0;		
    mapaPoklona = []; //mapi poklonja se dodeljuje prazan niz			
    for(i=0; i < VISINA; i++)
    {
        for(j=0; j < SIRINA; j++)
        {
            var tipPolja = nivoi[aktivniNivo][i][j];
            ucitajPolje(tipPolja, i, j);
        }
    }
}

function jelSadrziPoklon(pozicija)
{
    if( ! jelIspravnaPozicija(pozicija))
        return false;
    if(mapaPoklona[pozicija.red][pozicija.kolona] == 1)
        return true;
    return false;
}

function jelPrazno(pozicija)
{
    if( ! jelIspravnaPozicija(pozicija))
        return false;
    //ako se dedaMraz trenutno nalazi na poziciji iRed, iKolona onda funkcija vraca false
    if(dedaMraz.red == pozicija.red && dedaMraz.kolona == pozicija.kolona)
        return false;
    //ako se poklon nalazi na poziciji iRed, iKolona takodje vraca false
    if(jelSadrziPoklon(pozicija))
        return false;
    //ako je tip polja poklon ili zid onda polje nije prazno
    var tipPolja = nivoi[aktivniNivo][pozicija.red][pozicija.kolona];
    if(tipPolja == PRAZNINA || tipPolja == ZID)
        return false;
    //ispitao sam sve slucajeve kada nije prazno, tako da mogu da budem siguran da jeste prazno
    return true;
}

function jelJelka(pozicija)
{
    if( ! jelIspravnaPozicija(pozicija))
        return false;
    var kodPolja = nivoi[aktivniNivo][pozicija.red][pozicija.kolona];
    switch(kodPolja)
    {
        case JELKA_SA_DEDA_MRAZOM:
        case JELKA_SA_POKLONOM:
        case PRAZNA_JELKA:
            return true;	
    }
    return false;
}

function jelPolje(pozicija)
{
    if( ! jelIspravnaPozicija(pozicija))
        return false;
    var kodPolja = nivoi[aktivniNivo][pozicija.red][pozicija.kolona];
    switch(kodPolja)
    {
        case POLJE_SA_DEDA_MRAZOM:
        case POLJE_SA_POKLONOM:
        case PRAZNO_POLJE:
            return true;	
    }
    return false;	
}

function nadjenoResenje()
{
    var ukupnoNivoa = nivoi.length;
    var noviNivo = aktivniNivo + 1;
    if(noviNivo >= ukupnoNivoa)
        noviNivo = 0;
    if(noviNivo)
        alert('Пакетићи са нивоа '+ (aktivniNivo+1) + ' су подељени. Прелазимо на ниво ' + (noviNivo+1));
    else
        alert('Честитамо, решили сте све нивое!');
    ucitajNivo(noviNivo);
}

function premestiPoklon(pozicija1, pozicija2)
{
    if( ! jelSadrziPoklon(pozicija1) || ! jelPrazno(pozicija2))
        return false;
    mapaPoklona[pozicija1.red][pozicija1.kolona] = 0;
    if(jelJelka(pozicija1))
        brojPrekrivenih--;	
    mapaPoklona[pozicija2.red][pozicija2.kolona] = 1;
    if(jelJelka(pozicija2))
        brojPrekrivenih++;
    osveziPrikazPolja(pozicija1);
    osveziPrikazPolja(pozicija2);
    return true;
}

function premestiIgraca(pozicija)
{	
    if( ! jelPrazno(pozicija))
        return false;
    var staraPozicija = dedaMraz;
    dedaMraz = pozicija;
    osveziPrikazPolja(staraPozicija);
    osveziPrikazPolja(dedaMraz);		
    return true;
}

function pomeriDedaMraza(event)
{
    var pozicija1 = {}, pozicija2 = {};
    switch(event.keyCode)
    {
        case 37://levo
            pozicija1.red = dedaMraz.red;
            pozicija1.kolona = dedaMraz.kolona - 1;
            pozicija2.red = pozicija1.red;
            pozicija2.kolona = pozicija1.kolona - 1;
            break;
        case 38: //'gore'
            pozicija1.red = dedaMraz.red - 1;
            pozicija1.kolona = dedaMraz.kolona;
            pozicija2.red = pozicija1.red - 1;
            pozicija2.kolona = pozicija1.kolona;		
            break;
        case 39: //'desno'
            pozicija1.red = dedaMraz.red;
            pozicija1.kolona = dedaMraz.kolona + 1;
            pozicija2.red = pozicija1.red;
            pozicija2.kolona = pozicija1.kolona +1;			
            break;
        case 40: //dole
            pozicija1.red = dedaMraz.red + 1;
            pozicija1.kolona = dedaMraz.kolona;
            pozicija2.red = pozicija1.red + 1;
            pozicija2.kolona = pozicija1.kolona;					
            break;
        default:
            return;
    }	
    premestiPoklon(pozicija1, pozicija2);
    premestiIgraca(pozicija1);	
    if(brojPrekrivenih == brojJelki)
        nadjenoResenje();	
}

function init()
{
    napraviPraznuTablu();
    document.addEventListener('keydown', pomeriDedaMraza);
    ucitajNivo(0);	
}

init();

ЈаваСкрипт код генерише структуру документа и дефинише прелазе из једног стања у друго, као одговоре на догађаје тастатуре. Како би играч видео одговарајуће графичке симболе на екрану дефинисани су следећи стилови:

/*style.css*/
#sokoban{width:350px;height:450px; border: 1px solid gray; background:url('pozadina.jpg');}
#sokoban:focus{box-shadow: 3px 3px 3px black;}
.red{width:350px;height:50px;}
.praznina, .zid, .jelka, .deda-mraz, .prazno-polje, .poklon, .jelka-sa-poklonom {width:50px;height:50px;float:left;opacity:0.92;}
.praznina{background:transparent;}
.zid{background:url('zid.png'); box-shadow: 3px 3px 3px black;}
.prazno-polje{background:white;}
.jelka{background:url('jelka.png') white;}
.jelka-sa-poklonom{background:url('jelka-sa-poklonom2.png') white;}
.deda-mraz{background:url('deda-mraz.png') white;}
.poklon{background:url('poklon2.png') white;}

Зиповану архиву пројекта можете преузети са следећег линка: зип архива ~250KB. Сав потребан софтвер за модификовање игре и експериментисање већ имате на свом рачунару. Све што Вам је потребно су веб браузер и текст едитор. Ипак за удобнији рад препоручујем инсталирање додатка Фајербаг за Мозилу Фајерфокс и неки напреднији текст едитор попут Ноутпеда++.

Уколико направите занимљиву модификацију игре, молим Вас да ми пошаљете код како би и Ваша игра била објављена на сајту пројекта Библиотека++.

Ивица
04.01.2015
Одељци: Зид Чланци Ивица | Кључне речи:
0

Коментари:

Ивица: Код видео игре се може додатно поједноставити коришћењем флегова за стање поља. Више о отме можете сазнати у одговору са Елит Секјурити форума: http://www.elitesecurity.org/t481513-0#3527699
08.01.2015 у 14:59 часова
Нови коментар

©Библиотека++ 2017 Развој сајта Ивица Лазаревић