Added support for the [Creature Automated Animation Tool](http://www.kestrelmoon.com/creature/). You can now create a Phaser.Creature object which uses json data and a texture atlas for the animations. Creature is a powerful animation tool, similar to Spriter or Spine. It is currently limited to WebGL games only, but the new libs should prove a solid starting point for anyone wanting to incorporate Creature animations into their games.

This commit is contained in:
photonstorm 2015-04-13 23:16:29 +01:00
parent 0ffa499bd0
commit 8f06991527
9 changed files with 7386 additions and 6 deletions

View file

@ -62,6 +62,7 @@ module.exports = function (grunt) {
'p2': { 'description': 'P2 Physics', 'optional': true, 'stub': false },
'tilemaps': { 'description': 'Tilemap Support', 'optional': true, 'stub': false },
'particles': { 'description': 'Arcade Physics Particle System', 'optional': true, 'stub': true },
'creature': { 'description': 'Creature Animation Tool Support', 'optional': true, 'stub': false },
'outro': { 'description': 'Phaser UMD closure', 'optional': true, 'stub': false }
};
@ -207,7 +208,7 @@ module.exports = function (grunt) {
grunt.registerTask('build', 'Compile all Phaser versions just to the dist folder', function() {
grunt.option('exclude', 'ninja');
grunt.option('exclude', 'ninja,creature');
grunt.option('filename', 'phaser');
grunt.option('sourcemap', true);
grunt.option('copy', false);
@ -218,7 +219,7 @@ module.exports = function (grunt) {
grunt.registerTask('full', 'Phaser complete', function() {
grunt.option('exclude', 'ninja');
grunt.option('exclude', 'ninja,creature');
grunt.option('filename', 'phaser');
grunt.option('sourcemap', true);
grunt.option('copy', true);
@ -229,7 +230,7 @@ module.exports = function (grunt) {
grunt.registerTask('arcadephysics', 'Phaser with Arcade Physics, Tilemaps and Particles', function() {
grunt.option('exclude', 'ninja,p2');
grunt.option('exclude', 'ninja,p2,creature');
grunt.option('filename', 'phaser-arcade-physics');
grunt.option('sourcemap', true);
grunt.option('copy', false);
@ -241,7 +242,7 @@ module.exports = function (grunt) {
grunt.registerTask('nophysics', 'Phaser without physics, tilemaps or particles', function() {
grunt.option('exclude', 'arcade,ninja,p2,tilemaps,particles');
grunt.option('exclude', 'arcade,ninja,p2,tilemaps,particles,creature');
grunt.option('filename', 'phaser-no-physics');
grunt.option('sourcemap', true);
grunt.option('copy', false);
@ -253,7 +254,7 @@ module.exports = function (grunt) {
grunt.registerTask('minimum', 'Phaser without any optional modules except Pixi', function() {
grunt.option('exclude', 'gamepad,keyboard,bitmapdata,graphics,rendertexture,text,bitmaptext,retrofont,net,tweens,sound,debug,arcade,ninja,p2,tilemaps,particles');
grunt.option('exclude', 'gamepad,keyboard,bitmapdata,graphics,rendertexture,text,bitmaptext,retrofont,net,tweens,sound,debug,arcade,ninja,p2,tilemaps,particles,creature');
grunt.option('filename', 'phaser-minimum');
grunt.option('sourcemap', true);
grunt.option('copy', false);

View file

@ -246,6 +246,7 @@ Version 2.3.1 - "Katar" - in dev
### New Features
* Added support for the [Creature Automated Animation Tool](http://www.kestrelmoon.com/creature/). You can now create a Phaser.Creature object which uses json data and a texture atlas for the animations. Creature is a powerful animation tool, similar to Spriter or Spine. It is currently limited to WebGL games only, but the new libs should prove a solid starting point for anyone wanting to incorporate Creature animations into their games.
* Tilemap.getTileWorldXY has a new optional parameter: `nonNull` which if set makes it behave in the same way as `getTile` does (thanks @GGAlanSmithee #1722)
* Group.hash is an array (previously available as `Group._hash`, but protected) into which you can add any of its children via `Group.addToHash` and `Group.removeFromHash`. Only children of the Group can be added to and removed from the hash. The hash is used automatically by Arcade Physics in order to perform non z-index based destructive sorting. However if you don't use Arcade Physics, or it isn't a physics enabled Group, then you can use the hash to perform your own sorting and filtering of Group children without touching their z-index (and therefore display draw order).
* Group.physicsSortDirection is a new property allowing you to set a custom sort direction for Arcade Physics Sprites within the Group hash. Previously Arcade Physics used one single sort direction (defined on `Phaser.Physics.Arcade.sortDirection`) but this change allows you to specifically control how each and every Group is sorted, so you can now combine tall and wide Groups with narrow and thin in a single system.

View file

@ -20,7 +20,8 @@
'arcade' => true,
'p2' => true,
'ninja' => false,
'box2d' => false
'box2d' => false,
'creature' => false,
);
}
@ -29,6 +30,11 @@
echo " <script src=\"$path/src/physics/p2/p2.js\"></script>";
}
if ($modules['creature'])
{
echo " <script src=\"$path/src/animation/creature/gl-matrix.js\"></script>";
}
if ($modules['box2d'] && isset($box2dpath))
{
echo " <script src=\"$box2dpath/box2d-html5.js\"></script>";
@ -288,6 +294,17 @@ EOL;
EOL;
if ($modules['creature'])
{
echo <<<EOL
<script src="$path/src/animation/creature/CreatureMeshBone.js"></script>
<script src="$path/src/animation/creature/CreaturePixiJSRenderer.js"></script>
<script src="$path/src/gameobjects/Creature.js"></script>
EOL;
}
if ($modules['sound'])
{
echo <<<EOL

View file

@ -51,6 +51,7 @@ var Phaser = Phaser || {
MATRIX: 24,
POINT: 25,
ROUNDEDRECTANGLE: 26,
CREATURE: 27,
/**
* Various blend modes supported by pixi. IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,226 @@
/******************************************************************************
* Creature Runtimes License
*
* Copyright (c) 2015, Kestrel Moon Studios
* All rights reserved.
*
* Preamble: This Agreement governs the relationship between Licensee and Kestrel Moon Studios(Hereinafter: Licensor).
* This Agreement sets the terms, rights, restrictions and obligations on using [Creature Runtimes] (hereinafter: The Software) created and owned by Licensor,
* as detailed herein:
* License Grant: Licensor hereby grants Licensee a Sublicensable, Non-assignable & non-transferable, Commercial, Royalty free,
* Including the rights to create but not distribute derivative works, Non-exclusive license, all with accordance with the terms set forth and
* other legal restrictions set forth in 3rd party software used while running Software.
* Limited: Licensee may use Software for the purpose of:
* Running Software on Licensees Website[s] and Server[s];
* Allowing 3rd Parties to run Software on Licensees Website[s] and Server[s];
* Publishing Softwares output to Licensee and 3rd Parties;
* Distribute verbatim copies of Softwares output (including compiled binaries);
* Modify Software to suit Licensees needs and specifications.
* Binary Restricted: Licensee may sublicense Software as a part of a larger work containing more than Software,
* distributed solely in Object or Binary form under a personal, non-sublicensable, limited license. Such redistribution shall be limited to unlimited codebases.
* Non Assignable & Non-Transferable: Licensee may not assign or transfer his rights and duties under this license.
* Commercial, Royalty Free: Licensee may use Software for any purpose, including paid-services, without any royalties
* Including the Right to Create Derivative Works: Licensee may create derivative works based on Software,
* including amending Softwares source code, modifying it, integrating it into a larger work or removing portions of Software,
* as long as no distribution of the derivative works is made
*
* THE RUNTIMES IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE RUNTIMES OR THE USE OR OTHER DEALINGS IN THE
* RUNTIMES.
*****************************************************************************/
function CreatureRenderer(manager_in, texture_in)
{
PIXI.DisplayObjectContainer.call( this );
this.creature_manager = manager_in;
this.texture = texture_in;
this.dirty = true;
this.blendMode = PIXI.blendModes.NORMAL;
var target_creature = this.creature_manager.target_creature;
this.verticies = new PIXI.Float32Array(target_creature.total_num_pts * 2);
this.uvs = new PIXI.Float32Array(target_creature.total_num_pts * 2);
this.indices = new PIXI.Uint16Array(target_creature.global_indices.length);
for(var i = 0; i < this.indices.length; i++)
{
this.indices[i] = target_creature.global_indices[i];
}
this.colors = new PIXI.Float32Array([1,1,1,1]);
this.UpdateRenderData(target_creature.global_pts, target_creature.global_uvs);
};
// constructor
CreatureRenderer.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
CreatureRenderer.prototype.constructor = CreatureRenderer;
CreatureRenderer.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;
// render triangles..
renderSession.spriteBatch.stop();
// init! init!
if(!this._vertexBuffer)this._initWebGL(renderSession);
renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader);
this._renderCreature(renderSession);
///renderSession.shaderManager.activateDefaultShader();
renderSession.spriteBatch.start();
//TODO check culling
};
CreatureRenderer.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.verticies, 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);
};
CreatureRenderer.prototype._renderCreature = function(renderSession)
{
var gl = renderSession.gl;
var projection = renderSession.projection,
offset = renderSession.offset,
shader = renderSession.shaderManager.stripShader;
// gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real);
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.verticies);
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]);
}
// dont 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.verticies, 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]);
}
// dont 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);
};
CreatureRenderer.prototype.UpdateData = function()
{
var target_creature = this.creature_manager.target_creature;
var read_pts = target_creature.render_pts;
//var read_pts = target_creature.global_pts;
var read_uvs = target_creature.global_uvs;
this.UpdateRenderData(read_pts, read_uvs);
this.dirty = true;
};
CreatureRenderer.prototype.UpdateRenderData = function(inputVerts, inputUVs)
{
var target_creature = this.creature_manager.target_creature;
var pt_index = 0;
var uv_index = 0;
var write_pt_index = 0;
for(var i = 0; i < target_creature.total_num_pts; i++)
{
this.verticies[write_pt_index] = inputVerts[pt_index];
this.verticies[write_pt_index + 1] = -inputVerts[pt_index + 1];
this.uvs[uv_index] = inputUVs[uv_index];
this.uvs[uv_index + 1] = 1.0 - inputUVs[uv_index + 1];
pt_index += 3;
uv_index += 2;
write_pt_index += 2;
}
};

File diff suppressed because it is too large Load diff

109
src/gameobjects/Creature.js Normal file
View file

@ -0,0 +1,109 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Kestrel Moon Studios <creature@kestrelmoon.com>
* @copyright 2015 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.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 Creature runtimes, or have them loaded before your Phaser game boots.
* See the Phaser custom build process for more details. By default the Creature runtimes are NOT included in any pre-configured version of Phaser.
* So you'll need to do `grunt custom` to specify a build that includes them.
*
* @class Phaser.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 {CreatureManager} manager - A reference to the CreatureManager instance.
* @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.
*/
Phaser.Creature = function (game, manager, x, y, key) {
x = x || 0;
y = y || 0;
key = key || null;
/**
* @property {number} type - The const type of this object.
* @readonly
*/
this.type = Phaser.CREATURE;
/**
* @property {number} timeDelta - How quickly the animation time/playback advances
*/
this.timeDelta = 0.05;
/**
* @property {CreatureManager} _manager - The CreatureManager
* @private
*/
this._manager = manager;
if (typeof key === 'string')
{
var texture = game.cache.getPixiTexture(key);
}
else
{
var texture = key;
}
CreatureRenderer.call(this, manager, texture);
Phaser.Component.Core.init.call(this, game, x, y);
};
Phaser.Creature.prototype = Object.create(CreatureRenderer.prototype);
Phaser.Creature.prototype.constructor = Phaser.Creature;
Phaser.Component.Core.install.call(Phaser.Creature.prototype, [
'Angle',
'AutoCull',
'BringToTop',
'Destroy',
'FixedToCamera',
'LifeSpan',
'Reset'
]);
Phaser.Creature.prototype.preUpdateInWorld = Phaser.Component.InWorld.preUpdate;
Phaser.Creature.prototype.preUpdateCore = Phaser.Component.Core.preUpdate;
/**
* Automatically called by World.preUpdate.
*
* @method Phaser.Creature#preUpdate
* @memberof Phaser.Creature
*/
Phaser.Creature.prototype.preUpdate = function() {
if (!this.preUpdateInWorld())
{
return false;
}
this._manager.Update(this.timeDelta);
this.UpdateData();
return this.preUpdateCore();
};

View file

@ -0,0 +1,6 @@
[
"src/animation/creature/gl-matrix.js",
"src/animation/creature/CreatureMeshBone.js",
"src/animation/creature/CreaturePixiJSRenderer.js",
"src/gameobjects/Creature.js"
]