用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 ignoredif (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 queuethis.game.load.start();}}else{// No init? Then there was nothing to load eitherthis.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 statethis.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 statethis.onShutDownCallback = state['shutdown'] || this.dummy;// Reset the physics system, but not on the first state startif (this.current !== ''){this.game.physics.reset();}this.current = key;this._created = false;// At this point key and pendingState should equal each otherthis.onInitCallback.apply(this.callbackContext, this._args);// If they no longer do then the init callback hit StateManager.startif (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?錯誤。