mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-05 23:05:01 +08:00
IK constraints for spine-as3.
This commit is contained in:
parent
eafcc2829f
commit
84a80feccc
@ -34,10 +34,12 @@ public class Bone {
|
||||
static public var yDown:Boolean;
|
||||
|
||||
internal var _data:BoneData;
|
||||
internal var _skeleton:Skeleton;
|
||||
internal var _parent:Bone;
|
||||
public var x:Number;
|
||||
public var y:Number;
|
||||
public var rotation:Number;
|
||||
public var rotationIK:Number;
|
||||
public var scaleX:Number
|
||||
public var scaleY:Number;
|
||||
|
||||
@ -52,48 +54,52 @@ public class Bone {
|
||||
internal var _worldScaleY:Number;
|
||||
|
||||
/** @param parent May be null. */
|
||||
public function Bone (data:BoneData, parent:Bone) {
|
||||
if (data == null)
|
||||
throw new ArgumentError("data cannot be null.");
|
||||
public function Bone (data:BoneData, skeleton:Skeleton, parent:Bone) {
|
||||
if (data == null) throw new ArgumentError("data cannot be null.");
|
||||
if (skeleton == null) throw new ArgumentError("skeleton cannot be null.");
|
||||
_data = data;
|
||||
_skeleton = skeleton;
|
||||
_parent = parent;
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
/** Computes the world SRT using the parent bone and the local SRT. */
|
||||
public function updateWorldTransform (flipX:Boolean, flipY:Boolean) : void {
|
||||
if (_parent != null) {
|
||||
_worldX = x * _parent._m00 + y * _parent._m01 + _parent._worldX;
|
||||
_worldY = x * _parent._m10 + y * _parent._m11 + _parent._worldY;
|
||||
public function updateWorldTransform () : void {
|
||||
var parent:Bone = _parent;
|
||||
if (parent) {
|
||||
_worldX = x * parent._m00 + y * parent._m01 + parent._worldX;
|
||||
_worldY = x * parent._m10 + y * parent._m11 + parent._worldY;
|
||||
if (_data.inheritScale) {
|
||||
_worldScaleX = _parent._worldScaleX * scaleX;
|
||||
_worldScaleY = _parent._worldScaleY * scaleY;
|
||||
_worldScaleX = parent._worldScaleX * scaleX;
|
||||
_worldScaleY = parent._worldScaleY * scaleY;
|
||||
} else {
|
||||
_worldScaleX = scaleX;
|
||||
_worldScaleY = scaleY;
|
||||
}
|
||||
_worldRotation = _data.inheritRotation ? _parent._worldRotation + rotation : rotation;
|
||||
_worldRotation = _data.inheritRotation ? parent._worldRotation + rotationIK : rotationIK;
|
||||
} else {
|
||||
_worldX = flipX ? -x : x;
|
||||
_worldY = flipY != yDown ? -y : y;
|
||||
_worldX = _skeleton.flipX ? -x : x;
|
||||
_worldY = _skeleton.flipY != yDown ? -y : y;
|
||||
_worldScaleX = scaleX;
|
||||
_worldScaleY = scaleY;
|
||||
_worldRotation = rotation;
|
||||
_worldRotation = rotationIK;
|
||||
}
|
||||
var radians:Number = _worldRotation * (Math.PI / 180);
|
||||
var cos:Number = Math.cos(radians);
|
||||
var sin:Number = Math.sin(radians);
|
||||
_m00 = cos * _worldScaleX;
|
||||
_m10 = sin * _worldScaleX;
|
||||
_m01 = -sin * _worldScaleY;
|
||||
_m11 = cos * _worldScaleY;
|
||||
if (flipX) {
|
||||
_m00 = -_m00;
|
||||
_m01 = -_m01;
|
||||
if (skeleton.flipX) {
|
||||
_m00 = -cos * _worldScaleX;
|
||||
_m01 = sin * _worldScaleY;
|
||||
} else {
|
||||
_m00 = cos * _worldScaleX;
|
||||
_m01 = -sin * _worldScaleY;
|
||||
}
|
||||
if (flipY != yDown) {
|
||||
_m10 = -_m10;
|
||||
_m11 = -_m11;
|
||||
if (_skeleton.flipY != yDown) {
|
||||
_m10 = -sin * _worldScaleX;
|
||||
_m11 = -cos * _worldScaleY;
|
||||
} else {
|
||||
_m10 = sin * _worldScaleX;
|
||||
_m11 = cos * _worldScaleY;
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +107,7 @@ public class Bone {
|
||||
x = _data.x;
|
||||
y = _data.y;
|
||||
rotation = _data.rotation;
|
||||
rotationIK = rotation;
|
||||
scaleX = _data.scaleX;
|
||||
scaleY = _data.scaleY;
|
||||
}
|
||||
@ -108,10 +115,14 @@ public class Bone {
|
||||
public function get data () : BoneData {
|
||||
return _data;
|
||||
}
|
||||
|
||||
|
||||
public function get parent () : Bone {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
public function get skeleton () : Skeleton {
|
||||
return _skeleton;
|
||||
}
|
||||
|
||||
public function get m00 () : Number {
|
||||
return _m00;
|
||||
@ -149,6 +160,24 @@ public class Bone {
|
||||
return _worldScaleY;
|
||||
}
|
||||
|
||||
public function worldToLocal (world:Vector.<Number>) : void {
|
||||
var dx:Number = world[0] - _worldX, dy:Number = world[1] - _worldY;
|
||||
var m00:Number = _m00, m10:Number = _m10, m01:Number = _m01, m11:Number = _m11;
|
||||
if (_skeleton.flipX != (_skeleton.flipY != yDown)) {
|
||||
m00 = -m00;
|
||||
m11 = -m11;
|
||||
}
|
||||
var invDet:Number = 1 / (m00 * m11 - m01 * m10);
|
||||
world[0] = (dx * m00 * invDet - dy * m01 * invDet);
|
||||
world[1] = (dy * m11 * invDet - dx * m10 * invDet);
|
||||
}
|
||||
|
||||
public function localToWorld (local:Vector.<Number>) : void {
|
||||
var localX:Number = local[0], localY:Number = local[1];
|
||||
local[0] = localX * _m00 + localY * _m01 + _worldX;
|
||||
local[1] = localX * _m10 + localY * _m11 + _worldY;
|
||||
}
|
||||
|
||||
public function toString () : String {
|
||||
return _data._name;
|
||||
}
|
||||
|
||||
@ -31,8 +31,8 @@
|
||||
package spine {
|
||||
|
||||
public class BoneData {
|
||||
internal var _parent:BoneData;
|
||||
internal var _name:String;
|
||||
internal var _parent:BoneData;
|
||||
public var length:Number;
|
||||
public var x:Number;
|
||||
public var y:Number;
|
||||
@ -44,21 +44,20 @@ public class BoneData {
|
||||
|
||||
/** @param parent May be null. */
|
||||
public function BoneData (name:String, parent:BoneData) {
|
||||
if (name == null)
|
||||
throw new ArgumentError("name cannot be null.");
|
||||
if (name == null) throw new ArgumentError("name cannot be null.");
|
||||
_name = name;
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
public function get name () : String {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function get parent () : BoneData {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
public function get name () : String {
|
||||
return _name;
|
||||
}
|
||||
|
||||
public function toString () : String {
|
||||
return _name;
|
||||
}
|
||||
|
||||
@ -37,8 +37,7 @@ public class Event {
|
||||
public var stringValue:String;
|
||||
|
||||
public function Event (data:EventData) {
|
||||
if (data == null)
|
||||
throw new ArgumentError("data cannot be null.");
|
||||
if (data == null) throw new ArgumentError("data cannot be null.");
|
||||
_data = data;
|
||||
}
|
||||
|
||||
|
||||
@ -37,8 +37,7 @@ public class EventData {
|
||||
public var stringValue:String;
|
||||
|
||||
public function EventData (name:String) {
|
||||
if (name == null)
|
||||
throw new ArgumentError("name cannot be null.");
|
||||
if (name == null) throw new ArgumentError("name cannot be null.");
|
||||
_name = name;
|
||||
}
|
||||
|
||||
|
||||
149
spine-as3/spine-as3/src/spine/IkConstraint.as
Normal file
149
spine-as3/spine-as3/src/spine/IkConstraint.as
Normal file
@ -0,0 +1,149 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.1
|
||||
*
|
||||
* Copyright (c) 2013, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to install, execute and perform the Spine Runtimes
|
||||
* Software (the "Software") solely for internal use. Without the written
|
||||
* permission of Esoteric Software (typically granted by licensing Spine), you
|
||||
* may not (a) modify, translate, adapt or otherwise create derivative works,
|
||||
* improvements of the Software or develop new applications using the Software
|
||||
* or (b) remove, delete, alter or obscure any trademarks or any copyright,
|
||||
* trademark, patent or other intellectual property or proprietary rights
|
||||
* notices on or in the Software, including any copy thereof. Redistributions
|
||||
* in binary or source form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
package spine {
|
||||
|
||||
public class IkConstraint {
|
||||
static private const tempPosition:Vector.<Number> = new Vector.<Number>(2, true);
|
||||
static private const radDeg:Number = 180 / Math.PI;
|
||||
|
||||
internal var _data:IkConstraintData;
|
||||
public var bones:Vector.<Bone>;
|
||||
public var target:Bone;
|
||||
public var bendDirection:int;
|
||||
public var mix:Number;
|
||||
|
||||
public function IkConstraint (data:IkConstraintData, skeleton:Skeleton) {
|
||||
if (data == null) throw new ArgumentError("data cannot be null.");
|
||||
if (skeleton == null) throw new ArgumentError("skeleton cannot be null.");
|
||||
_data = data;
|
||||
mix = data.mix;
|
||||
bendDirection = data.bendDirection;
|
||||
|
||||
bones = new Vector.<Bone>();
|
||||
for each (var boneData:BoneData in data.bones)
|
||||
bones[bones.length] = skeleton.findBone(boneData.name);
|
||||
target = skeleton.findBone(data.target._name);
|
||||
}
|
||||
|
||||
public function apply () : void {
|
||||
switch (bones.length) {
|
||||
case 1:
|
||||
apply1(bones[0], target._worldX, target._worldY, mix);
|
||||
break;
|
||||
case 2:
|
||||
apply2(bones[0], bones[1], target._worldX, target._worldY, bendDirection, mix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function get data () : IkConstraintData {
|
||||
return _data;
|
||||
}
|
||||
|
||||
public function toString () : String {
|
||||
return _data._name;
|
||||
}
|
||||
|
||||
/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
|
||||
* coordinate system. */
|
||||
static public function apply1 (bone:Bone, targetX:Number, targetY:Number, alpha:Number) : void {
|
||||
var parentRotation:Number = (!bone._data.inheritRotation || bone._parent == null) ? 0 : bone._parent._worldRotation;
|
||||
var rotation:Number = bone.rotation;
|
||||
var rotationIK:Number = Math.atan2(targetY - bone._worldY, targetX - bone._worldX) * radDeg - parentRotation;
|
||||
bone.rotationIK = rotation + (rotationIK - rotation) * alpha;
|
||||
}
|
||||
|
||||
/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The
|
||||
* target is specified in the world coordinate system.
|
||||
* @param child Any descendant bone of the parent. */
|
||||
static public function apply2 (parent:Bone, child:Bone, targetX:Number, targetY:Number, bendDirection:int, alpha:Number) : void {
|
||||
var childRotation:Number = child.rotation, parentRotation:Number = parent.rotation;
|
||||
if (alpha == 0) {
|
||||
child.rotationIK = childRotation;
|
||||
parent.rotationIK = parentRotation;
|
||||
return;
|
||||
}
|
||||
var positionX:Number, positionY:Number;
|
||||
var parentParent:Bone = parent._parent;
|
||||
if (parentParent) {
|
||||
tempPosition[0] = targetX;
|
||||
tempPosition[1] = targetY;
|
||||
parentParent.worldToLocal(tempPosition);
|
||||
targetX = (tempPosition[0] - parent.x) * parentParent._worldScaleX;
|
||||
targetY = (tempPosition[1] - parent.y) * parentParent._worldScaleY;
|
||||
} else {
|
||||
targetX -= parent.x;
|
||||
targetY -= parent.y;
|
||||
}
|
||||
if (child._parent == parent) {
|
||||
positionX = child.x;
|
||||
positionY = child.y;
|
||||
} else {
|
||||
tempPosition[0] = child.x;
|
||||
tempPosition[1] = child.y;
|
||||
child._parent.localToWorld(tempPosition);
|
||||
parent.worldToLocal(tempPosition);
|
||||
positionX = tempPosition[0];
|
||||
positionY = tempPosition[1];
|
||||
}
|
||||
var childX:Number = positionX * parent._worldScaleX, childY:Number = positionY * parent._worldScaleY;
|
||||
var offset:Number = Math.atan2(childY, childX);
|
||||
var len1:Number = Math.sqrt(childX * childX + childY * childY), len2:Number = child.data.length * child._worldScaleX;
|
||||
// Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
|
||||
var cosDenom:Number = 2 * len1 * len2;
|
||||
if (cosDenom < 0.0001) {
|
||||
child.rotationIK = childRotation + (Math.atan2(targetY, targetX) * radDeg - parentRotation - childRotation) * alpha;
|
||||
return;
|
||||
}
|
||||
var cos:Number = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom;
|
||||
if (cos < -1)
|
||||
cos = -1;
|
||||
else if (cos > 1)
|
||||
cos = 1;
|
||||
var childAngle:Number = Math.acos(cos) * bendDirection;
|
||||
var adjacent:Number = len1 + len2 * cos, opposite:Number = len2 * Math.sin(childAngle);
|
||||
var parentAngle:Number = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite);
|
||||
var rotation:Number = (parentAngle - offset) * radDeg - parentRotation;
|
||||
if (rotation > 180)
|
||||
rotation -= 360;
|
||||
else if (rotation < -180) //
|
||||
rotation += 360;
|
||||
parent.rotationIK = parentRotation + rotation * alpha;
|
||||
rotation = (childAngle + offset) * radDeg - childRotation;
|
||||
if (rotation > 180)
|
||||
rotation -= 360;
|
||||
else if (rotation < -180) //
|
||||
rotation += 360;
|
||||
child.rotationIK = childRotation + (rotation + parent._worldRotation - child._parent._worldRotation) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
54
spine-as3/spine-as3/src/spine/IkConstraintData.as
Normal file
54
spine-as3/spine-as3/src/spine/IkConstraintData.as
Normal file
@ -0,0 +1,54 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.1
|
||||
*
|
||||
* Copyright (c) 2013, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to install, execute and perform the Spine Runtimes
|
||||
* Software (the "Software") solely for internal use. Without the written
|
||||
* permission of Esoteric Software (typically granted by licensing Spine), you
|
||||
* may not (a) modify, translate, adapt or otherwise create derivative works,
|
||||
* improvements of the Software or develop new applications using the Software
|
||||
* or (b) remove, delete, alter or obscure any trademarks or any copyright,
|
||||
* trademark, patent or other intellectual property or proprietary rights
|
||||
* notices on or in the Software, including any copy thereof. Redistributions
|
||||
* in binary or source form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
package spine {
|
||||
|
||||
public class IkConstraintData {
|
||||
internal var _name:String;
|
||||
public var bones:Vector.<BoneData> = new Vector.<BoneData>();
|
||||
public var target:BoneData;
|
||||
public var bendDirection:int = 1;
|
||||
public var mix:Number = 1;
|
||||
|
||||
public function IkConstraintData (name:String) {
|
||||
if (name == null) throw new ArgumentError("name cannot be null.");
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public function get name () : String {
|
||||
return _name;
|
||||
}
|
||||
|
||||
public function toString () : String {
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,45 +33,95 @@ import spine.attachments.Attachment;
|
||||
|
||||
public class Skeleton {
|
||||
internal var _data:SkeletonData;
|
||||
internal var _bones:Vector.<Bone>;
|
||||
internal var _slots:Vector.<Slot>;
|
||||
internal var _drawOrder:Vector.<Slot>;
|
||||
internal var _skin:Skin;
|
||||
public var r:Number = 1;
|
||||
public var g:Number = 1;
|
||||
public var b:Number = 1;
|
||||
public var a:Number = 1;
|
||||
public var bones:Vector.<Bone>;
|
||||
public var slots:Vector.<Slot>;
|
||||
public var drawOrder:Vector.<Slot>;
|
||||
public var ikConstraints:Vector.<IkConstraint>;
|
||||
private var _boneCache:Vector.<Vector.<Bone>> = new Vector.<Vector.<Bone>>();
|
||||
private var _skin:Skin;
|
||||
public var r:Number = 1, g:Number = 1, b:Number = 1, a:Number = 1;
|
||||
public var time:Number = 0;
|
||||
public var flipX:Boolean;
|
||||
public var flipY:Boolean;
|
||||
public var x:Number = 0;
|
||||
public var y:Number = 0;
|
||||
public var flipX:Boolean, flipY:Boolean;
|
||||
public var x:Number = 0, y:Number = 0;
|
||||
|
||||
public function Skeleton (data:SkeletonData) {
|
||||
if (data == null)
|
||||
throw new ArgumentError("data cannot be null.");
|
||||
_data = data;
|
||||
|
||||
_bones = new Vector.<Bone>();
|
||||
bones = new Vector.<Bone>();
|
||||
for each (var boneData:BoneData in data.bones) {
|
||||
var parent:Bone = boneData.parent == null ? null : _bones[data.bones.indexOf(boneData.parent)];
|
||||
_bones[_bones.length] = new Bone(boneData, parent);
|
||||
var parent:Bone = boneData.parent == null ? null : bones[data.bones.indexOf(boneData.parent)];
|
||||
bones[bones.length] = new Bone(boneData, this, parent);
|
||||
}
|
||||
|
||||
_slots = new Vector.<Slot>();
|
||||
_drawOrder = new Vector.<Slot>();
|
||||
slots = new Vector.<Slot>();
|
||||
drawOrder = new Vector.<Slot>();
|
||||
for each (var slotData:SlotData in data.slots) {
|
||||
var bone:Bone = _bones[data.bones.indexOf(slotData.boneData)];
|
||||
var slot:Slot = new Slot(slotData, this, bone);
|
||||
_slots[_slots.length] = slot;
|
||||
_drawOrder[_drawOrder.length] = slot;
|
||||
var bone:Bone = bones[data.bones.indexOf(slotData.boneData)];
|
||||
var slot:Slot = new Slot(slotData, bone);
|
||||
slots[slots.length] = slot;
|
||||
drawOrder[drawOrder.length] = slot;
|
||||
}
|
||||
|
||||
ikConstraints = new Vector.<IkConstraint>()
|
||||
for each (var ikConstraintData:IkConstraintData in data.ikConstraints)
|
||||
ikConstraints[ikConstraints.length] = new IkConstraint(ikConstraintData, this);
|
||||
|
||||
updateCache();
|
||||
}
|
||||
|
||||
/** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */
|
||||
public function updateCache () : void {
|
||||
var ikConstraintsCount:int = ikConstraints.length;
|
||||
|
||||
var arrayCount:int = ikConstraintsCount + 1;
|
||||
if (_boneCache.length > arrayCount) _boneCache.splice(arrayCount, _boneCache.length - arrayCount);
|
||||
for each (var cachedBones:Vector.<Bone> in _boneCache)
|
||||
cachedBones.length = 0;
|
||||
while (_boneCache.length < arrayCount)
|
||||
_boneCache[_boneCache.length] = new Vector.<Bone>();
|
||||
|
||||
var nonIkBones:Vector.<Bone> = _boneCache[0];
|
||||
|
||||
outer:
|
||||
for each (var bone:Bone in bones) {
|
||||
var current:Bone = bone;
|
||||
do {
|
||||
var ii:int = 0;
|
||||
for each (var ikConstraint:IkConstraint in ikConstraints) {
|
||||
var parent:Bone = ikConstraint.bones[0];
|
||||
var child:Bone = ikConstraint.bones[int(ikConstraint.bones.length - 1)];
|
||||
while (true) {
|
||||
if (current == child) {
|
||||
_boneCache[ii].push(bone);
|
||||
_boneCache[int(ii + 1)].push(bone);
|
||||
continue outer;
|
||||
}
|
||||
if (child == parent) break;
|
||||
child = child.parent;
|
||||
}
|
||||
ii++;
|
||||
}
|
||||
current = current.parent;
|
||||
} while (current != null);
|
||||
nonIkBones[nonIkBones.length] = bone;
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the world transform for each bone. */
|
||||
/** Updates the world transform for each bone and applies IK constraints. */
|
||||
public function updateWorldTransform () : void {
|
||||
for each (var bone:Bone in _bones)
|
||||
bone.updateWorldTransform(flipX, flipY);
|
||||
var bone:Bone;
|
||||
for each (bone in bones)
|
||||
bone.rotationIK = bone.rotation;
|
||||
var i:int = 0, last:int = _boneCache.length - 1;
|
||||
while (true) {
|
||||
for each (bone in _boneCache[i])
|
||||
bone.updateWorldTransform();
|
||||
if (i == last) break;
|
||||
ikConstraints[i].apply();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the bones and slots to their setup pose values. */
|
||||
@ -81,13 +131,18 @@ public class Skeleton {
|
||||
}
|
||||
|
||||
public function setBonesToSetupPose () : void {
|
||||
for each (var bone:Bone in _bones)
|
||||
for each (var bone:Bone in bones)
|
||||
bone.setToSetupPose();
|
||||
|
||||
for each (var ikConstraint:IkConstraint in ikConstraints) {
|
||||
ikConstraint.bendDirection = ikConstraint._data.bendDirection;
|
||||
ikConstraint.mix = ikConstraint._data.mix;
|
||||
}
|
||||
}
|
||||
|
||||
public function setSlotsToSetupPose () : void {
|
||||
var i:int = 0;
|
||||
for each (var slot:Slot in _slots) {
|
||||
for each (var slot:Slot in slots) {
|
||||
drawOrder[i++] = slot;
|
||||
slot.setToSetupPose();
|
||||
}
|
||||
@ -97,23 +152,17 @@ public class Skeleton {
|
||||
return _data;
|
||||
}
|
||||
|
||||
public function get bones () : Vector.<Bone> {
|
||||
return _bones;
|
||||
}
|
||||
|
||||
public function get rootBone () : Bone {
|
||||
if (_bones.length == 0)
|
||||
return null;
|
||||
return _bones[0];
|
||||
if (bones.length == 0) return null;
|
||||
return bones[0];
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findBone (boneName:String) : Bone {
|
||||
if (boneName == null)
|
||||
throw new ArgumentError("boneName cannot be null.");
|
||||
for each (var bone:Bone in _bones)
|
||||
if (bone.data.name == boneName)
|
||||
return bone;
|
||||
for each (var bone:Bone in bones)
|
||||
if (bone._data._name == boneName) return bone;
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -122,25 +171,19 @@ public class Skeleton {
|
||||
if (boneName == null)
|
||||
throw new ArgumentError("boneName cannot be null.");
|
||||
var i:int = 0;
|
||||
for each (var bone:Bone in _bones) {
|
||||
if (bone.data.name == boneName)
|
||||
return i;
|
||||
for each (var bone:Bone in bones) {
|
||||
if (bone._data._name == boneName) return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function get slots () : Vector.<Slot> {
|
||||
return _slots;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findSlot (slotName:String) : Slot {
|
||||
if (slotName == null)
|
||||
throw new ArgumentError("slotName cannot be null.");
|
||||
for each (var slot:Slot in _slots)
|
||||
if (slot.data.name == slotName)
|
||||
return slot;
|
||||
for each (var slot:Slot in slots)
|
||||
if (slot._data._name == slotName) return slot;
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -149,26 +192,20 @@ public class Skeleton {
|
||||
if (slotName == null)
|
||||
throw new ArgumentError("slotName cannot be null.");
|
||||
var i:int = 0;
|
||||
for each (var slot:Slot in _slots) {
|
||||
if (slot.data.name == slotName)
|
||||
return i;
|
||||
for each (var slot:Slot in slots) {
|
||||
if (slot._data._name == slotName) return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function get drawOrder () : Vector.<Slot> {
|
||||
return _drawOrder;
|
||||
}
|
||||
|
||||
public function get skin () : Skin {
|
||||
return _skin;
|
||||
}
|
||||
|
||||
public function set skinName (skinName:String) : void {
|
||||
var skin:Skin = data.findSkin(skinName);
|
||||
if (skin == null)
|
||||
throw new ArgumentError("Skin not found: " + skinName);
|
||||
if (skin == null) throw new ArgumentError("Skin not found: " + skinName);
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
@ -182,8 +219,8 @@ public class Skeleton {
|
||||
newSkin.attachAll(this, skin);
|
||||
else {
|
||||
var i:int = 0;
|
||||
for each (var slot:Slot in _slots) {
|
||||
var name:String = slot.data.attachmentName;
|
||||
for each (var slot:Slot in slots) {
|
||||
var name:String = slot._data.attachmentName;
|
||||
if (name) {
|
||||
var attachment:Attachment = newSkin.getAttachment(i, name);
|
||||
if (attachment) slot.attachment = attachment;
|
||||
@ -202,25 +239,21 @@ public class Skeleton {
|
||||
|
||||
/** @return May be null. */
|
||||
public function getAttachmentForSlotIndex (slotIndex:int, attachmentName:String) : Attachment {
|
||||
if (attachmentName == null)
|
||||
throw new ArgumentError("attachmentName cannot be null.");
|
||||
if (attachmentName == null) throw new ArgumentError("attachmentName cannot be null.");
|
||||
if (skin != null) {
|
||||
var attachment:Attachment = skin.getAttachment(slotIndex, attachmentName);
|
||||
if (attachment != null)
|
||||
return attachment;
|
||||
if (attachment != null) return attachment;
|
||||
}
|
||||
if (data.defaultSkin != null)
|
||||
return data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
||||
if (data.defaultSkin != null) return data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @param attachmentName May be null. */
|
||||
public function setAttachment (slotName:String, attachmentName:String) : void {
|
||||
if (slotName == null)
|
||||
throw new ArgumentError("slotName cannot be null.");
|
||||
if (slotName == null) throw new ArgumentError("slotName cannot be null.");
|
||||
var i:int = 0;
|
||||
for each (var slot:Slot in _slots) {
|
||||
if (slot.data.name == slotName) {
|
||||
for each (var slot:Slot in slots) {
|
||||
if (slot._data._name == slotName) {
|
||||
var attachment:Attachment = null;
|
||||
if (attachmentName != null) {
|
||||
attachment = getAttachmentForSlotIndex(i, attachmentName);
|
||||
@ -235,6 +268,14 @@ public class Skeleton {
|
||||
throw new ArgumentError("Slot not found: " + slotName);
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findIkConstraint (ikConstraintName:String) : IkConstraint {
|
||||
if (ikConstraintName == null) throw new ArgumentError("ikConstraintName cannot be null.");
|
||||
for each (var ikConstraint:IkConstraint in ikConstraints)
|
||||
if (ikConstraint._data._name == ikConstraintName) return ikConstraint;
|
||||
return null;
|
||||
}
|
||||
|
||||
public function update (delta:Number) : void {
|
||||
time += delta;
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ public class SkeletonBounds {
|
||||
var slots:Vector.<Slot> = skeleton.slots;
|
||||
var slotCount:int = slots.length;
|
||||
var x:Number = skeleton.x, y:Number = skeleton.y;
|
||||
|
||||
|
||||
boundingBoxes.length = 0;
|
||||
for each (var polygon:Polygon in polygons)
|
||||
polygonPool[polygonPool.length] = polygon;
|
||||
|
||||
@ -32,6 +32,7 @@ package spine {
|
||||
import spine.animation.Animation;
|
||||
|
||||
public class SkeletonData {
|
||||
/** May be null. */
|
||||
public var name:String;
|
||||
public var bones:Vector.<BoneData> = new Vector.<BoneData>(); // Ordered parents first.
|
||||
public var slots:Vector.<SlotData> = new Vector.<SlotData>(); // Setup pose draw order.
|
||||
@ -39,122 +40,87 @@ public class SkeletonData {
|
||||
public var defaultSkin:Skin;
|
||||
public var events:Vector.<EventData> = new Vector.<EventData>();
|
||||
public var animations:Vector.<Animation> = new Vector.<Animation>();
|
||||
public var ikConstraints:Vector.<IkConstraintData> = new Vector.<IkConstraintData>();
|
||||
public var width:Number, height:Number;
|
||||
public var version:String, hash:String;
|
||||
|
||||
// --- Bones.
|
||||
|
||||
public function addBone (bone:BoneData) : void {
|
||||
if (bone == null)
|
||||
throw new ArgumentError("bone cannot be null.");
|
||||
bones[bones.length] = bone;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findBone (boneName:String) : BoneData {
|
||||
if (boneName == null)
|
||||
throw new ArgumentError("boneName cannot be null.");
|
||||
if (boneName == null) throw new ArgumentError("boneName cannot be null.");
|
||||
for (var i:int = 0, n:int = bones.length; i < n; i++) {
|
||||
var bone:BoneData = bones[i];
|
||||
if (bone._name == boneName)
|
||||
return bone;
|
||||
if (bone._name == boneName) return bone;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return -1 if the bone was not found. */
|
||||
public function findBoneIndex (boneName:String) : int {
|
||||
if (boneName == null)
|
||||
throw new ArgumentError("boneName cannot be null.");
|
||||
if (boneName == null) throw new ArgumentError("boneName cannot be null.");
|
||||
for (var i:int = 0, n:int = bones.length; i < n; i++)
|
||||
if (bones[i]._name == boneName)
|
||||
return i;
|
||||
if (bones[i]._name == boneName) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --- Slots.
|
||||
|
||||
public function addSlot (slot:SlotData) : void {
|
||||
if (slot == null)
|
||||
throw new ArgumentError("slot cannot be null.");
|
||||
slots[slots.length] = slot;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findSlot (slotName:String) : SlotData {
|
||||
if (slotName == null)
|
||||
throw new ArgumentError("slotName cannot be null.");
|
||||
if (slotName == null) throw new ArgumentError("slotName cannot be null.");
|
||||
for (var i:int = 0, n:int = slots.length; i < n; i++) {
|
||||
var slot:SlotData = slots[i];
|
||||
if (slot._name == slotName)
|
||||
return slot;
|
||||
if (slot._name == slotName) return slot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return -1 if the bone was not found. */
|
||||
public function findSlotIndex (slotName:String) : int {
|
||||
if (slotName == null)
|
||||
throw new ArgumentError("slotName cannot be null.");
|
||||
if (slotName == null) throw new ArgumentError("slotName cannot be null.");
|
||||
for (var i:int = 0, n:int = slots.length; i < n; i++)
|
||||
if (slots[i]._name == slotName)
|
||||
return i;
|
||||
if (slots[i]._name == slotName) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --- Skins.
|
||||
|
||||
public function addSkin (skin:Skin) : void {
|
||||
if (skin == null)
|
||||
throw new ArgumentError("skin cannot be null.");
|
||||
skins[skins.length] = skin;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findSkin (skinName:String) : Skin {
|
||||
if (skinName == null)
|
||||
throw new ArgumentError("skinName cannot be null.");
|
||||
if (skinName == null) throw new ArgumentError("skinName cannot be null.");
|
||||
for each (var skin:Skin in skins)
|
||||
if (skin._name == skinName)
|
||||
return skin;
|
||||
if (skin._name == skinName) return skin;
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Events.
|
||||
|
||||
public function addEvent (eventData:EventData) : void {
|
||||
if (eventData == null)
|
||||
throw new ArgumentError("eventData cannot be null.");
|
||||
events[events.length] = eventData;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findEvent (eventName:String) : EventData {
|
||||
if (eventName == null)
|
||||
throw new ArgumentError("eventName cannot be null.");
|
||||
for (var i:int = 0, n:int = events.length; i < n; i++) {
|
||||
var eventData:EventData = events[i];
|
||||
if (eventData.name == eventName)
|
||||
return eventData;
|
||||
}
|
||||
if (eventName == null) throw new ArgumentError("eventName cannot be null.");
|
||||
for each (var eventData:EventData in events)
|
||||
if (eventData._name == eventName) return eventData;
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Animations.
|
||||
|
||||
public function addAnimation (animation:Animation) : void {
|
||||
if (animation == null)
|
||||
throw new ArgumentError("animation cannot be null.");
|
||||
animations[animations.length] = animation;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function findAnimation (animationName:String) : Animation {
|
||||
if (animationName == null)
|
||||
throw new ArgumentError("animationName cannot be null.");
|
||||
for (var i:int = 0, n:int = animations.length; i < n; i++) {
|
||||
var animation:Animation = animations[i];
|
||||
if (animation.name == animationName)
|
||||
return animation;
|
||||
}
|
||||
if (animationName == null) throw new ArgumentError("animationName cannot be null.");
|
||||
for each (var animation:Animation in animations)
|
||||
if (animation.name == animationName) return animation;
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- IK constraints.
|
||||
|
||||
/** @return May be null. */
|
||||
public function findIkConstraint (ikConstraintName:String) : IkConstraintData {
|
||||
if (ikConstraintName == null) throw new ArgumentError("ikConstraintName cannot be null.");
|
||||
for each (var ikConstraintData:IkConstraintData in ikConstraints)
|
||||
if (ikConstraintData._name == ikConstraintName) return ikConstraintData;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ import spine.animation.CurveTimeline;
|
||||
import spine.animation.DrawOrderTimeline;
|
||||
import spine.animation.EventTimeline;
|
||||
import spine.animation.FfdTimeline;
|
||||
import spine.animation.IkConstraintTimeline;
|
||||
import spine.animation.RotateTimeline;
|
||||
import spine.animation.ScaleTimeline;
|
||||
import spine.animation.Timeline;
|
||||
@ -66,8 +67,7 @@ public class SkeletonJson {
|
||||
|
||||
/** @param object A String or ByteArray. */
|
||||
public function readSkeletonData (object:*, name:String = null) : SkeletonData {
|
||||
if (object == null)
|
||||
throw new ArgumentError("object cannot be null.");
|
||||
if (object == null) throw new ArgumentError("object cannot be null.");
|
||||
|
||||
var root:Object;
|
||||
if (object is String)
|
||||
@ -82,6 +82,15 @@ public class SkeletonJson {
|
||||
var skeletonData:SkeletonData = new SkeletonData();
|
||||
skeletonData.name = name;
|
||||
|
||||
// Skeleton.
|
||||
var skeletonMap:Object = root["skeleton"];
|
||||
if (skeletonMap) {
|
||||
skeletonData.hash = skeletonMap["hash"];
|
||||
skeletonData.version = skeletonMap["spine"];
|
||||
skeletonData.width = skeletonMap["width"] || 0;
|
||||
skeletonData.height = skeletonMap["height"] || 0;
|
||||
}
|
||||
|
||||
// Bones.
|
||||
var boneData:BoneData;
|
||||
for each (var boneMap:Object in root["bones"]) {
|
||||
@ -89,8 +98,7 @@ public class SkeletonJson {
|
||||
var parentName:String = boneMap["parent"];
|
||||
if (parentName) {
|
||||
parent = skeletonData.findBone(parentName);
|
||||
if (!parent)
|
||||
throw new Error("Parent bone not found: " + parentName);
|
||||
if (!parent) throw new Error("Parent bone not found: " + parentName);
|
||||
}
|
||||
boneData = new BoneData(boneMap["name"], parent);
|
||||
boneData.length = (boneMap["length"] || 0) * scale;
|
||||
@ -101,15 +109,33 @@ public class SkeletonJson {
|
||||
boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1;
|
||||
boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true;
|
||||
boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true;
|
||||
skeletonData.addBone(boneData);
|
||||
skeletonData.bones[skeletonData.bones.length] = boneData;
|
||||
}
|
||||
|
||||
// IK constraints.
|
||||
for each (var ikMap:Object in root["ik"]) {
|
||||
var ikConstraintData:IkConstraintData = new IkConstraintData(ikMap["name"]);
|
||||
|
||||
for each (var boneName:String in ikMap["bones"]) {
|
||||
var bone:BoneData = skeletonData.findBone(boneName);
|
||||
if (!bone) throw new Error("IK bone not found: " + boneName);
|
||||
ikConstraintData.bones[ikConstraintData.bones.length] = bone;
|
||||
}
|
||||
|
||||
ikConstraintData.target = skeletonData.findBone(ikMap["target"]);
|
||||
if (!ikConstraintData.target) throw new Error("Target bone not found: " + ikMap["target"]);
|
||||
|
||||
ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1;
|
||||
ikConstraintData.mix = ikMap["mix"] || 1;
|
||||
|
||||
skeletonData.ikConstraints[skeletonData.ikConstraints.length] = ikConstraintData;
|
||||
}
|
||||
|
||||
// Slots.
|
||||
for each (var slotMap:Object in root["slots"]) {
|
||||
var boneName:String = slotMap["bone"];
|
||||
boneName = slotMap["bone"];
|
||||
boneData = skeletonData.findBone(boneName);
|
||||
if (!boneData)
|
||||
throw new Error("Slot bone not found: " + boneName);
|
||||
if (!boneData) throw new Error("Slot bone not found: " + boneName);
|
||||
var slotData:SlotData = new SlotData(slotMap["name"], boneData);
|
||||
|
||||
var color:String = slotMap["color"];
|
||||
@ -123,7 +149,7 @@ public class SkeletonJson {
|
||||
slotData.attachmentName = slotMap["attachment"];
|
||||
slotData.additiveBlending = slotMap["additive"];
|
||||
|
||||
skeletonData.addSlot(slotData);
|
||||
skeletonData.slots[skeletonData.slots.length] = slotData;
|
||||
}
|
||||
|
||||
// Skins.
|
||||
@ -140,7 +166,7 @@ public class SkeletonJson {
|
||||
skin.addAttachment(slotIndex, attachmentName, attachment);
|
||||
}
|
||||
}
|
||||
skeletonData.addSkin(skin);
|
||||
skeletonData.skins[skeletonData.skins.length] = skin;
|
||||
if (skin.name == "default")
|
||||
skeletonData.defaultSkin = skin;
|
||||
}
|
||||
@ -154,7 +180,7 @@ public class SkeletonJson {
|
||||
eventData.intValue = eventMap["int"] || 0;
|
||||
eventData.floatValue = eventMap["float"] || 0;
|
||||
eventData.stringValue = eventMap["string"] || null;
|
||||
skeletonData.addEvent(eventData);
|
||||
skeletonData.events[skeletonData.events.length] = eventData;
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,8 +348,7 @@ public class SkeletonJson {
|
||||
var bones:Object = map["bones"];
|
||||
for (var boneName:String in bones) {
|
||||
var boneIndex:int = skeletonData.findBoneIndex(boneName);
|
||||
if (boneIndex == -1)
|
||||
throw new Error("Bone not found: " + boneName);
|
||||
if (boneIndex == -1) throw new Error("Bone not found: " + boneName);
|
||||
var boneMap:Object = bones[boneName];
|
||||
|
||||
for (timelineName in boneMap) {
|
||||
@ -368,6 +393,24 @@ public class SkeletonJson {
|
||||
}
|
||||
}
|
||||
|
||||
var ikMap:Object = map["ik"];
|
||||
for (var ikConstraintName:String in ikMap) {
|
||||
var ikConstraint:IkConstraintData = skeletonData.findIkConstraint(ikConstraintName);
|
||||
values = ikMap[ikConstraintName];
|
||||
var ikTimeline:IkConstraintTimeline = new IkConstraintTimeline(values.length);
|
||||
ikTimeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint);
|
||||
frameIndex = 0;
|
||||
for each (valueMap in values) {
|
||||
var mix:Number = valueMap["mix"] || 1;
|
||||
var bendDirection:int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1;
|
||||
ikTimeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection);
|
||||
readCurve(ikTimeline, frameIndex, valueMap);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines[timelines.length] = ikTimeline;
|
||||
duration = Math.max(duration, ikTimeline.frames[ikTimeline.frameCount * 3 - 3]);
|
||||
}
|
||||
|
||||
var ffd:Object = map["ffd"];
|
||||
for (var skinName:String in ffd) {
|
||||
var skin:Skin = skeletonData.findSkin(skinName);
|
||||
@ -468,7 +511,7 @@ public class SkeletonJson {
|
||||
frameIndex = 0;
|
||||
for each (var eventMap:Object in eventsMap) {
|
||||
var eventData:EventData = skeletonData.findEvent(eventMap["name"]);
|
||||
if (eventData == null) throw new Error("Event not found: " + eventMap["name"]);
|
||||
if (!eventData) throw new Error("Event not found: " + eventMap["name"]);
|
||||
var event:Event = new Event(eventData);
|
||||
event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue;
|
||||
event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue;
|
||||
@ -479,23 +522,20 @@ public class SkeletonJson {
|
||||
duration = Math.max(duration, eventTimeline.frames[eventTimeline.frameCount - 1]);
|
||||
}
|
||||
|
||||
skeletonData.addAnimation(new Animation(name, timelines, duration));
|
||||
skeletonData.animations[skeletonData.animations.length] = new Animation(name, timelines, duration);
|
||||
}
|
||||
|
||||
static private function readCurve (timeline:CurveTimeline, frameIndex:int, valueMap:Object) : void {
|
||||
var curve:Object = valueMap["curve"];
|
||||
if (curve == null)
|
||||
return;
|
||||
if (!curve) return;
|
||||
if (curve == "stepped")
|
||||
timeline.setStepped(frameIndex);
|
||||
else if (curve is Array) {
|
||||
else if (curve is Array)
|
||||
timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]);
|
||||
}
|
||||
}
|
||||
|
||||
static private function toColor (hexString:String, colorIndex:int) : Number {
|
||||
if (hexString.length != 8)
|
||||
throw new ArgumentError("Color hexidecimal length must be 8, recieved: " + hexString);
|
||||
if (hexString.length != 8) throw new ArgumentError("Color hexidecimal length must be 8, recieved: " + hexString);
|
||||
return parseInt(hexString.substring(colorIndex * 2, colorIndex * 2 + 2), 16) / 255;
|
||||
}
|
||||
|
||||
|
||||
@ -39,14 +39,12 @@ public class Skin {
|
||||
private var _attachments:Vector.<Dictionary> = new Vector.<Dictionary>();
|
||||
|
||||
public function Skin (name:String) {
|
||||
if (name == null)
|
||||
throw new ArgumentError("name cannot be null.");
|
||||
if (name == null) throw new ArgumentError("name cannot be null.");
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public function addAttachment (slotIndex:int, name:String, attachment:Attachment) : void {
|
||||
if (attachment == null)
|
||||
throw new ArgumentError("attachment cannot be null.");
|
||||
if (attachment == null) throw new ArgumentError("attachment cannot be null.");
|
||||
if (slotIndex >= attachments.length) attachments.length = slotIndex + 1;
|
||||
if (!attachments[slotIndex]) attachments[slotIndex] = new Dictionary();
|
||||
attachments[slotIndex][name] = attachment;
|
||||
@ -74,7 +72,7 @@ public class Skin {
|
||||
/** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */
|
||||
public function attachAll (skeleton:Skeleton, oldSkin:Skin) : void {
|
||||
var slotIndex:int = 0;
|
||||
for each (var slot:Slot in skeleton._slots) {
|
||||
for each (var slot:Slot in skeleton.slots) {
|
||||
var slotAttachment:Attachment = slot.attachment;
|
||||
if (slotAttachment && slotIndex < oldSkin.attachments.length) {
|
||||
var dictionary:Dictionary = oldSkin.attachments[slotIndex];
|
||||
|
||||
@ -34,7 +34,6 @@ import spine.attachments.Attachment;
|
||||
public class Slot {
|
||||
internal var _data:SlotData;
|
||||
internal var _bone:Bone;
|
||||
internal var _skeleton:Skeleton;
|
||||
public var r:Number;
|
||||
public var g:Number;
|
||||
public var b:Number;
|
||||
@ -43,15 +42,10 @@ public class Slot {
|
||||
private var _attachmentTime:Number;
|
||||
public var attachmentVertices:Vector.<Number> = new Vector.<Number>();
|
||||
|
||||
public function Slot (data:SlotData, skeleton:Skeleton, bone:Bone) {
|
||||
if (data == null)
|
||||
throw new ArgumentError("data cannot be null.");
|
||||
if (skeleton == null)
|
||||
throw new ArgumentError("skeleton cannot be null.");
|
||||
if (bone == null)
|
||||
throw new ArgumentError("bone cannot be null.");
|
||||
public function Slot (data:SlotData, bone:Bone) {
|
||||
if (data == null) throw new ArgumentError("data cannot be null.");
|
||||
if (bone == null) throw new ArgumentError("bone cannot be null.");
|
||||
_data = data;
|
||||
_skeleton = skeleton;
|
||||
_bone = bone;
|
||||
setToSetupPose();
|
||||
}
|
||||
@ -59,14 +53,14 @@ public class Slot {
|
||||
public function get data () : SlotData {
|
||||
return _data;
|
||||
}
|
||||
|
||||
public function get skeleton () : Skeleton {
|
||||
return _skeleton;
|
||||
}
|
||||
|
||||
|
||||
public function get bone () : Bone {
|
||||
return _bone;
|
||||
}
|
||||
|
||||
public function get skeleton () : Skeleton {
|
||||
return _bone._skeleton;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public function get attachment () : Attachment {
|
||||
@ -77,26 +71,26 @@ public class Slot {
|
||||
* @param attachment May be null. */
|
||||
public function set attachment (attachment:Attachment) : void {
|
||||
_attachment = attachment;
|
||||
_attachmentTime = _skeleton.time;
|
||||
_attachmentTime = _bone._skeleton.time;
|
||||
attachmentVertices.length = 0;
|
||||
}
|
||||
|
||||
public function set attachmentTime (time:Number) : void {
|
||||
_attachmentTime = skeleton.time - time;
|
||||
_attachmentTime = _bone._skeleton.time - time;
|
||||
}
|
||||
|
||||
/** Returns the time since the attachment was set. */
|
||||
public function get attachmentTime () : Number {
|
||||
return skeleton.time - _attachmentTime;
|
||||
return _bone._skeleton.time - _attachmentTime;
|
||||
}
|
||||
|
||||
public function setToSetupPose () : void {
|
||||
var slotIndex:int = skeleton.data.slots.indexOf(data);
|
||||
var slotIndex:int = _bone._skeleton.data.slots.indexOf(data);
|
||||
r = _data.r;
|
||||
g = _data.g;
|
||||
b = _data.b;
|
||||
a = _data.a;
|
||||
attachment = _data.attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, data.attachmentName);
|
||||
attachment = _data.attachmentName == null ? null : _bone._skeleton.getAttachmentForSlotIndex(slotIndex, data.attachmentName);
|
||||
}
|
||||
|
||||
public function toString () : String {
|
||||
|
||||
@ -41,10 +41,8 @@ public class SlotData {
|
||||
public var additiveBlending:Boolean;
|
||||
|
||||
public function SlotData (name:String, boneData:BoneData) {
|
||||
if (name == null)
|
||||
throw new ArgumentError("name cannot be null.");
|
||||
if (boneData == null)
|
||||
throw new ArgumentError("boneData cannot be null.");
|
||||
if (name == null) throw new ArgumentError("name cannot be null.");
|
||||
if (boneData == null) throw new ArgumentError("boneData cannot be null.");
|
||||
_name = name;
|
||||
_boneData = boneData;
|
||||
}
|
||||
|
||||
@ -38,10 +38,8 @@ public class Animation {
|
||||
public var duration:Number;
|
||||
|
||||
public function Animation (name:String, timelines:Vector.<Timeline>, duration:Number) {
|
||||
if (name == null)
|
||||
throw new ArgumentError("name cannot be null.");
|
||||
if (timelines == null)
|
||||
throw new ArgumentError("timelines cannot be null.");
|
||||
if (name == null) throw new ArgumentError("name cannot be null.");
|
||||
if (timelines == null) throw new ArgumentError("timelines cannot be null.");
|
||||
_name = name;
|
||||
_timelines = timelines;
|
||||
this.duration = duration;
|
||||
@ -53,8 +51,7 @@ public class Animation {
|
||||
|
||||
/** Poses the skeleton at the specified time for this animation. */
|
||||
public function apply (skeleton:Skeleton, lastTime:Number, time:Number, loop:Boolean, events:Vector.<Event>) : void {
|
||||
if (skeleton == null)
|
||||
throw new ArgumentError("skeleton cannot be null.");
|
||||
if (skeleton == null) throw new ArgumentError("skeleton cannot be null.");
|
||||
|
||||
if (loop && duration != 0) {
|
||||
time %= duration;
|
||||
@ -68,8 +65,7 @@ public class Animation {
|
||||
/** Poses the skeleton at the specified time for this animation mixed with the current pose.
|
||||
* @param alpha The amount of this animation that affects the current pose. */
|
||||
public function mix (skeleton:Skeleton, lastTime:Number, time:Number, loop:Boolean, events:Vector.<Event>, alpha:Number) : void {
|
||||
if (skeleton == null)
|
||||
throw new ArgumentError("skeleton cannot be null.");
|
||||
if (skeleton == null) throw new ArgumentError("skeleton cannot be null.");
|
||||
|
||||
if (loop && duration != 0) {
|
||||
time %= duration;
|
||||
@ -107,6 +103,25 @@ public class Animation {
|
||||
return 0; // Can't happen.
|
||||
}
|
||||
|
||||
/** @param target After the first and before the last entry. */
|
||||
static public function binarySearch1 (values:Vector.<Number>, target:Number) : int {
|
||||
var low:int = 0;
|
||||
var high:int = values.length - 2;
|
||||
if (high == 0)
|
||||
return 1;
|
||||
var current:int = high >>> 1;
|
||||
while (true) {
|
||||
if (values[int(current + 1)] <= target)
|
||||
low = current + 1;
|
||||
else
|
||||
high = current;
|
||||
if (low == high)
|
||||
return low + 1;
|
||||
current = (low + high) >>> 1;
|
||||
}
|
||||
return 0; // Can't happen.
|
||||
}
|
||||
|
||||
static public function linearSearch (values:Vector.<Number>, target:Number, step:int) : int {
|
||||
for (var i:int = 0, last:int = values.length - step; i <= last; i += step)
|
||||
if (values[i] > target)
|
||||
|
||||
@ -46,26 +46,21 @@ public class AnimationStateData {
|
||||
|
||||
public function setMixByName (fromName:String, toName:String, duration:Number) : void {
|
||||
var from:Animation = _skeletonData.findAnimation(fromName);
|
||||
if (from == null)
|
||||
throw new ArgumentError("Animation not found: " + fromName);
|
||||
if (from == null) throw new ArgumentError("Animation not found: " + fromName);
|
||||
var to:Animation = _skeletonData.findAnimation(toName);
|
||||
if (to == null)
|
||||
throw new ArgumentError("Animation not found: " + toName);
|
||||
if (to == null) throw new ArgumentError("Animation not found: " + toName);
|
||||
setMix(from, to, duration);
|
||||
}
|
||||
|
||||
public function setMix (from:Animation, to:Animation, duration:Number) : void {
|
||||
if (from == null)
|
||||
throw new ArgumentError("from cannot be null.");
|
||||
if (to == null)
|
||||
throw new ArgumentError("to cannot be null.");
|
||||
if (from == null) throw new ArgumentError("from cannot be null.");
|
||||
if (to == null) throw new ArgumentError("to cannot be null.");
|
||||
animationToMixTime[from.name + ":" + to.name] = duration;
|
||||
}
|
||||
|
||||
public function getMix (from:Animation, to:Animation) : Number {
|
||||
var time:Object = animationToMixTime[from.name + ":" + to.name];
|
||||
if (time == null)
|
||||
return defaultMix;
|
||||
if (time == null) return defaultMix;
|
||||
return time as Number;
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,78 +35,51 @@ import spine.Skeleton;
|
||||
/** Base class for frames that use an interpolation bezier curve. */
|
||||
public class CurveTimeline implements Timeline {
|
||||
static private const LINEAR:Number = 0;
|
||||
static private const STEPPED:Number = -1;
|
||||
static private const STEPPED:Number = 1;
|
||||
static private const BEZIER:Number = 2;
|
||||
static private const BEZIER_SEGMENTS:int = 10;
|
||||
static private const BEZIER_SIZE:int = BEZIER_SEGMENTS * 2 - 1;
|
||||
|
||||
private var curves:Vector.<Number>; // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ...
|
||||
private var curves:Vector.<Number>; // type, x, y, ...
|
||||
|
||||
public function CurveTimeline (frameCount:int) {
|
||||
curves = new Vector.<Number>(frameCount * 6, true)
|
||||
curves = new Vector.<Number>((frameCount - 1) * BEZIER_SIZE, true)
|
||||
}
|
||||
|
||||
public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector.<Event>, alpha:Number) : void {
|
||||
}
|
||||
|
||||
public function get frameCount () : int {
|
||||
return curves.length / 6;
|
||||
return curves.length / BEZIER_SIZE + 1;
|
||||
}
|
||||
|
||||
public function setLinear (frameIndex:int) : void {
|
||||
curves[int(frameIndex * 6)] = LINEAR;
|
||||
curves[int(frameIndex * BEZIER_SIZE)] = LINEAR;
|
||||
}
|
||||
|
||||
public function setStepped (frameIndex:int) : void {
|
||||
curves[int(frameIndex * 6)] = STEPPED;
|
||||
curves[int(frameIndex * BEZIER_SIZE)] = STEPPED;
|
||||
}
|
||||
|
||||
/** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
|
||||
* cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
|
||||
* the difference between the keyframe's values. */
|
||||
public function setCurve (frameIndex:int, cx1:Number, cy1:Number, cx2:Number, cy2:Number) : void {
|
||||
var subdiv_step:Number = 1 / BEZIER_SEGMENTS;
|
||||
var subdiv_step2:Number = subdiv_step * subdiv_step;
|
||||
var subdiv_step3:Number = subdiv_step2 * subdiv_step;
|
||||
var pre1:Number = 3 * subdiv_step;
|
||||
var pre2:Number = 3 * subdiv_step2;
|
||||
var pre4:Number = 6 * subdiv_step2;
|
||||
var pre5:Number = 6 * subdiv_step3;
|
||||
var tmp1x:Number = -cx1 * 2 + cx2;
|
||||
var tmp1y:Number = -cy1 * 2 + cy2;
|
||||
var tmp2x:Number = (cx1 - cx2) * 3 + 1;
|
||||
var tmp2y:Number = (cy1 - cy2) * 3 + 1;
|
||||
var i:int = frameIndex * 6;
|
||||
curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3;
|
||||
curves[int(i + 1)] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3;
|
||||
curves[int(i + 2)] = tmp1x * pre4 + tmp2x * pre5;
|
||||
curves[int(i + 3)] = tmp1y * pre4 + tmp2y * pre5;
|
||||
curves[int(i + 4)] = tmp2x * pre5;
|
||||
curves[int(i + 5)] = tmp2y * pre5;
|
||||
}
|
||||
var subdiv1:Number = 1 / BEZIER_SEGMENTS, subdiv2:Number = subdiv1 * subdiv1, subdiv3:Number = subdiv2 * subdiv1;
|
||||
var pre1:Number = 3 * subdiv1, pre2:Number = 3 * subdiv2, pre4:Number = 6 * subdiv2, pre5:Number = 6 * subdiv3;
|
||||
var tmp1x:Number = -cx1 * 2 + cx2, tmp1y:Number = -cy1 * 2 + cy2, tmp2x:Number = (cx1 - cx2) * 3 + 1, tmp2y:Number = (cy1 - cy2) * 3 + 1;
|
||||
var dfx:Number = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy:Number = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3;
|
||||
var ddfx:Number = tmp1x * pre4 + tmp2x * pre5, ddfy:Number = tmp1y * pre4 + tmp2y * pre5;
|
||||
var dddfx:Number = tmp2x * pre5, dddfy:Number = tmp2y * pre5;
|
||||
|
||||
public function getCurvePercent (frameIndex:int, percent:Number) : Number {
|
||||
var curveIndex:int = frameIndex * 6;
|
||||
var dfx:Number = curves[curveIndex];
|
||||
if (dfx == LINEAR)
|
||||
return percent;
|
||||
if (dfx == STEPPED)
|
||||
return 0;
|
||||
var dfy:Number = curves[int(curveIndex + 1)];
|
||||
var ddfx:Number = curves[int(curveIndex + 2)];
|
||||
var ddfy:Number = curves[int(curveIndex + 3)];
|
||||
var dddfx:Number = curves[int(curveIndex + 4)];
|
||||
var dddfy:Number = curves[int(curveIndex + 5)];
|
||||
var x:Number = dfx;
|
||||
var y:Number = dfy;
|
||||
var i:int = BEZIER_SEGMENTS - 2;
|
||||
while (true) {
|
||||
if (x >= percent) {
|
||||
var prevX:Number = x - dfx;
|
||||
var prevY:Number = y - dfy;
|
||||
return prevY + (y - prevY) * (percent - prevX) / (x - prevX);
|
||||
}
|
||||
if (i == 0)
|
||||
break;
|
||||
i--;
|
||||
var i:int = frameIndex * BEZIER_SIZE;
|
||||
var curves:Vector.<Number> = this.curves;
|
||||
curves[int(i++)] = BEZIER;
|
||||
|
||||
var x:Number = dfx, y:Number = dfy;
|
||||
for (var n:int = i + BEZIER_SIZE - 1; i < n; i += 2) {
|
||||
curves[i] = x;
|
||||
curves[int(i + 1)] = y;
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
ddfx += dddfx;
|
||||
@ -114,6 +87,31 @@ public class CurveTimeline implements Timeline {
|
||||
x += dfx;
|
||||
y += dfy;
|
||||
}
|
||||
}
|
||||
|
||||
public function getCurvePercent (frameIndex:int, percent:Number) : Number {
|
||||
var curves:Vector.<Number> = this.curves;
|
||||
var i:int = frameIndex * BEZIER_SIZE;
|
||||
var type:Number = curves[i];
|
||||
if (type == LINEAR) return percent;
|
||||
if (type == STEPPED) return 0;
|
||||
i++;
|
||||
var x:Number = 0;
|
||||
for (var start:int = i, n:int = i + BEZIER_SIZE - 1; i < n; i += 2) {
|
||||
x = curves[i];
|
||||
if (x >= percent) {
|
||||
var prevX:Number, prevY:Number;
|
||||
if (i == start) {
|
||||
prevX = 0;
|
||||
prevY = 0;
|
||||
} else {
|
||||
prevX = curves[int(i - 2)];
|
||||
prevY = curves[int(i - 1)];
|
||||
}
|
||||
return prevY + (curves[int(i + 1)] - prevY) * (percent - prevX) / (x - prevX);
|
||||
}
|
||||
}
|
||||
var y:Number = curves[int(i - 1)];
|
||||
return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ public class DrawOrderTimeline implements Timeline {
|
||||
if (time >= frames[int(frames.length - 1)]) // Time is after last frame.
|
||||
frameIndex = frames.length - 1;
|
||||
else
|
||||
frameIndex = Animation.binarySearch(frames, time, 1) - 1;
|
||||
frameIndex = Animation.binarySearch1(frames, time) - 1;
|
||||
|
||||
var drawOrder:Vector.<Slot> = skeleton.drawOrder;
|
||||
var slots:Vector.<Slot> = skeleton.slots;
|
||||
|
||||
@ -67,7 +67,7 @@ public class EventTimeline implements Timeline {
|
||||
if (lastTime < frames[0])
|
||||
frameIndex = 0;
|
||||
else {
|
||||
frameIndex = Animation.binarySearch(frames, lastTime, 1);
|
||||
frameIndex = Animation.binarySearch1(frames, lastTime);
|
||||
var frame:Number = frames[frameIndex];
|
||||
while (frameIndex > 0) { // Fire multiple events with the same frame.
|
||||
if (frames[int(frameIndex - 1)] != frame) break;
|
||||
|
||||
@ -83,7 +83,7 @@ public class FfdTimeline extends CurveTimeline {
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
var frameIndex:int = Animation.binarySearch(frames, time, 1);
|
||||
var frameIndex:int = Animation.binarySearch1(frames, time);
|
||||
var frameTime:Number = frames[frameIndex];
|
||||
var percent:Number = 1 - (time - frameTime) / (frames[int(frameIndex - 1)] - frameTime);
|
||||
percent = getCurvePercent(frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2.1
|
||||
*
|
||||
* Copyright (c) 2013, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to install, execute and perform the Spine Runtimes
|
||||
* Software (the "Software") solely for internal use. Without the written
|
||||
* permission of Esoteric Software (typically granted by licensing Spine), you
|
||||
* may not (a) modify, translate, adapt or otherwise create derivative works,
|
||||
* improvements of the Software or develop new applications using the Software
|
||||
* or (b) remove, delete, alter or obscure any trademarks or any copyright,
|
||||
* trademark, patent or other intellectual property or proprietary rights
|
||||
* notices on or in the Software, including any copy thereof. Redistributions
|
||||
* in binary or source form must include this license and terms.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
package spine.animation {
|
||||
import spine.Event;
|
||||
import spine.IkConstraint;
|
||||
import spine.Skeleton;
|
||||
|
||||
public class IkConstraintTimeline extends CurveTimeline {
|
||||
static private const PREV_FRAME_TIME:int = -3;
|
||||
static private const PREV_FRAME_MIX:int = -2;
|
||||
static private const PREV_FRAME_BEND_DIRECTION:int = -1;
|
||||
static private const FRAME_MIX:int = 1;
|
||||
|
||||
public var ikConstraintIndex:int;
|
||||
public var frames:Vector.<Number>; // time, mix, bendDirection, ...
|
||||
|
||||
public function IkConstraintTimeline (frameCount:int) {
|
||||
super(frameCount);
|
||||
frames = new Vector.<Number>(frameCount * 3, true);
|
||||
}
|
||||
|
||||
/** Sets the time, mix and bend direction of the specified keyframe. */
|
||||
public function setFrame (frameIndex:int, time:Number, mix:Number, bendDirection:int) : void {
|
||||
frameIndex *= 3;
|
||||
frames[frameIndex] = time;
|
||||
frames[int(frameIndex + 1)] = mix;
|
||||
frames[int(frameIndex + 2)] = bendDirection;
|
||||
}
|
||||
|
||||
override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector.<Event>, alpha:Number) : void {
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
var ikConstraint:IkConstraint = skeleton.ikConstraints[ikConstraintIndex];
|
||||
|
||||
if (time >= frames[int(frames.length - 3)]) { // Time is after last frame.
|
||||
ikConstraint.mix += (frames[int(frames.length - 2)] - ikConstraint.mix) * alpha;
|
||||
ikConstraint.bendDirection = int(frames[int(frames.length - 1)]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
var frameIndex:int = Animation.binarySearch(frames, time, 3);
|
||||
var prevFrameMix:Number = frames[int(frameIndex + PREV_FRAME_MIX)];
|
||||
var frameTime:Number = frames[frameIndex];
|
||||
var percent:Number = 1 - (time - frameTime) / (frames[int(frameIndex + PREV_FRAME_TIME)] - frameTime);
|
||||
percent = getCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||
|
||||
var mix:Number = prevFrameMix + (frames[int(frameIndex + FRAME_MIX)] - prevFrameMix) * percent;
|
||||
ikConstraint.mix += (mix - ikConstraint.mix) * alpha;
|
||||
ikConstraint.bendDirection = int(frames[int(frameIndex + PREV_FRAME_BEND_DIRECTION)]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<actionScriptProperties analytics="false" mainApplicationPath="Main.as" projectUUID="164cbec6-d4e8-44d9-9ac1-41bccc772b2e" version="11">
|
||||
<compiler additionalCompilerArguments="-locale en_US" advancedTelemetry="false" autoRSLOrdering="true" copyDependentFiles="true" fteInMXComponents="false" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="true" htmlHistoryManagement="true" htmlPlayerVersionCheck="true" includeNetmonSwc="false" outputFolderPath="bin-debug" removeUnusedRSL="true" sourceFolderPath="src" strict="true" targetPlayerVersion="0.0.0" useApolloConfig="false" useDebugRSLSwfs="true" useFlashSDK="true" verifyDigests="true" warn="true">
|
||||
<actionScriptProperties analytics="false" mainApplicationPath="spine/Main.as" projectUUID="164cbec6-d4e8-44d9-9ac1-41bccc772b2e" version="10">
|
||||
<compiler additionalCompilerArguments="-locale en_US" autoRSLOrdering="true" copyDependentFiles="true" fteInMXComponents="false" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="true" htmlHistoryManagement="true" htmlPlayerVersionCheck="true" includeNetmonSwc="false" outputFolderPath="bin-debug" removeUnusedRSL="true" sourceFolderPath="src" strict="true" targetPlayerVersion="0.0.0" useApolloConfig="false" useDebugRSLSwfs="true" verifyDigests="true" warn="true">
|
||||
<compilerSourcePath/>
|
||||
<libraryPath defaultLinkType="0">
|
||||
<libraryPathEntry kind="4" path="">
|
||||
@ -34,10 +34,9 @@
|
||||
<sourceAttachmentPath/>
|
||||
</compiler>
|
||||
<applications>
|
||||
<application path="Main.as"/>
|
||||
<application path="spine/Main.as"/>
|
||||
</applications>
|
||||
<modules/>
|
||||
<workers/>
|
||||
<buildCSSFiles/>
|
||||
<flashCatalyst validateFlashCatalystCompatibility="false"/>
|
||||
</actionScriptProperties>
|
||||
|
||||
251
spine-starling/spine-starling-example/src/raptor.atlas
Normal file
251
spine-starling/spine-starling-example/src/raptor.atlas
Normal file
@ -0,0 +1,251 @@
|
||||
|
||||
raptor.png
|
||||
size: 1022,1022
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
back_arm
|
||||
rotate: false
|
||||
xy: 410, 545
|
||||
size: 46, 29
|
||||
orig: 46, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_bracer
|
||||
rotate: false
|
||||
xy: 540, 548
|
||||
size: 39, 28
|
||||
orig: 39, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_hand
|
||||
rotate: true
|
||||
xy: 504, 538
|
||||
size: 36, 34
|
||||
orig: 36, 34
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_knee
|
||||
rotate: false
|
||||
xy: 299, 478
|
||||
size: 49, 67
|
||||
orig: 49, 67
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
back_thigh
|
||||
rotate: true
|
||||
xy: 140, 247
|
||||
size: 39, 24
|
||||
orig: 39, 24
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
eyes_open
|
||||
rotate: true
|
||||
xy: 2, 2
|
||||
size: 47, 45
|
||||
orig: 47, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_arm
|
||||
rotate: false
|
||||
xy: 360, 544
|
||||
size: 48, 30
|
||||
orig: 48, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_bracer
|
||||
rotate: false
|
||||
xy: 538, 578
|
||||
size: 41, 29
|
||||
orig: 41, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_hand
|
||||
rotate: false
|
||||
xy: 538, 609
|
||||
size: 41, 38
|
||||
orig: 41, 38
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_open_hand
|
||||
rotate: false
|
||||
xy: 894, 782
|
||||
size: 43, 44
|
||||
orig: 43, 44
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
front_thigh
|
||||
rotate: false
|
||||
xy: 942, 849
|
||||
size: 57, 29
|
||||
orig: 57, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun
|
||||
rotate: false
|
||||
xy: 785, 774
|
||||
size: 107, 103
|
||||
orig: 107, 103
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
gun_nohand
|
||||
rotate: false
|
||||
xy: 614, 703
|
||||
size: 105, 102
|
||||
orig: 105, 102
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
head
|
||||
rotate: false
|
||||
xy: 2, 137
|
||||
size: 136, 149
|
||||
orig: 136, 149
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
lower_leg
|
||||
rotate: true
|
||||
xy: 780, 699
|
||||
size: 73, 98
|
||||
orig: 73, 98
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
mouth_smile
|
||||
rotate: true
|
||||
xy: 49, 2
|
||||
size: 47, 30
|
||||
orig: 47, 30
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
neck
|
||||
rotate: true
|
||||
xy: 1001, 860
|
||||
size: 18, 21
|
||||
orig: 18, 21
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_arm_back
|
||||
rotate: false
|
||||
xy: 940, 936
|
||||
size: 82, 86
|
||||
orig: 82, 86
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_body
|
||||
rotate: false
|
||||
xy: 2, 737
|
||||
size: 610, 285
|
||||
orig: 610, 285
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_front_arm
|
||||
rotate: true
|
||||
xy: 195, 464
|
||||
size: 81, 102
|
||||
orig: 81, 102
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_front_leg
|
||||
rotate: false
|
||||
xy: 2, 478
|
||||
size: 191, 257
|
||||
orig: 191, 257
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_hindleg_back
|
||||
rotate: false
|
||||
xy: 614, 807
|
||||
size: 169, 215
|
||||
orig: 169, 215
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_horn
|
||||
rotate: false
|
||||
xy: 360, 655
|
||||
size: 182, 80
|
||||
orig: 182, 80
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_horn_back
|
||||
rotate: false
|
||||
xy: 360, 576
|
||||
size: 176, 77
|
||||
orig: 176, 77
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_jaw
|
||||
rotate: false
|
||||
xy: 785, 879
|
||||
size: 153, 143
|
||||
orig: 153, 143
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_noshadow
|
||||
rotate: false
|
||||
xy: 2, 288
|
||||
size: 163, 188
|
||||
orig: 163, 188
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_strap_front
|
||||
rotate: false
|
||||
xy: 721, 710
|
||||
size: 57, 95
|
||||
orig: 57, 95
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_strap_rear
|
||||
rotate: true
|
||||
xy: 940, 880
|
||||
size: 54, 74
|
||||
orig: 54, 74
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_saddle_w_shadow
|
||||
rotate: false
|
||||
xy: 195, 547
|
||||
size: 163, 188
|
||||
orig: 163, 188
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
raptor_tongue
|
||||
rotate: true
|
||||
xy: 544, 649
|
||||
size: 86, 64
|
||||
orig: 86, 64
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_back
|
||||
rotate: false
|
||||
xy: 458, 539
|
||||
size: 44, 35
|
||||
orig: 44, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_front
|
||||
rotate: true
|
||||
xy: 81, 4
|
||||
size: 45, 50
|
||||
orig: 45, 50
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
stirrup_strap
|
||||
rotate: true
|
||||
xy: 894, 828
|
||||
size: 49, 46
|
||||
orig: 49, 46
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
torso
|
||||
rotate: true
|
||||
xy: 610, 647
|
||||
size: 54, 91
|
||||
orig: 54, 91
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
visor
|
||||
rotate: false
|
||||
xy: 2, 51
|
||||
size: 131, 84
|
||||
orig: 131, 84
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
1333
spine-starling/spine-starling-example/src/raptor.json
Normal file
1333
spine-starling/spine-starling-example/src/raptor.json
Normal file
File diff suppressed because one or more lines are too long
BIN
spine-starling/spine-starling-example/src/raptor.png
Normal file
BIN
spine-starling/spine-starling-example/src/raptor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 550 KiB |
@ -1,7 +1,5 @@
|
||||
package {
|
||||
package spine {
|
||||
|
||||
import spine.SkeletonData;
|
||||
import spine.SkeletonJson;
|
||||
import spine.atlas.Atlas;
|
||||
import spine.attachments.AtlasAttachmentLoader;
|
||||
import spine.attachments.AttachmentLoader;
|
||||
@ -1,11 +1,11 @@
|
||||
|
||||
package {
|
||||
package spine {
|
||||
|
||||
import flash.display.Sprite;
|
||||
|
||||
import starling.core.Starling;
|
||||
|
||||
[SWF(width = "640", height = "480", frameRate = "60", backgroundColor = "#dddddd")]
|
||||
[SWF(width = "800", height = "600", frameRate = "60", backgroundColor = "#dddddd")]
|
||||
public class Main extends Sprite {
|
||||
private var _starling:Starling;
|
||||
static public var useStarlingAtlas:Boolean;
|
||||
@ -13,7 +13,8 @@ public class Main extends Sprite {
|
||||
public function Main () {
|
||||
var example:Class;
|
||||
//example = SpineboyExample;
|
||||
example = GoblinsExample;
|
||||
//example = GoblinsExample;
|
||||
example = RaptorExample;
|
||||
useStarlingAtlas = true;
|
||||
|
||||
_starling = new Starling(example, stage);
|
||||
@ -0,0 +1,62 @@
|
||||
package spine {
|
||||
|
||||
import spine.atlas.Atlas;
|
||||
import spine.attachments.AtlasAttachmentLoader;
|
||||
import spine.attachments.AttachmentLoader;
|
||||
import spine.starling.SkeletonAnimation;
|
||||
import spine.starling.StarlingAtlasAttachmentLoader;
|
||||
import spine.starling.StarlingTextureLoader;
|
||||
|
||||
import starling.core.Starling;
|
||||
import starling.display.Sprite;
|
||||
import starling.events.Touch;
|
||||
import starling.events.TouchEvent;
|
||||
import starling.events.TouchPhase;
|
||||
import starling.textures.Texture;
|
||||
import starling.textures.TextureAtlas;
|
||||
|
||||
public class RaptorExample extends Sprite {
|
||||
[Embed(source = "raptor.json", mimeType = "application/octet-stream")]
|
||||
static public const RaptorJson:Class;
|
||||
|
||||
[Embed(source = "raptor.atlas", mimeType = "application/octet-stream")]
|
||||
static public const RaptorAtlas:Class;
|
||||
|
||||
[Embed(source = "raptor.png")]
|
||||
static public const RaptorAtlasTexture:Class;
|
||||
|
||||
private var skeleton:SkeletonAnimation;
|
||||
private var gunGrabbed:Boolean;
|
||||
|
||||
public function RaptorExample () {
|
||||
var attachmentLoader:AttachmentLoader;
|
||||
var spineAtlas:Atlas = new Atlas(new RaptorAtlas(), new StarlingTextureLoader(new RaptorAtlasTexture()));
|
||||
attachmentLoader = new AtlasAttachmentLoader(spineAtlas);
|
||||
|
||||
var json:SkeletonJson = new SkeletonJson(attachmentLoader);
|
||||
json.scale = 0.5;
|
||||
var skeletonData:SkeletonData = json.readSkeletonData(new RaptorJson());
|
||||
|
||||
skeleton = new SkeletonAnimation(skeletonData, true);
|
||||
skeleton.x = 320;
|
||||
skeleton.y = 560;
|
||||
skeleton.state.setAnimationByName(0, "walk", true);
|
||||
|
||||
addChild(skeleton);
|
||||
Starling.juggler.add(skeleton);
|
||||
|
||||
addEventListener(TouchEvent.TOUCH, onClick);
|
||||
}
|
||||
|
||||
private function onClick (event:TouchEvent) : void {
|
||||
var touch:Touch = event.getTouch(this);
|
||||
if (touch && touch.phase == TouchPhase.BEGAN) {
|
||||
if (gunGrabbed)
|
||||
skeleton.skeleton.setToSetupPose();
|
||||
else
|
||||
skeleton.state.setAnimationByName(1, "gungrab", false);
|
||||
gunGrabbed = !gunGrabbed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,5 @@
|
||||
package {
|
||||
package spine {
|
||||
|
||||
import spine.Event;
|
||||
import spine.SkeletonData;
|
||||
import spine.SkeletonJson;
|
||||
import spine.animation.AnimationStateData;
|
||||
import spine.atlas.Atlas;
|
||||
import spine.attachments.AtlasAttachmentLoader;
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 253 KiB |
Loading…
x
Reference in New Issue
Block a user