- Home /
Confusion about animation blending, weights
I'm attempting to use animation weights to blend some animations, instead of using the locomotion system.
I understand the general concept I suppose, that the weights of all currently enabled animation clips should add up to 1.0 (right?), so if i for instance, enabled a walk and a run animation, and set them to both have 0.5 weight, it should be a blend of half the walk and half the run?
Basically, what I'm trying to do is dynamically play a walk forward and a strafe animation based on the character's forward and sideways velocity (so that when the player is moving forward at full speed, the walk animation should be at 100%, and when the player is moving sideways at full speed, the side-strafe animation should be at 100%. Therefore, if he's moving diagonally, they should be at some combination in between so it looks like hes side-stepping forward diagonally).
The speed calculations were easy, with the CharacterController velocity and some dot products with transform.forward and transform.right, now I just don't know how to blend the weights smoothly and also smoothly blend back to the idle animation when he's not moving.
This is about as far as I've gotten, save some failed experiments. Note that I'm not currently worried about the reverses (walk backward, strafe left), I just wanted to get the blending worked out before I started adding more states:
public var maxForwardSpeed : float = 5.0;
public var maxSidewaysSpeed : float = 3.0;
private var myController : CharacterController;
function Start(){
myController = GetComponent(CharacterController);
animation["walk"].enabled = true;
animation["walk"].weight = 0;
animation["strafeRight"].enabled = true;
animation["strafeRight"].weight = 0;
}
function Update(){
var charVelocity = myController.velocity;
charVelocity.y = 0;
var forwardMotion = Vector3.Dot(transform.forward, charVelocity);
var sidewaysMotion = Vector3.Dot(transform.right, charVelocity);
animation[walk].weight = forwardMotion/maxForwardSpeed;
animation["strafeRight"].weight = sidewaysMotion/maxSidewaysSpeed;
}
The example in the docs about leaning doesn't really help me, I don't quite understand the need to use normalizedTime instead of just changing the weights.
Answer by PrimeDerektive · Jun 28, 2011 at 12:48 PM
I figured this out, also added in the functionality to blend in a run animation when the forward speed is greater than 50%; the math was a little funky to figure out the curve. I'm also controlling the animation playback speeds based on the acceleration/velocity, it results in some really nice looking animation:
public var maxForwardSpeed : float = 5.0;
public var maxSidewaysSpeed : float = 3.0;
private var myController : CharacterController;
function Start(){
//in my setup, the animated player model is a child object of the controller
myController = transform.parent.GetComponent(CharacterController);
animation["walk"].enabled = true;
animation["walk"].weight = 0;
animation["walk"].layer = 1;
animation["run"].enabled = true;
animation["run"].weight = 0;
animation["run"].layer = 1;
animation["strafeRight"].enabled = true;
animation["strafeRight"].weight = 0;
animation["strafeRight"].layer = 1;
animation["idle"].enabled = true;
animation["idle"].weight = 0;
animation["idle"].layer = 1;
animation.SyncLayer(1);
}
function Update(){
var charVelocity = myController.velocity;
charVelocity.y = 0;
var forwardMotion = Vector3.Dot(transform.forward, charVelocity);
var sidewaysMotion = Vector3.Dot(transform.right, charVelocity);
var v = forwardMotion/maxForwardSpeed;
v *= 2;
var walkWeight = 1.0 - Mathf.Abs(v - 1.0);
var runWeight = Mathf.Max(0.0, v - 1.0);
var strafeWeight = sidewaysMotion/maxSidewaysSpeed;
animation["walk"].weight = walkWeight;
animation["run"].weight = runWeight;
animation["strafeRight"].weight = strafeWeight;
animation["idle"].weight = 1.0 - walkWeight - runWeight - strafeWeight;
animation["walk"].speed = forwardMotion/(maxForwardSpeed/2);
animation["run"].speed = forwardMotion/maxForwardSpeed;
animation["strafeRight"].speed = sidewaysMotion/maxSidewaysSpeed;
}
If you want to add walking in reverse and strafing left, just use the absolute value of forwardMotion/sidewaysMotion, but make sure you save a boolean or something first as to whether or not it was negative, and then depending on that boolean, multiply the appropriate animation's speed by -1.
one question please: are you using "simplemove" for the "sidewaysmotion" calculation? I can't figure out what sort of motion you use to calculate speeds and weights, and if you're using "simplemove" is it a single call or a simplemove call for 'forward' and one for 'strafe'?
thanks
Answer by Owen-Reynolds · Jun 28, 2011 at 06:39 AM
Animations need to be on separate layers to play at the same time (to blend them.) The weights don't need to add to 1. They are passed down from higher layer to lower. Each gets that much of the remainder, or all that's left if on the bottom.
Ex:
animation["strafeRight"].layer = 2; // in Start. Can now mix strafe and walk/idle
// "walk" and "idle" still on layer 0
animation.Play("walk");
animation.Play("strafeRight");
// Update:
// cheap way to get 0-1 weight based on sideSpeed:
animation["strafeRight"].weight = Mathf.Abs( Input.getAxis("Horizontal") );
// NOTE: walk/idle auto-gets the rest. No need to set weight
// "standard" fade from idle/walk. Same layer, so playing one turns off the other
if( notmoving ) animation.CrossFade("idle");
else animation.CrossFade("walk");
// NOTE: strafeRight is always playing, but at weight 0 if you aren't strafing
I am very familiar with the animation system in general, and crossfading is not what I want. I want full control over the blend weights of all my animations, not to only blend in a single animation at a time with crossfade. your "cheap way to get 0-1 weight based on sideSpeed" is wrong, because my character accelerates to reach full movement speed, he doesn't just snap to full speed when the movement keys are held down. Lastly, you're wrong about animations needing to be on separate layers to blend. See the docs on AnimationState.weight, and Animation.SyncLayer(). I figured this out myself, see my own answer.
The Horizontal/Vertical Axis vars give a smooth ramp as you hold arrow keys -- they are written not to snap. The weight doc specifically uses different layers. I didn't realize that using enable w/o Play could force same layer blending. CrossFade is good to have idle ramp up when you stop moving, otherwise you begin tapping your foot as you slow down (but it assumes you are using the suggested diff-layer approach.)
While Input.GetAxis() does have a smoothing filter so it does "ramp" a bit (imo, it is negligible when used with keypresses), my point was that it would have no real "intelligence" as to my character's actual speed, maximum speed and acceleration, etc.
Answer by ddd.du · Jun 28, 2011 at 12:47 AM
i suggest you just create a "side-stepping forward diagonally" animation like what bootcamp demo did, they created an animation for each,its looks like hard but it works for me. I also added characterstate there.
Your answer
Follow this Question
Related Questions
Play 2 animations at once 4 Answers
Animation blending. 2 Answers
What am I not getting about animation weight blending? 4 Answers
Animation events play for all animations when blending? 1 Answer
Issue with animation 1 Answer