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

3. diel - Stav hry

V minulom diele som ukazoval, ako sa dajú mazať kocky V tomto diele ukážem, ako môžeme držať stav celej aplikácie na jednom mieste.

Pred čítaním tohto článku je dobré poznať základy MVC architektúry a niečo o frameworku Redux.

webová hra - Vytvor si vlastné 3D webovú hru

Stav aplikácie

Stav aplikácie sú dáta, ktoré predstavujú podstatu toho, čo používateľ vidí na obrazovke.

V bežnej webovej aplikácii - napr. Wikipédii, je stav aplikácia to, na akej stránke + odseku som. V Eshope je stav aplikácie zvyčajne aký produkt si prezerám + čo mám v košíku. V našej hre bude stav aplikácie aké kocky sú na scéne.

Redux je Framework pre držanie stavu aplikácie a manipuláciu s ním. V single page aplikáciách využívajúcich Redux sa stav aplikácie obvykle ukladá do čistých javascriptových objektov alebo immutable objektov. Ja využijem čisté JS objekty. Takto bude vyzerať defaultný stav hry:

const defaultState = {
    blocks: [{id:'My first block!!!',position:{x:0,y:0,z:0}}]
};

Poznámka: Natočenie a vzdialenosť kamery by tiež mala byť súčasťou stavu aplikácie. To je však trochu ťažšie na implementáciu a tým pádom tomu venujem celý článok niekedy v budúcnosti.

Na to aby nám dnešná ukážka fungovala, musíme pridať knižnice Redux a knižnicu pre generovanie uuid. V dnešnom diele to urobím v hlavičke html. V niektorom z ďalších dielov použijem balíčkovací systém NPM.

<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.min.js"></script>

Stavom aplikácie sa manipuluje pomocou funkcie zvanej reducer. Do nej odovzdáme pôvodný stav + akciu a von vypadne stav nový. Je veľmi dôležité vyrobiť stav nový a neupravovať stav pôvodnej, aby sme nad aplikáciou nestratili kontrolu. Akcia je prostý JS objekt, ktorý má kľúč type. Ten pomenúva podstatu toho, ako sa stav zmení. Pre našu hru budeme mať zatiaľ 2 typy akcií: pridávanie nové kocky a zmazanie kocky.

function stateReducer(state, action) {
    switch (action.type) {

        case 'BLOCK_ADD':
            return {
                blocks: state.blocks.concat([action.newBlock])
            };

        case 'BLOCK_DELETE':
            return {
                blocks: state.blocks.filter((block)=>block.id!==action.blockId)
            };

        default:
            return state;
    }
}

Teraz môžeme z reducer a defaultného stave vyrobiť Redux store. To je "centrálne úložisko" stave našej hry.

const store = Redux.createStore(stateReducer,defaultState);

Tam, kde som pôvodne zasahoval priamo do Babylon scény, budem odosielať akcie do Redux úložisko.

function onPointerUp(event) {
    const pickInfo = scene.pick(scene.pointerX, scene.pointerY);
    if (pickInfo.hit) {
        const currentMesh = pickInfo.pickedMesh;
        switch (event.button) {
            case 0:
                const diff = currentMesh.position.subtract(pickInfo.pickedPoint);
                const position = currentMesh.position.clone();

                ['x', 'y', 'z'].forEach((dimension) => {
                    if (diff[dimension] >= 0.5 - 0.001) {
                        position[dimension]--;
                    } else if (diff[dimension] <= -0.5 + 0.001) {
                        position[dimension]++;
                    }
                });

                store.dispatch({
                    type: 'BLOCK_ADD', newBlock: {
                        id: uuid.v4(),//Pro nový blok generuji nové id pomocí knihovny "uuid":https://www.npmjs.com/package/uuid
                        position: {
                            x: Math.floor(position.x),
                            y: Math.floor(position.y),
                            z: Math.floor(position.z)
                        }
                    }
                });
                break;

            case 2:
                store.dispatch({type: 'BLOCK_DELETE', blockId: currentMesh.name});
                break;
        }
    }
}

Musím však zaručiť, aby sa kocky vytvorila či zmazala aj v scéne, nie iba v dátovom modeli. Preto si funkciu createScene rozdelím na dve:

  • createScene - Pripraví mi prázdnu Babylon scénu. To docielim tak, že z createScene odstarním časť, kedy vytváram prvý kocku. Tú spustím pri štarte hry.
  • updateScene - Vytvorí v scéne kocky presne podľa aktuálneho stavu aplikácie.
function updateScene(scene, state) {
    //Nejdříve vyčistím scénu.
    scene.meshes.forEach((mesh) => {
        mesh.dispose();
    });
    scene.meshes = [];

    //Vyhledám základní materiál.
    const materialNormal = scene.materials.find(material=>material.name==='material-normal');

    //Potom ji naplním podle aktuálního stavu.
    state.blocks.forEach(block=>{
        const newBox = BABYLON.Mesh.CreateBox(block.id, 1, scene);
        newBox.material = materialNormal;
        newBox.position = new BABYLON.Vector3(block.position.x, block.position.y, block.position.z);
    });
}

Tú spustím po vytvorení store a vždy, keď sa mi zmení stav aplikácie.

const store = Redux.createStore(stateReducer, defaultState);

var canvas = document.getElementById("scene");
var engine = new BABYLON.Engine(canvas, true);
var scene = createScene(canvas, engine);

//Vytvořím pomocnou funkci render.
function render() {
    updateScene(scene, store.getState());
}

store.subscribe(render);
render();

engine.runRenderLoop(function () {
    scene.render();
});

// Resize
window.addEventListener("resize", function () {
    engine.resize();
});

Poznámka: Nie je uplně efektívne po každej zmene stavu celú scénu přemazávat. V tomto prípade som tento postup zvolil preto, že za mierne plytvanie pamäťou, získam oveľa lepšiu kontrolu nad tým, čo sa v aplikácie deje. Neskôr môžeme funkciu updateScene optimalizovať.

Súbory

Vzhľadom na to, že sa nám začína projekt rozrastať, rozdelil som ho do niekoľkých súborov:

src
    script
        scene
            create-scene.ts
            update-scene.ts
        state
            default-state.js
        state-reducers
            state-reducer.js
        browser.tsx
    style
        index.css
index.html

Rozrobenú hru si môžeš stiahnuť pod článkom, alebo ísť do Git repozitára, kde nájdeš najnovšiu verziu zdrojových kódov. Alebo si ju rovno môžeš vyskúšať na webappgames.git­hub.io/web-game. V ďalšom diele ukážem, aké Feature vďaka tomu môžem docieliť.

Vytvor si vlastné 3D webovú hru

 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 71x (3.59 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

Predchádzajúci článok
Vylepšovanie základnej scény
Všetky články v sekcii
Vytvor si vlastné 3D webovú hru
Preskočiť článok
(neodporúčame)
Späť a Vpred
Článok pre vás napísal Pavol Hejný
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
/^(web )?(app )?developer$/
Aktivity