phaser/src/gameobjects/creature/Creature.js

482 lines
14 KiB
JavaScript
Raw Normal View History

/**
* @author Richard Davey <rich@photonstorm.com>
* @author Kestrel Moon Studios <creature@kestrelmoon.com>
2016-04-04 21:15:01 +00:00
* @copyright 2016 Photon Storm Ltd and Kestrel Moon Studios
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Creature is a custom Game Object used in conjunction with the Creature Runtime libraries by Kestrel Moon Studios.
*
* It allows you to display animated Game Objects that were created with the [Creature Automated Animation Tool](http://www.kestrelmoon.com/creature/).
*
* Note 1: You can only use Phaser.GameObjects.Creature objects in WebGL enabled games. They do not work in Canvas mode games.
*
* Note 2: You must use a build of Phaser that includes the CreatureMeshBone.js runtime and gl-matrix.js, or have them
* loaded before your Phaser game boots.
2015-07-22 14:31:30 +00:00
*
* See the Phaser custom build process for more details.
*
* By default the Creature runtimes are NOT included in any pre-configured version of Phaser.
*
2015-07-22 14:31:30 +00:00
* So you'll need to do `grunt custom` to create a build that includes them.
*
* @class Phaser.GameObjects.Creature
* @extends PIXI.DisplayObjectContainer
* @extends Phaser.Component.Core
* @extends Phaser.Component.Angle
* @extends Phaser.Component.AutoCull
* @extends Phaser.Component.BringToTop
* @extends Phaser.Component.Destroy
* @extends Phaser.Component.FixedToCamera
* @extends Phaser.Component.LifeSpan
* @extends Phaser.Component.Reset
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {number} x - The x coordinate of the Game Object. The coordinate is relative to any parent container this Game Object may be in.
* @param {number} y - The y coordinate of the Game Object. The coordinate is relative to any parent container this Game Object may be in.
* @param {string|PIXI.Texture} key - The texture used by the Creature Object during rendering. It can be a string which is a reference to the Cache entry, or an instance of a PIXI.Texture.
* @param {string} mesh - The mesh data for the Creature Object. It should be a string which is a reference to the Cache JSON entry.
* @param {string} [animation='default'] - The animation within the mesh data to play.
*/
Phaser.GameObjects.Creature = function (game, x, y, key, mesh, animation) {
if (animation === undefined) { animation = 'default'; }
/**
* @property {number} type - The const type of this object.
* @readonly
*/
this.type = Phaser.CREATURE;
if (!game.cache.checkJSONKey(mesh))
{
console.warn('Phaser.GameObjects.Creature: Invalid mesh key given. Not found in Phaser.Cache');
return;
}
var meshData = game.cache.getJSON(mesh);
/**
* @property {Creature} _creature - The Creature instance.
* @private
*/
this._creature = new Creature(meshData);
/**
* @property {CreatureAnimation} animation - The CreatureAnimation instance.
*/
this.animation = new CreatureAnimation(meshData, animation, this._creature);
/**
* @property {CreatureManager} manager - The CreatureManager instance for this object.
*/
this.manager = new CreatureManager(this._creature);
/**
* @property {number} timeDelta - How quickly the animation advances.
* @default
*/
this.timeDelta = 0.05;
if (typeof key === 'string')
{
* PIXI.Texture.fromImage, PIXI.BaseTexture.fromImage and PIXI.Sprite.fromImage have all been removed. They should never have actually been used, as they bypass the Phaser Loader, and don't factor in CORs or any other advanced loader settings. * The PIXI.BaseTexture.imageUrl property has been removed, as it was never actually populated. * The PIXI.BaseTexture._UID property has been removed, as it was never actually used internally. * All references to PIXI.BaseTextureCache have been removed (primarily from BaseTexture.destroy and Texture.destroy), as the BaseTextureCache was never used internally by Phaser, or by our custom version of Pixi. * PIXI.TextureCache has been removed. It was only ever used by the __default and __missing images that Phaser generates on start-up. It wasn't used internally by Phaser anywhere else, and the only references Pixi has to it have all been removed. If you need it in your own game, please refactor it to avoid it, or re-create the object on the PIXI global object. * Canvases created by `BaseTexture.fromCanvas` no longer have the `_pixiId` property attached to them, as this was never used internally by Phaser or Pixi. * PIXI.BaseTexture.updateSourceImage is now deprecated. Please use `Sprite.loadTexture` instead. * The property PIXI.BaseTextureCacheIdGenerator has been removed, as it is no longer used internally by Phaser or Pixi. * PIXI.Texture.addTextureToCache has been removed. The PIXI Texture Cache was never actually used by Phaser, and was leading to complications internally. * PIXI.Texture.removeTextureFromCache has been removed. The PIXI Texture Cache was never actually used by Phaser, and was leading to complications internally. * PIXI.Texture.fromFrame and PIXI.Sprite.fromFrame have been removed. They relied on the PIXI Texture Cache, which was never actually used by Phaser, and was never used internally by Pixi either. * The property PIXI.TextureCacheIdGenerator has been removed, as it was not used internally. * The property PIXI.FrameCache has been removed, as it was not used internally.
2016-07-06 20:47:27 +00:00
var texture = new PIXI.Texture(game.cache.getBaseTexture(key));
}
else
{
var texture = key;
}
/**
* @property {PIXI.Texture} texture - The texture the animation is using.
*/
this.texture = texture;
PIXI.DisplayObjectContainer.call(this);
this.dirty = true;
this.blendMode = Phaser.blendModes.NORMAL;
/**
* @property {Phaser.Point} creatureBoundsMin - The minimum bounds point.
* @protected
*/
this.creatureBoundsMin = new Phaser.Point();
/**
* @property {Phaser.Point} creatureBoundsMax - The maximum bounds point.
* @protected
*/
this.creatureBoundsMax = new Phaser.Point();
var target = this.manager.target_creature;
/**
* @property {Float32Array} vertices - The vertices data.
* @protected
*/
this.vertices = new Float32Array(target.total_num_pts * 2);
/**
* @property {Float32Array} uvs - The UV data.
* @protected
*/
this.uvs = new Float32Array(target.total_num_pts * 2);
/**
* @property {Uint16Array} indices
* @protected
*/
this.indices = new Uint16Array(target.global_indices.length);
for (var i = 0; i < this.indices.length; i++)
{
this.indices[i] = target.global_indices[i];
}
/**
* @property {Uint16Array} colors - The vertices colors
* @protected
*/
this.colors = new Float32Array([1, 1, 1, 1]);
this.updateRenderData(target.global_pts, target.global_uvs);
this.manager.AddAnimation(this.animation);
this.manager.SetActiveAnimationName(animation, false);
Phaser.Component.Core.init.call(this, game, x, y);
};
Phaser.GameObjects.Creature.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
Phaser.GameObjects.Creature.prototype.constructor = Phaser.GameObjects.Creature;
Phaser.Component.Core.install.call(Phaser.GameObjects.Creature.prototype, [
'Angle',
'AutoCull',
'BringToTop',
'Destroy',
'FixedToCamera',
'LifeSpan',
'Reset'
]);
Phaser.GameObjects.Creature.prototype.preUpdateInWorld = Phaser.Component.InWorld.preUpdate;
Phaser.GameObjects.Creature.prototype.preUpdateCore = Phaser.Component.Core.preUpdate;
/**
* Automatically called by World.preUpdate.
*
* @method Phaser.GameObjects.Creature#preUpdate
* @memberof Phaser.GameObjects.Creature
*/
Phaser.GameObjects.Creature.prototype.preUpdate = function () {
if (!this.preUpdateInWorld())
{
return false;
}
this.manager.Update(this.timeDelta);
this.updateData();
return this.preUpdateCore();
};
/**
*
*
* @method Phaser.GameObjects.Creature#_initWebGL
* @memberof Phaser.GameObjects.Creature
* @private
*/
Phaser.GameObjects.Creature.prototype._initWebGL = function (renderSession) {
// build the strip!
var gl = renderSession.gl;
this._vertexBuffer = gl.createBuffer();
this._indexBuffer = gl.createBuffer();
this._uvBuffer = gl.createBuffer();
this._colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
};
/**
* @method Phaser.GameObjects.Creature#_renderWebGL
* @memberof Phaser.GameObjects.Creature
* @private
*/
Phaser.GameObjects.Creature.prototype._renderWebGL = function (renderSession) {
// If the sprite is not visible or the alpha is 0 then no need to render this element
if (!this.visible || this.alpha <= 0)
{
return;
}
renderSession.spriteBatch.stop();
// init! init!
if (!this._vertexBuffer)
{
this._initWebGL(renderSession);
}
renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader);
this._renderCreature(renderSession);
renderSession.spriteBatch.start();
};
/**
* @method Phaser.GameObjects.Creature#_renderCreature
* @memberof Phaser.GameObjects.Creature
* @private
*/
Phaser.GameObjects.Creature.prototype._renderCreature = function (renderSession) {
var gl = renderSession.gl;
var projection = renderSession.projection;
var offset = renderSession.offset;
var shader = renderSession.shaderManager.stripShader;
renderSession.blendModeManager.setBlendMode(this.blendMode);
// Set uniforms
gl.uniformMatrix3fv(shader.translationMatrix, false, this.worldTransform.toArray(true));
gl.uniform2f(shader.projectionVector, projection.x, -projection.y);
gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);
gl.uniform1f(shader.alpha, this.worldAlpha);
if (!this.dirty)
{
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
// Update the uvs
gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
// Check if a texture is dirty..
if (this.texture.baseTexture._dirty[gl.id])
{
renderSession.renderer.updateTexture(this.texture.baseTexture);
}
else
{
// Bind the current texture
gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]);
}
// Don't need to upload!
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
}
else
{
this.dirty = false;
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
// Update the uvs
gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
// Check if a texture is dirty
if (this.texture.baseTexture._dirty[gl.id])
{
renderSession.renderer.updateTexture(this.texture.baseTexture);
}
else
{
gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]);
}
// Don't need to upload!
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
}
gl.drawElements(gl.TRIANGLES, this.indices.length, gl.UNSIGNED_SHORT, 0);
};
/**
* @method Phaser.GameObjects.Creature#updateCreatureBounds
* @memberof Phaser.GameObjects.Creature
* @private
*/
Phaser.GameObjects.Creature.prototype.updateCreatureBounds = function () {
// Update bounds based off world transform matrix
var target = this.manager.target_creature;
target.ComputeBoundaryMinMax();
this.creatureBoundsMin.set(target.boundary_min[0], -target.boundary_min[1]);
this.creatureBoundsMax.set(target.boundary_max[0], -target.boundary_max[1]);
this.worldTransform.apply(this.creatureBoundsMin, this.creatureBoundsMin);
this.worldTransform.apply(this.creatureBoundsMax, this.creatureBoundsMax);
};
/**
* @method Phaser.GameObjects.Creature#updateData
* @memberof Phaser.GameObjects.Creature
* @private
*/
Phaser.GameObjects.Creature.prototype.updateData = function () {
var target = this.manager.target_creature;
var read_pts = target.render_pts;
var read_uvs = target.global_uvs;
this.updateRenderData(read_pts, read_uvs);
this.updateCreatureBounds();
this.dirty = true;
};
/**
* @method Phaser.GameObjects.Creature#updateRenderData
* @memberof Phaser.GameObjects.Creature
* @private
*/
Phaser.GameObjects.Creature.prototype.updateRenderData = function (verts, uvs) {
var target = this.manager.target_creature;
var pt_index = 0;
var uv_index = 0;
var write_pt_index = 0;
for (var i = 0; i < target.total_num_pts; i++)
{
this.vertices[write_pt_index] = verts[pt_index];
this.vertices[write_pt_index + 1] = -verts[pt_index + 1];
this.uvs[uv_index] = uvs[uv_index];
this.uvs[uv_index + 1] = uvs[uv_index + 1];
pt_index += 3;
uv_index += 2;
write_pt_index += 2;
}
};
/**
* Sets the Animation this Creature object will play, as defined in the mesh data.
*
* @method Phaser.GameObjects.Creature#setAnimation
* @memberof Phaser.GameObjects.Creature
* @param {string} key - The key of the animation to set, as defined in the mesh data.
*/
Phaser.GameObjects.Creature.prototype.setAnimation = function (key) {
this.manager.SetActiveAnimationName(key, true);
};
/**
* Plays the currently set animation.
*
* @method Phaser.GameObjects.Creature#play
* @memberof Phaser.GameObjects.Creature
* @param {boolean} [loop=false] - Should the animation loop?
*/
Phaser.GameObjects.Creature.prototype.play = function (loop) {
if (loop === undefined) { loop = false; }
this.loop = loop;
this.manager.SetIsPlaying(true);
this.manager.RunAtTime(0);
};
/**
* Stops the currently playing animation.
*
* @method Phaser.GameObjects.Creature#stop
* @memberof Phaser.GameObjects.Creature
*/
Phaser.GameObjects.Creature.prototype.stop = function () {
this.manager.SetIsPlaying(false);
};
/**
* @name Phaser.GameObjects.Creature#isPlaying
* @property {boolean} isPlaying - Is the _current_ animation playing?
*/
Object.defineProperty(Phaser.GameObjects.Creature.prototype, 'isPlaying', {
get: function() {
return this.manager.GetIsPlaying();
},
set: function(value) {
this.manager.SetIsPlaying(value);
}
});
/**
* @name Phaser.GameObjects.Creature#loop
* @property {boolean} loop - Should the _current_ animation loop or not?
*/
Object.defineProperty(Phaser.GameObjects.Creature.prototype, 'loop', {
get: function() {
return this.manager.should_loop;
},
set: function(value) {
this.manager.SetShouldLoop(value);
}
});