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,
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 错误。