Hi, all
During the development of the last game (http://www.stargamm.com/?
p=34), I used animated models for my characters. I needed to use more
than one animation to the model at the same time.
I want to discuss a solution that I found.
I am sure that my decision is not optimal and I present it as an
occasion to discuss this issue.
Here I try to explain the principle, skipping standard code for
creation scene, etc.
For convenience, I need the class <strong>CharacterAnimation</strong>,
in which we will post the entire functionality for working with the
animation model.
<pre>
public class CharacterAnimation
{
// current frame of the animation
private var animationTime:Number;
// our model
private var animationSrc:ObjectContainer3D;
// actual animation
private var animation:BonesAnimator;
// array of bones, movement of which we need to mix
private var blendBoneList:Array=[];
// array for storing bones transform.matrix3D
private var blendCurrentState:Array=[];
private var blendFinState:Array=[];
private var isBlend:Boolean=false;
private var blendPercent:Number=0.5;
private var fps:Number=25.2;
private var startFrame:int;
private var stopFrame:int;
private var loop:Boolean;
public function CharacterAnimation(animationSrc:ObjectContainer3D,
startFrame:int, stopFrame:int, loop:Boolean=true,
isBlend:Boolean=false )
{
this.animationSrc=animationSrc;
this.startFrame=startFrame;
this.stopFrame=stopFrame;
this.loop=loop;
this.isBlend = isBlend;
// use default animation
this.animation =
this.animationSrc.animationLibrary.getAnimation("default").animation
as BonesAnimator;
animation.loop=this.loop;
animationTime.time = startFrame;
}
// This function must be called before scene render
public function sync():void
{
// increment animation frame
animationTime +=1;
if (animationTime >= stopFrame)
{
if(this.loop)
animationTime = startFrame;
else
{
// stop();
return;
}
}
// calc the time for the current frame
var ts:Number=animationTime /fps;
// The usual animation, do not blend movement
if(!isBlend)
animation.update(ts,false);
else
{
// trying to blend a movement of the bones
// reset variables
blendBoneList=[];
blendCurrentState=[];
blendFinState=[];
// in an array blendCurrentState put current
transformation matrix
of bones
getBlendBonesState(animationSrc,blendBoneList,blendCurrentState);
// play the current frame of animation, after the
method call
transformation matrix changed)
animation.update(ts,false);
// in an array blendFinState put current transformation
matrix of
bones
getBlendBonesState(animationSrc,null,blendFinState);
// blend movement
blendPercent=0.5;
setBlendState();
}
}
// method sets the transformation matrix of bone as the interpolation
of two matrices
private function setBlendState():void
{
// loop through bones
for(var i:int=0;i< blendBoneList.length;i++)
{
var b:Bone=animationSrc.getBoneByName(blendBoneList[i]);
if(b==null) continue;
// set the transformation matrix of bone
b.transform.matrix3D=Matrix3D.interpolate(
Matrix3D(blendCurrentState[i]),
Matrix3D(blendFinState[i]),
blendPercent);
}
// here should be update of skinControllers and skinVertices
of the
model,
// ie part of the BonesAnimator.update ().
// In my case I had override this method,
// here I will cite only the part of the BonesAnimator.update
()
method
//update skincontrollers
for each (_skinController in _skinControllers)
_skinController.update();
//update skinvertices
for each (_skinVertex in _skinVertices)
_skinVertex.update();
}
// method fills an array of bone transformation matrix model.
private function
getBlendBonesState(src:ObjectContainer3D,boneList:Array,stateList:Array):void
{
for each(var ch:Object3D in src.children)
{
if(ch is Bone)
{
var b:Bone=ch as Bone;
if(boneList!=null)
boneList.push(b.name);
stateList.push(b.transform.matrix3D);
}
if(ch is ObjectContainer3D || ch is Bone)
getBlendBonesState((ch as
ObjectContainer3D),boneList,stateList);
}
}
}
</pre>
My model and its animation are stored in the file “model.dae”. I load
it from an embedded resource and put on the scene.
<pre>
[Embed(source="//..//assets//models//test//
model.dae",mimeType="application/octet-stream")]
public static var actorModelSrc:Class;
public var model:ObjectContainer3D;
var loader:Collada=new Collada();
model=loader.parseGeometry(new actorModelSrc()) as ObjectContainer3D;
// create two instances of CharacterAnimation
// move forward animation
walkForward=new CharacterAnimation(this.parent, model, 1, 30, true,
false);
// some movement of hand animation
handMotion =new CharacterAnimation(this.parent, model, 34, 50, true,
true);</pre>
We need to update walkForward and handMotion before render scene and
in strict sequence.
<pre>
// enter frame
private function enterFrame(e:Event):void
{
…
walkForward.sync();
handMotion.sync();
…
scene.render();
}</pre>
As a result I have "hand movement" mixed with "walk forward" movement.