zoukankan      html  css  js  c++  java
  • Phaserjs V2的state状态解析及技巧

    用phaserjs开发了好多游戏了,但是对phaser还是了解不深,只知道怎么去用,今天就特意花点时间研究下phaser的状态管理到底是怎么回事。

    首先,new Phaser.Game,以下是Phaser.Game的部分源码:

    Phaser.Game = function (width, height, renderer, parent, state, transparent, antialias, physicsConfig) {
    
        /**
        * @property {number} id - Phaser Game ID
        * @readonly
        */
        this.id = Phaser.GAMES.push(this) - 1;
    
    ...........................(省略的代码)
        //  Parse the configuration object (if any)
        if (arguments.length === 1 && typeof arguments[0] === 'object')
        {
            this.parseConfig(arguments[0]);
        }
        else
        {
            this.config = { enableDebug: true };
    
            if (typeof width !== 'undefined')
            {
                this._width = width;
            }
    
            if (typeof height !== 'undefined')
            {
                this._height = height;
            }
    
            if (typeof renderer !== 'undefined')
            {
                this.renderType = renderer;
            }
    
            if (typeof parent !== 'undefined')
            {
                this.parent = parent;
            }
    
            if (typeof transparent !== 'undefined')
            {
                this.transparent = transparent;
            }
    
            if (typeof antialias !== 'undefined')
            {
                this.antialias = antialias;
            }
    
            this.rnd = new Phaser.RandomDataGenerator([(Date.now() * Math.random()).toString()]);
    
            this.state = new Phaser.StateManager(this, state);
        }
    
        this.device.whenReady(this.boot, this);
    
        return this;
    
    };

    先看this.device.whenReady(this.boot, this);这段代码,源码的意思是设备准备就绪或者dom文档准备就绪后就执行boot,说白了就是DOMContentLoaded这个事件或者window的load事件的回调,一切准备就绪,执行boot,boot源码如下:

    boot: function () {
    
            if (this.isBooted)
            {
                return;
            }
    
            this.onPause = new Phaser.Signal();
            this.onResume = new Phaser.Signal();
            this.onBlur = new Phaser.Signal();
            this.onFocus = new Phaser.Signal();
    
            this.isBooted = true;
    
            PIXI.game = this;
    
            this.math = Phaser.Math;
    
            this.scale = new Phaser.ScaleManager(this, this._width, this._height);
            this.stage = new Phaser.Stage(this);
    
            this.setUpRenderer();
    
            this.world = new Phaser.World(this);
            this.add = new Phaser.GameObjectFactory(this);
            this.make = new Phaser.GameObjectCreator(this);
            this.cache = new Phaser.Cache(this);
            this.load = new Phaser.Loader(this);
            this.time = new Phaser.Time(this);
            this.tweens = new Phaser.TweenManager(this);
            this.input = new Phaser.Input(this);
            this.sound = new Phaser.SoundManager(this);
            this.physics = new Phaser.Physics(this, this.physicsConfig);
            this.particles = new Phaser.Particles(this);
            this.create = new Phaser.Create(this);
            this.plugins = new Phaser.PluginManager(this);
            this.net = new Phaser.Net(this);
    
            this.time.boot();
            this.stage.boot();
            this.world.boot();
            this.scale.boot();
            this.input.boot();
            this.sound.boot();
            this.state.boot();
    
            if (this.config['enableDebug'])
            {
                this.debug = new Phaser.Utils.Debug(this);
                this.debug.boot();
            }
            else
            {
                this.debug = { preUpdate: function () {}, update: function () {}, reset: function () {}, isDisabled: true };
            }
    
            this.showDebugHeader();
    
            this.isRunning = true;
    
            if (this.config && this.config['forceSetTimeOut'])
            {
                this.raf = new Phaser.RequestAnimationFrame(this, this.config['forceSetTimeOut']);
            }
            else
            {
                this.raf = new Phaser.RequestAnimationFrame(this, false);
            }
    
            this._kickstart = true;
    
            if (window['focus'])
            {
                if (!window['PhaserGlobal'] || (window['PhaserGlobal'] && !window['PhaserGlobal'].stopFocus))
                {
                    window.focus();
                }
            }
    
            if (this.config['disableStart'])
            {
                return;
            }
    
            if (this.cache.isReady)
            {
                this.raf.start();
            }
            else
            {
                this.cache.onReady.addOnce(function () {
                    this.raf.start();
                }, this);
            }
    
        }

    很有序的初始化一些事件回调和方法,其中world,stage等等都有boot初始化方法,整个Phaser里Phaser.Stage只在这里实例化一次,仅此一次,游戏的舞台就只有这一个stage,stage继承PIXI.DisplayObjectContainer,到这里就都清楚了。

    Phaser.World也仅此一次被实例化,它继承自Phaser.Group,Phaser.Group也继承自PIXI.DisplayObjectContainer,但是实例化它的时候,默认是把它添加到Phaser.World里的,

    if (parent === undefined)
        {
            parent = game.world;
        }

    所以Group都是在World里,除非显示指定stage为其parent,实际开发几乎没这个必要。

    Phaser.GameObjectFactory类封装了Phaser的组件,如image,sprite等,显示组件基本都是通过game.add直接被添加到World里(bitmapData除外这里不作研究):
    image: function (x, y, key, frame, group) {
    
            if (group === undefined) { group = this.world; }
    
            return group.add(new Phaser.Image(this.game, x, y, key, frame));
    
        },

    接下来看下this.state = new Phaser.StateManager(this, state);这段代码,Phaser.StateManager也是仅被实例化一次,主要用来管理游戏state的,构造函数有两个参数game和state,game就是我们的游戏对象,stage是默认的

    boot: function () {
    
            this.game.onPause.add(this.pause, this);
            this.game.onResume.add(this.resume, this);
    
            if (this._pendingState !== null && typeof this._pendingState !== 'string')
            {
                this.add('default', this._pendingState, true);
            }
    
        },

    默认state的key为'default',通过game.state.add添加state的源码:

    add: function (key, state, autoStart) {
    
            if (autoStart === undefined) { autoStart = false; }
    
            var newState;
    
            if (state instanceof Phaser.State)
            {
                newState = state;
            }
            else if (typeof state === 'object')
            {
                newState = state;
                newState.game = this.game;
            }
            else if (typeof state === 'function')
            {
                newState = new state(this.game);
            }
    
            this.states[key] = newState;
    
            if (autoStart)
            {
                if (this.game.isBooted)
                {
                    this.start(key);
                }
                else
                {
                    this._pendingState = key;
                }
            }
    
            return newState;
    
        },

    就是一个包含init,preload,create等的object或function,或者是Phaser.State类,这个类就是封装一个state包含的所有方法,所有方法没有任何实现。

    game.state.start源码:

    start: function (key, clearWorld, clearCache) {
    
            if (clearWorld === undefined) { clearWorld = true; }
            if (clearCache === undefined) { clearCache = false; }
    
            if (this.checkState(key))
            {
                //  Place the state in the queue. It will be started the next time the game loop begins.
                this._pendingState = key;
                this._clearWorld = clearWorld;
                this._clearCache = clearCache;
    
                if (arguments.length > 3)
                {
                    this._args = Array.prototype.splice.call(arguments, 3);
                }
            }
    
        },

    就是检查下是否是一个State,没什么啊,其实真正的start是在preUpadte里,

    preUpdate: function () {
    
            if (this._pendingState && this.game.isBooted)
            {
                var previousStateKey = this.current;
    
                //  Already got a state running?
                this.clearCurrentState();
    
                this.setCurrentState(this._pendingState);
    
                this.onStateChange.dispatch(this.current, previousStateKey);
    
                if (this.current !== this._pendingState)
                {
                    return;
                }
                else
                {
                    this._pendingState = null;
                }
    
                //  If StateManager.start has been called from the init of a State that ALSO has a preload, then
                //  onPreloadCallback will be set, but must be ignored
                if (this.onPreloadCallback)
                {
                    this.game.load.reset(true);
                    this.onPreloadCallback.call(this.callbackContext, this.game);
    
                    //  Is the loader empty?
                    if (this.game.load.totalQueuedFiles() === 0 && this.game.load.totalQueuedPacks() === 0)
                    {
                        this.loadComplete();
                    }
                    else
                    {
                        //  Start the loader going as we have something in the queue
                        this.game.load.start();
                    }
                }
                else
                {
                    //  No init? Then there was nothing to load either
                    this.loadComplete();
                }
            }
    
        },

    start的时候,this._pendingState设置了当前的state,preUpdate方法先清除上一个state,再设置当前state,

    setCurrentState里调用了link方法同时初始化一些事件监听,clearCurrentState方法调用了unlink方法同时移除一些事件监听,link方法如下:
    link: function (key) {
    
            var state = this.states[key];
    
            state.game = this.game;
            state.add = this.game.add;
            state.make = this.game.make;
            state.camera = this.game.camera;
            state.cache = this.game.cache;
            state.input = this.game.input;
            state.load = this.game.load;
            state.math = this.game.math;
            state.sound = this.game.sound;
            state.scale = this.game.scale;
            state.state = this;
            state.stage = this.game.stage;
            state.time = this.game.time;
            state.tweens = this.game.tweens;
            state.world = this.game.world;
            state.particles = this.game.particles;
            state.rnd = this.game.rnd;
            state.physics = this.game.physics;
            state.key = key;
    
        },

    由此可见每个状态都有game所拥有的几乎所有属性,所以每个state都相当于一个单独的game,包括init,preload,create等等。

    setCurrentState:

    setCurrentState: function (key) {
    
            var state = this.states[key];
    
            this.callbackContext = state;
    
            this.link(key);
    
            //  Used when the state is set as being the current active state
            this.onInitCallback = state['init'] || this.dummy;
    
            this.onPreloadCallback = state['preload'] || null;
            this.onLoadRenderCallback = state['loadRender'] || null;
            this.onLoadUpdateCallback = state['loadUpdate'] || null;
            this.onCreateCallback = state['create'] || null;
            this.onUpdateCallback = state['update'] || null;
            this.onPreRenderCallback = state['preRender'] || null;
            this.onRenderCallback = state['render'] || null;
            this.onResizeCallback = state['resize'] || null;
            this.onPausedCallback = state['paused'] || null;
            this.onResumedCallback = state['resumed'] || null;
            this.onPauseUpdateCallback = state['pauseUpdate'] || null;
    
            //  Used when the state is no longer the current active state
            this.onShutDownCallback = state['shutdown'] || this.dummy;
    
            //  Reset the physics system, but not on the first state start
            if (this.current !== '')
            {
                this.game.physics.reset();
            }
    
            this.current = key;
            this._created = false;
    
            //  At this point key and pendingState should equal each other
            this.onInitCallback.apply(this.callbackContext, this._args);
    
            //  If they no longer do then the init callback hit StateManager.start
            if (key === this._pendingState)
            {
                this._args = [];
            }
    
            this.game._kickstart = true;
    
        },

    每个state在start的时候都会重新执行init,preload,create,update等方法一遍,this.onInitCallback.apply的时候还把参数传进来了,所以如果想start state的时候传参数,请定义个init方法,接受初始化传过来的data,这种模式犹如class的contructor一样使得

    state之间相互独立,又可以相互传值,形成一条state网状链式结构,由于clearCurrentState默认clearWorld = true,所以在切换state的时候会先game.world.shutdown();相当于移除所有舞台元素,同时提供了shutdown方法用于关闭state时做的处理。

    整个phaser的state流程是----》

    初始化Phaser.Game可设置默认state ==》game.state.add(key,state);添加state,==》start state:game.state.start(key1) ==> 移除key1,game.state.start(key2) ==》如此循环,整个过程所有state共享game同时共享其所有方法和属性。

    Phaser的初衷也是以最快的速度完成一个游戏,的确这种相互独立e又可以相互连接的state真是可以为开发节省很多很多时间。

    设计思路:一个游戏先preload(一般之前会加个boot)=》menuState = 》gameState1=》gameState2=》gameState3等等=》overState  独立模块或场景都可添加state,UI界面最好是继承Group,不要做state,注意由于state切换的时候会destroy world,所以单例或共享View界面最好在start之前全部先移除,否则会出现destroy摧毁当前的单例view的child等所有 导致单例undefined 错误。

  • 相关阅读:
    git常用指令 github版本回退 reset
    三门问题 概率论
    如何高效的学习高等数学
    数据库6 关系代数(relational algebra) 函数依赖(functional dependency)
    数据库5 索引 动态哈希(Dynamic Hashing)
    数据库4 3层结构(Three Level Architecture) DBA DML DDL DCL DQL
    梦想开始的地方
    java String字符串转对象实体类
    java 生成图片验证码
    java 对象之间相同属性进行赋值
  • 原文地址:https://www.cnblogs.com/wangzisheng/p/9051839.html
Copyright © 2011-2022 走看看