Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

4. diel - Späť a Vpred

V minulom diele som ukazoval, čo to je stav aplikácie a ako ho držať. V tomto diele ukážem, aké výhody nám to prináša.

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

Logovanie

Vo chvíli, keď držíme stav aplikácie na jednom mieste, môžeme všetky jeho zmeny veľmi elegantne logovať a tým vidieť, čo sa v aplikácii deje. Vytvoríme si funkciu wrapReducer.

function wrapReducer(reducer){
    //Kontrola pro případ, že bych byl v prostředí node.js
    if('groupCollapsed' in console){
        return function (oldState,action) {
            console.groupCollapsed(`==[${action.type}]==>`);
            console.log(oldState);
            console.log('||');
            console.log('||');

            console.log(`[${action.type}]`, action);
            const newState = reducer(oldState, action);

            console.log('||');
            console.log('||');
            console.log('\\/');
            console.log(newState);

            console.groupEnd();
            return newState;
        }
    }else{

        return reducer;
    }
}

Potom vytvárame store tak, že reducer najskôr "obalíme" touto logovacie funkcií.

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

Teraz sa po každej akcii do konzoly prehliadača vypíše, čo sa stalo + stav predtým a potom.

Url

Vo webovej aplikácii by bolo dobré, aby sa každá zmena stavu zaznamenávala do URL adresy v prehliadači. Do URL však nemôžeme uložiť príliš veľa informácií a tak môžeme stav ukladať do LocalStorage pod unikátnym IDčkem a dané ID dávať do URL adresy.

function loadState(key){
    try {
        const serializedState = localStorage.getItem(key);
        if (serializedState === null) {
            return null;
        }
        return JSON.parse(serializedState);
    } catch (err) {
        return null;
    }
}
saveState(state){
    const key = uuid.v4();
    const serializedState = JSON.stringify(state);
    localStorage.setItem(key,serializedState);
    return key;
}
function createStateFromUri(uri){
    const key = uri.split('#',2)[1];
    const state = loadState(key);
    if(state){
        return state;
    }else{
        return defaultState;
    }
}
function createUriFromState(state){
    var key = saveState(state);
    return `#${key}`;
}

Teraz budeme počúvať zmeny na store nielen kvôli vykresľovanie, ale aj preto, aby sme si nový stav uložili do LocalStorage a URL.

const store = Redux.createStore(stateReducer, createStateFromUri(document.location.toString()));

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

function render() {
    updateScene(scene, store.getState());
}

store.subscribe(render);
render();


store.subscribe(()=> {
    const state = store.getState();
    const uri = createUriFromState(state);
    const title = createTitleFromState(state);
    document.title = title;
    history.pushState({}, title, uri);
});

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


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

Späť a vpred

Teraz ukladáme stav do URL adresy a pri načítaní hry ho z neho načítame. Ak chceme naplno využiť potenciál webové aplikácie, musíme umožniť užívateľovi klikať na tlačidlá späť a vpred v prehliadači.

V stave aplikácie budeme ukladať poslednú akciu a pridáme akciu CHANGE_STATE, ktorá zmení stav podľa toho, čo ju príde.

function stateReducer(state, action) {
    switch (action.type) {
        case 'CHANGE_STATE':
            return  Object.assign({},action.state, {lastAction:action});

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

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

        default:
            return state;
    }
}

Do URL budeme ukladať zmeny stavu iba ak posledná akcia nie je CHANGE_STATE:

const store = Redux.createStore(wrapReducer(stateReducer), createStateFromUri(document.location.toString()));
const root = document.getElementById("root");
const canvas = document.getElementById("scene");
const engine = new BABYLON.Engine(canvas, true);
const scene = createScene(canvas, engine, store);
engine.runRenderLoop(function () {
    scene.render();
});
window.addEventListener("resize", function () {
    engine.resize();
});

function render() {
    updateScene(scene, store.getState());
}
store.subscribe(()=>{
    const state = store.getState();
    if(state.lastAction.type!=='CHANGE_STATE'){
        const uri = createUriFromState(state);
        history.pushState({}, '', uri);
    }
});
store.subscribe(render);
render();

//Vždy když uživatel klikne na zpět nebo vpřed, odešle se akce CHANGE_STATE.
window.onpopstate = function () {
    const state = createStateFromUri(document.location.toString());
    store.dispatch(createAction.CHANGE_STATE(state));
};

Titulok

Na to, aby naša hra naplno využívala históriu prehliadača, už stačí meniť titulok podľa aktuálneho stavu. Vytvoríme preto funkciu, ktorá vyrába zo stavu titulok stránky.

const WEB_NAME = 'Simple web game';
const TITLE_SEPARATOR = ' | ';

function createTitleFromState(state) {
    let titleParts = [];

    if (state.blocks.length> 1) {
        titleParts.push(state.blocks.length + ' blocks world');
    }
    titleParts.push(WEB_NAME);

    return titleParts.join(TITLE_SEPARATOR);
}

A túto funkciu pri každej zmene stavu využijeme.

store.subscribe(()=>
    const state = store.getState();
    if(state.lastAction.type!=='CHANGE_STATE'){
        const uri = createUriFromState(state);
        const title = createTitleFromState(state);
        document.title = title;
        history.pushState({}, title, uri);
    }
});

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, ako jednotlivé JavaScriptovej súbory spojiť pomocou nástroja WebPack.


 

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é 57x (5.56 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

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