Wrong skeleton positions when crossfading from walk to idle

Hi there!

I hope this is the correct section to explain my problem.

I have imported a character with 3 animation cycle (idle, walk and run) from 3dsMax. All the animations work pretty well as long as I play them one at a time. The problem comes when I crossfade from an animation to another. In particular when I crossfade from walk (or run) to idle: the walk (run) animation does not finish its cycle and when the idle animation starts the arms of my character are in a wrong position (they don’t come to the “home” position, but the rest of the body does).

Home Position:

Wrong Arms Position:

Since I thought the problem was not in the animations themselves, I wrote a script to fix the problem.

The script works basically like this:

in the Start() function the script visits the character Bip hierarchy and stores the rotation and position of every single node in the character skeleton.

Then, every time the character crossfades to idle, the script visits the character Bip hierachy again and replaces the rotation and position values of every single node with the stored ones.

I created this script to be a temporary solution, but since then I was not able to find a better way to deal with this problem.

Do you know a way to fix this problem?

Thank you :slight_smile:

Can you post the original script (ie, the one with the problem)?

Actually the script is not the problem. Indeed the script works well, but it’s more a work around, not a real solution to my problem. I’m trying to find a more elegant way to deal with this problem. Anyway, the script is the following:

private static var i = 1;
private var nodes = new Array();
private var length = 0;

function Awake() {
	scanChild(transform);
}

function scanChild(node : Transform){
	if(i == 1){
		saveChild(node);
	}
	index = node.childCount;
	if(index > 0){
		for(var child : Transform in node){
			saveChild(child);
			scanChild(child);
			i++;
		}
	}else{
		i = 1;
	}
}

function saveChild(node : Transform){
	length = nodes.push(new Array(node.name, node.transform.localPosition, node.transform.localRotation));
}

function Fix(){
	for(var node : Array in nodes){
		var joint : GameObject = GameObject.Find(node[0]);
		if(joint.transform.localPosition != node[1])
			joint.transform.localPosition = node[1];
		if(joint.transform.localRotation != node[2])
			joint.transform.localRotation = node[2];
	}
}

I didn’t optimized it at all, hoping to find a better solution, but I didn’t find another way around.

Anyway, I attach this script to the neck of the character, and every time the character crossfades to idle, I call the Fix() function, and the arms immediately come back to the right position (ie, they move from the wrong position to the right position in a sigle frame, and it’s pretty ugly to see).

I’m sorry, I forgot to post the scripts that controls the player and the animations

private var walkSpeed : float = 1.0;
private var gravity = 100.0;
private var moveDirection : Vector3 = Vector3.zero;
private var charController : CharacterController;
private var neck : GameObject;

function Start()
{
	charController = GetComponent(CharacterController);
	animation.wrapMode = WrapMode.Loop;
	animation.Stop();
	neck = GameObject.Find("Bip01 Neck");
}

function Update ()
{
	if(charController.isGrounded == true)
	{
		if(Input.GetAxis("Vertical") > .1)
		{
			if(Input.GetButton("Run"))
			{
				animation.CrossFade("run");
				walkSpeed = 2;
			}
			else
			{
				animation["walk"].speed = 1;
				animation.CrossFade("walk");
				walkSpeed = 1;
			}
		}
		else if(Input.GetAxis("Vertical") < -.1)
		{
			animation["walk"].speed = -1;
			animation.CrossFade("walk");
			walkSpeed = 1;
		}
		else
		{
			neck.SendMessage("Fix");
			animation.CrossFade("idle");
		}
		
		if(Input.GetAxis("Horizontal")  !Input.GetAxis("Vertical"))
		{
			animation.CrossFade("walk");
		}
		
		transform.eulerAngles.y += Input.GetAxis("Horizontal");
		
		moveDirection = Vector3(0, 0, Input.GetAxis("Vertical"));
		moveDirection = transform.TransformDirection(moveDirection);
	}
	
	moveDirection.y -= gravity * Time.deltaTime;
	charController.Move(moveDirection * (Time.deltaTime * walkSpeed));
}