mirror of
https://github.com/photonstorm/phaser
synced 2025-02-17 06:28:30 +00:00
Adding in the State Manager.
This commit is contained in:
parent
1bb711ea2c
commit
c2f624d349
6 changed files with 715 additions and 2 deletions
|
@ -1,4 +1,4 @@
|
|||
var CHECKSUM = {
|
||||
build: 'f71c8930-b620-11e6-b63f-ab05aca00975'
|
||||
build: '099b0010-b62f-11e6-b06a-d5df4eb4313b'
|
||||
};
|
||||
module.exports = CHECKSUM;
|
|
@ -4,7 +4,33 @@ var CONST = {
|
|||
|
||||
AUTO: 0,
|
||||
CANVAS: 1,
|
||||
WEBGL: 2
|
||||
WEBGL: 2,
|
||||
|
||||
blendModes: {
|
||||
NORMAL: 0,
|
||||
ADD: 1,
|
||||
MULTIPLY: 2,
|
||||
SCREEN: 3,
|
||||
OVERLAY: 4,
|
||||
DARKEN: 5,
|
||||
LIGHTEN: 6,
|
||||
COLOR_DODGE: 7,
|
||||
COLOR_BURN: 8,
|
||||
HARD_LIGHT: 9,
|
||||
SOFT_LIGHT: 10,
|
||||
DIFFERENCE: 11,
|
||||
EXCLUSION: 12,
|
||||
HUE: 13,
|
||||
SATURATION: 14,
|
||||
COLOR: 15,
|
||||
LUMINOSITY: 16
|
||||
},
|
||||
|
||||
scaleModes: {
|
||||
DEFAULT: 0,
|
||||
LINEAR: 0,
|
||||
NEAREST: 1
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
16
v3/src/state/STATE_CONST.js
Normal file
16
v3/src/state/STATE_CONST.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
var STATE_CONST = {
|
||||
|
||||
PENDING: 0,
|
||||
INSTALLED: 1,
|
||||
|
||||
BOOT: 0,
|
||||
INIT: 1,
|
||||
PRELOAD: 2,
|
||||
CREATE: 3,
|
||||
UPDATE: 4,
|
||||
RENDER: 5,
|
||||
SHUTDOWN: 6
|
||||
|
||||
};
|
||||
|
||||
module.exports = STATE_CONST;
|
41
v3/src/state/Settings.js
Normal file
41
v3/src/state/Settings.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
var CONST = require('../CONST');
|
||||
var STATE_CONST = require('./STATE_CONST');
|
||||
var GetObjectValue = require('../utils/GetObjectValue');
|
||||
|
||||
var Settings = function (state, config)
|
||||
{
|
||||
if (typeof config === 'string')
|
||||
{
|
||||
config = { key: config };
|
||||
}
|
||||
else if (config === undefined)
|
||||
{
|
||||
// Pass the 'hasOwnProperty' checks
|
||||
config = {};
|
||||
}
|
||||
|
||||
this.state = state; // Do we actually need this reference? This could just be a property bucket
|
||||
|
||||
this.status = STATE_CONST.PENDING;
|
||||
|
||||
// Which part of this State is currently being processed?
|
||||
// preload, create, update, shutdown, etc
|
||||
this.op = STATE_CONST.BOOT;
|
||||
|
||||
this.key = GetObjectValue(config, 'key', '');
|
||||
this.active = GetObjectValue(config, 'active', false);
|
||||
this.visible = GetObjectValue(config, 'visible', true);
|
||||
this.scaleMode = GetObjectValue(config, 'scaleMode', CONST.scaleModes.DEFAULT);
|
||||
this.fps = GetObjectValue(config, 'fps', 60);
|
||||
this.x = GetObjectValue(config, 'x', 0);
|
||||
this.y = GetObjectValue(config, 'y', 0);
|
||||
|
||||
// -1 means the State Manager will set it to be the Game dimensions
|
||||
this.width = GetObjectValue(config, 'width', -1);
|
||||
this.height = GetObjectValue(config, 'height', -1);
|
||||
};
|
||||
|
||||
// Unless we add some actual functions in here, we'll make this just return an Object instead of an instance
|
||||
Settings.prototype.constructor = Settings;
|
||||
|
||||
module.exports = Settings;
|
56
v3/src/state/State.js
Normal file
56
v3/src/state/State.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* @author Richard Davey <rich@photonstorm.com>
|
||||
* @copyright 2016 Photon Storm Ltd.
|
||||
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
||||
*/
|
||||
|
||||
var Settings = require('./Settings');
|
||||
var Systems = require('./Systems');
|
||||
|
||||
/**
|
||||
* A Base State Class.
|
||||
*
|
||||
* @class Phaser.State
|
||||
* @constructor
|
||||
*/
|
||||
var State = function (config)
|
||||
{
|
||||
// The properties a State *must* have, that cannot be changed without breaking it:
|
||||
|
||||
this.game = null;
|
||||
|
||||
this.settings = new Settings(this, config);
|
||||
|
||||
this.sys = new Systems(this, config);
|
||||
|
||||
// Reference to sys.children, set during sys.init only
|
||||
this.children;
|
||||
};
|
||||
|
||||
State.prototype.constructor = State;
|
||||
|
||||
State.prototype = {
|
||||
|
||||
// Can be overridden by your own States
|
||||
preUpdate: function ()
|
||||
{
|
||||
},
|
||||
|
||||
// Can be overridden by your own States
|
||||
update: function ()
|
||||
{
|
||||
},
|
||||
|
||||
// Can be overridden by your own States
|
||||
postUpdate: function ()
|
||||
{
|
||||
},
|
||||
|
||||
// Can be overridden by your own States
|
||||
render: function ()
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = State;
|
574
v3/src/state/StateManager.js
Normal file
574
v3/src/state/StateManager.js
Normal file
|
@ -0,0 +1,574 @@
|
|||
/**
|
||||
* @author Richard Davey <rich@photonstorm.com>
|
||||
* @copyright 2016 Photon Storm Ltd.
|
||||
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
||||
*/
|
||||
|
||||
var CONST = require('../const');
|
||||
var NOOP = require('../utils/NOOP');
|
||||
|
||||
/**
|
||||
* The State Manager is responsible for loading, setting up and switching game states.
|
||||
*
|
||||
* @class Phaser.StateManager
|
||||
* @constructor
|
||||
* @param {Phaser.Game} game - A reference to the currently running game.
|
||||
*/
|
||||
var StateManager = function (game, stateConfig)
|
||||
{
|
||||
this.game = game;
|
||||
|
||||
// Everything kept in here
|
||||
this.keys = {};
|
||||
this.states = [];
|
||||
|
||||
// Only active states are kept in here
|
||||
this.active = [];
|
||||
|
||||
this._pending = [];
|
||||
|
||||
if (stateConfig)
|
||||
{
|
||||
if (Array.isArray(stateConfig))
|
||||
{
|
||||
for (var i = 0; i < stateConfig.length; i++)
|
||||
{
|
||||
// The i === 0 part just starts the first State given
|
||||
this._pending.push({ index: i, key: 'default', state: stateConfig[i], autoStart: (i === 0) });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this._pending.push({ index: 0, key: 'default', state: stateConfig, autoStart: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
StateManager.prototype.constructor = StateManager;
|
||||
|
||||
StateManager.prototype = {
|
||||
|
||||
/**
|
||||
* The Boot handler is called by Phaser.Game when it first starts up.
|
||||
* The renderer is available by now.
|
||||
*
|
||||
* @method Phaser.StateManager#boot
|
||||
* @private
|
||||
*/
|
||||
boot: function ()
|
||||
{
|
||||
// this.game.onPause.add(this.pause, this);
|
||||
// this.game.onResume.add(this.resume, this);
|
||||
|
||||
console.log('StateManager.boot');
|
||||
|
||||
for (var i = 0; i < this._pending.length; i++)
|
||||
{
|
||||
var entry = this._pending[i];
|
||||
|
||||
this.add(entry.key, entry.state, entry.autoStart);
|
||||
}
|
||||
|
||||
// Clear the pending list
|
||||
this._pending = [];
|
||||
},
|
||||
|
||||
getKey: function (key, stateConfig)
|
||||
{
|
||||
if (!key) { key = 'default'; }
|
||||
|
||||
if (stateConfig instanceof Phaser.State)
|
||||
{
|
||||
key = stateConfig.settings.key;
|
||||
}
|
||||
else if (typeof stateConfig === 'object' && stateConfig.hasOwnProperty('key'))
|
||||
{
|
||||
key = stateConfig.key;
|
||||
}
|
||||
|
||||
// By this point it's either 'default' or extracted from the State
|
||||
|
||||
if (this.keys.hasOwnProperty(key))
|
||||
{
|
||||
throw new Error('Cannot add a State with duplicate key: ' + key);
|
||||
}
|
||||
else
|
||||
{
|
||||
return key;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new State into the StateManager. You must give each State a unique key by which you'll identify it.
|
||||
* The State can be either a Phaser.State object (or an object that extends it), a plain JavaScript object or a function.
|
||||
* If a function is given a new state object will be created by calling it.
|
||||
*
|
||||
* @method Phaser.StateManager#add
|
||||
* @param {string} key - A unique key you use to reference this state, i.e. "MainMenu", "Level1".
|
||||
* @param {Phaser.State|object|function} state - The state you want to switch to.
|
||||
* @param {boolean} [autoStart=false] - If true the State will be started immediately after adding it.
|
||||
*/
|
||||
add: function (key, stateConfig, autoStart)
|
||||
{
|
||||
if (autoStart === undefined) { autoStart = false; }
|
||||
|
||||
// if not booted, then put state into a holding pattern
|
||||
if (!this.game.isBooted)
|
||||
{
|
||||
this._pending.push({
|
||||
index: this._pending.length,
|
||||
key: key,
|
||||
state: stateConfig,
|
||||
autoStart: autoStart
|
||||
});
|
||||
|
||||
console.log('StateManager not yet booted, adding to list', this._pending.length);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
key = this.getKey(key, stateConfig);
|
||||
|
||||
var newState;
|
||||
|
||||
if (stateConfig instanceof Phaser.State)
|
||||
{
|
||||
console.log('StateManager.add from instance', key);
|
||||
newState = this.createStateFromInstance(key, stateConfig);
|
||||
}
|
||||
else if (typeof stateConfig === 'object')
|
||||
{
|
||||
console.log('StateManager.add from object', key);
|
||||
|
||||
stateConfig.key = key;
|
||||
|
||||
newState = this.createStateFromObject(key, stateConfig);
|
||||
}
|
||||
else if (typeof stateConfig === 'function')
|
||||
{
|
||||
console.log('StateManager.add from function', key);
|
||||
|
||||
newState = this.createStateFromFunction(key, stateConfig);
|
||||
}
|
||||
|
||||
this.keys[key] = newState;
|
||||
|
||||
this.states.push(newState);
|
||||
|
||||
// window.console.dir(newState);
|
||||
|
||||
if (autoStart || newState.settings.active)
|
||||
{
|
||||
if (this.game.isBooted)
|
||||
{
|
||||
this.start(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._start.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return newState;
|
||||
},
|
||||
|
||||
createStateFromInstance: function (key, newState)
|
||||
{
|
||||
newState.game = this.game;
|
||||
|
||||
newState.settings.key = key;
|
||||
|
||||
newState.sys.init();
|
||||
|
||||
if (this.game.config.renderType === CONST.WEBGL)
|
||||
{
|
||||
this.createStateFrameBuffer(newState);
|
||||
}
|
||||
|
||||
return newState;
|
||||
},
|
||||
|
||||
createStateFromObject: function (key, stateConfig)
|
||||
{
|
||||
var newState = new Phaser.State(stateConfig);
|
||||
|
||||
newState.game = this.game;
|
||||
|
||||
newState.sys.init();
|
||||
|
||||
if (this.game.config.renderType === CONST.WEBGL)
|
||||
{
|
||||
this.createStateFrameBuffer(newState);
|
||||
}
|
||||
|
||||
// Extract callbacks or set NOOP
|
||||
|
||||
if (stateConfig.hasOwnProperty('init'))
|
||||
{
|
||||
newState.init = stateConfig.init;
|
||||
}
|
||||
|
||||
if (stateConfig.hasOwnProperty('preload'))
|
||||
{
|
||||
newState.preload = stateConfig.preload;
|
||||
}
|
||||
|
||||
if (stateConfig.hasOwnProperty('create'))
|
||||
{
|
||||
newState.create = stateConfig.create;
|
||||
}
|
||||
|
||||
if (stateConfig.hasOwnProperty('shutdown'))
|
||||
{
|
||||
newState.shutdown = stateConfig.shutdown;
|
||||
}
|
||||
|
||||
newState.preUpdate = (stateConfig.hasOwnProperty('preUpdate')) ? stateConfig.preUpdate : NOOP;
|
||||
newState.update = (stateConfig.hasOwnProperty('update')) ? stateConfig.update : NOOP;
|
||||
newState.postUpdate = (stateConfig.hasOwnProperty('postUpdate')) ? stateConfig.postUpdate : NOOP;
|
||||
newState.render = (stateConfig.hasOwnProperty('render')) ? stateConfig.render : NOOP;
|
||||
|
||||
return newState;
|
||||
},
|
||||
|
||||
createStateFromFunction: function (key, state)
|
||||
{
|
||||
var newState = new state();
|
||||
|
||||
if (newState instanceof Phaser.State)
|
||||
{
|
||||
return this.createStateFromInstance(key, newState);
|
||||
}
|
||||
else
|
||||
{
|
||||
newState.game = this.game;
|
||||
|
||||
newState.settings = new Phaser.State.Settings(newState, key);
|
||||
|
||||
newState.sys = new Phaser.State.Systems(newState);
|
||||
|
||||
newState.sys.init();
|
||||
|
||||
if (this.game.config.renderType === CONST.WEBGL)
|
||||
{
|
||||
this.createStateFrameBuffer(newState);
|
||||
}
|
||||
|
||||
// Default required functions
|
||||
|
||||
if (!newState.preUpdate)
|
||||
{
|
||||
newState.preUpdate = NOOP;
|
||||
}
|
||||
|
||||
if (!newState.update)
|
||||
{
|
||||
newState.update = NOOP;
|
||||
}
|
||||
|
||||
if (!newState.postUpdate)
|
||||
{
|
||||
newState.postUpdate = NOOP;
|
||||
}
|
||||
|
||||
if (!newState.render)
|
||||
{
|
||||
newState.render = NOOP;
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
},
|
||||
|
||||
createStateFrameBuffer: function (newState)
|
||||
{
|
||||
var x = newState.settings.x;
|
||||
var y = newState.settings.y;
|
||||
|
||||
if (newState.settings.width === -1)
|
||||
{
|
||||
newState.settings.width = this.game.width;
|
||||
}
|
||||
|
||||
if (newState.settings.height === -1)
|
||||
{
|
||||
newState.settings.height = this.game.height;
|
||||
}
|
||||
|
||||
var width = newState.settings.width;
|
||||
var height = newState.settings.height;
|
||||
|
||||
newState.sys.fbo = this.game.renderer.createFBO(newState, x, y, width, height);
|
||||
},
|
||||
|
||||
getState: function (key)
|
||||
{
|
||||
return this.keys[key];
|
||||
},
|
||||
|
||||
getStateIndex: function (state)
|
||||
{
|
||||
return this.states.indexOf(state);
|
||||
},
|
||||
|
||||
getActiveStateIndex: function (state)
|
||||
{
|
||||
for (var i = 0; i < this.active.length; i++)
|
||||
{
|
||||
if (this.active[i].state === state)
|
||||
{
|
||||
return this.active[i].index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
|
||||
isActive: function (key)
|
||||
{
|
||||
var state = this.getState(key);
|
||||
|
||||
return (state && state.settings.active && this.active.indexOf(state) !== -1);
|
||||
},
|
||||
|
||||
start: function (key)
|
||||
{
|
||||
// if not booted, then put state into a holding pattern
|
||||
if (!this.game.isBooted)
|
||||
{
|
||||
console.log('StateManager not yet booted, setting autoStart on pending list');
|
||||
|
||||
for (var i = 0; i < this._pending.length; i++)
|
||||
{
|
||||
var entry = this._pending[i];
|
||||
|
||||
if (entry.key === key)
|
||||
{
|
||||
entry.autoStart = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var state = this.getState(key);
|
||||
|
||||
if (state)
|
||||
{
|
||||
// Already started? Nothing more to do here ...
|
||||
if (this.isActive(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
state.settings.active = true;
|
||||
|
||||
// + arguments
|
||||
if (state.init)
|
||||
{
|
||||
state.init.call(state);
|
||||
}
|
||||
|
||||
if (state.preload)
|
||||
{
|
||||
state.sys.load.reset(true);
|
||||
|
||||
state.preload.call(state, this.game);
|
||||
|
||||
// Is the loader empty?
|
||||
if (state.sys.load.totalQueuedFiles() === 0 && state.sys.load.totalQueuedPacks() === 0)
|
||||
{
|
||||
// console.log('empty queue');
|
||||
this.startCreate(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
// console.log('load start');
|
||||
|
||||
// Start the loader going as we have something in the queue
|
||||
// state.load.onLoadComplete.addOnce(this.loadComplete, this, 0, state);
|
||||
|
||||
state.sys.load.start();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// console.log('no preload');
|
||||
|
||||
// No preload? Then there was nothing to load either
|
||||
this.startCreate(state);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
loadComplete: function (state)
|
||||
{
|
||||
// console.log('loadComplete');
|
||||
|
||||
// Make sure to do load-update one last time before state is set to _created
|
||||
|
||||
if (state.hasOwnProperty('loadUpdate'))
|
||||
{
|
||||
state.loadUpdate.call(state);
|
||||
}
|
||||
|
||||
this.startCreate(state);
|
||||
},
|
||||
|
||||
startCreate: function (state)
|
||||
{
|
||||
if (state.create)
|
||||
{
|
||||
state.create.call(state);
|
||||
}
|
||||
|
||||
// Insert at the correct index, or it just all goes wrong :)
|
||||
|
||||
var i = this.getStateIndex(state);
|
||||
|
||||
this.active.push({ index: i, state: state });
|
||||
|
||||
// Sort the 'active' array based on the index property
|
||||
this.active.sort(this.sortStates.bind(this));
|
||||
|
||||
state.sys.updates.running = true;
|
||||
|
||||
state.sys.mainloop.start();
|
||||
},
|
||||
|
||||
pause: function (key)
|
||||
{
|
||||
var index = this.getActiveStateIndex(key);
|
||||
|
||||
if (index > -1)
|
||||
{
|
||||
var state = this.getState(key);
|
||||
|
||||
state.settings.active = false;
|
||||
|
||||
this.active.splice(index, 1);
|
||||
|
||||
this.active.sort(this.sortStates.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
sortStates: function (stateA, stateB)
|
||||
{
|
||||
// Sort descending
|
||||
if (stateA.index < stateB.index)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (stateA.index > stateB.index)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
// See if we can reduce this down to just update and render
|
||||
|
||||
step: function (timestamp)
|
||||
{
|
||||
for (var i = 0; i < this.active.length; i++)
|
||||
{
|
||||
var state = this.active[i].state;
|
||||
|
||||
if (state.sys.mainloop.running)
|
||||
{
|
||||
state.sys.mainloop.step(timestamp);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
preUpdate: function ()
|
||||
{
|
||||
for (var i = 0; i < this.active.length; i++)
|
||||
{
|
||||
var state = this.active[i].state;
|
||||
|
||||
for (var c = 0; c < state.sys.children.list.length; c++)
|
||||
{
|
||||
state.sys.children.list[c].preUpdate();
|
||||
}
|
||||
|
||||
state.preUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
update: function ()
|
||||
{
|
||||
for (var i = 0; i < this.active.length; i++)
|
||||
{
|
||||
var state = this.active[i].state;
|
||||
|
||||
// Invoke State Main Loop here - updating all of its systems (tweens, physics, etc)
|
||||
|
||||
// This shouldn't be called if the State is still loading
|
||||
// Have a State.STATUS const in the Settings, dictating what is going on
|
||||
|
||||
for (var c = 0; c < state.sys.children.list.length; c++)
|
||||
{
|
||||
var child = state.sys.children.list[c];
|
||||
|
||||
if (child.exists)
|
||||
{
|
||||
child.update();
|
||||
}
|
||||
}
|
||||
|
||||
state.update();
|
||||
}
|
||||
},
|
||||
|
||||
postUpdate: function ()
|
||||
{
|
||||
for (var i = 0; i < this.active.length; i++)
|
||||
{
|
||||
var state = this.active[i].state;
|
||||
|
||||
for (var c = 0; c < state.sys.children.list.length; c++)
|
||||
{
|
||||
state.sys.children.list[c].postUpdate();
|
||||
}
|
||||
|
||||
state.postUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
render: function ()
|
||||
{
|
||||
for (var i = 0; i < this.active.length; i++)
|
||||
{
|
||||
var state = this.active[i].state;
|
||||
|
||||
// Can put all kinds of other checks in here, like MainLoop, FPS, etc.
|
||||
if (!state.settings.visible || state.sys.color.alpha === 0 || state.sys.children.list.length === 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.game.renderer.render(state);
|
||||
}
|
||||
},
|
||||
*/
|
||||
|
||||
renderChildren: function (renderer, state, interpolationPercentage)
|
||||
{
|
||||
// Populates the display list
|
||||
for (var c = 0; c < state.sys.children.list.length; c++)
|
||||
{
|
||||
var child = state.sys.children.list[c];
|
||||
|
||||
child.render(renderer, child, interpolationPercentage);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = StateManager;
|
Loading…
Add table
Reference in a new issue