Unity 4.0 - Mecanim - (Swimming - problem)

Hello,
I’m beginner in Unity, but i have collected some experience in Unity. And
i startet a little bit with the mecanim tool of Unity. :wink:

But now I have a (for me unsolvable) problem. (i work with the example script) :frowning:

I have downloaded some bvh Files and convert them to fbx. (that was no problem)
I wrote this small code and put it at the end of the BotControllScript (but it is in the public class of the BotControlScript)
and I added a Box Collider on the Water.

	public void OnTriggerEnter(Collider other){
  		if(other.CompareTag ("Water")){
			anim.SetBool("Swim", true);
		}
	}
	
	public void OnTriggerExit(Collider other){
		if(other.CompareTag ("Water")){
			anim.SetBool("Swim", false);
		}
	}

Here is my poor result :mrgreen: :

First silly question:
How can I add a simple movement?

Second silly question:
Why does the animation only starts in the Idle state?

I hope somebody can help me :wink:
kind regards
Leosubaru

I haven’t used the example script your speaking of or bot control script so I’ll take a shot at answering your questions as generally as possible.

Question 1:
Not sure what you mean by this… if you mean add an additional animation you need to drag another animation into the animator window to create another state, Then connect it via a transition that will evaluate as true at some point to trigger the transition…

Question 2:
As in answer 1 I suspect your problem with not going into swimming animation from a run is because you don’t have a transition from your run state to your swim state, Without a transition you won’t be able to leave the run state, regardless of the fact your bool gets set to true.

Hopefully those are the answers you needed.

Oh shit :wink: Thank you. I have really forgotten to make a transition beween run and swim. (Shame on me)
Tomorrow i will check why I’m swimming on on the spot.

If you mean why your swimming in place it is probably because you’ve animated it swimming in place ( in which case you’ll have to move him in code when in that state )
Or if the animation is using root motion (animating through the world) check your bake options. Orientation, y , xz can all be baked independently when your on the import Model / Rig / Animation panel. Make sure xz is off if you want to use the root motion.

The character is animated, swimming in place - that’s my problem :wink:
And I can’t move him :frowning:
My character has a Rigidbody but i can’t move him for example with Rigidbody.AddForce.
I have tried many things… hopy you can help a noob :smile:

Here is the Code of the example. I have only added a swim-state Line 34 and in 44-54 the Trigger.

using UnityEngine;
using System.Collections;

// Require these components when using this script
[RequireComponent(typeof (Animator))]
[RequireComponent(typeof (CapsuleCollider))]
[RequireComponent(typeof (Rigidbody))]
public class BotControlScript : MonoBehaviour
{
	[System.NonSerialized]					
	public float lookWeight;					// the amount to transition when using head look
	
	[System.NonSerialized]
	public Transform enemy;						// a transform to Lerp the camera to during head look
	
	public float animSpeed = 20.5f;				// a public setting for overall animator animation speed
	public float lookSmoother = 3f;				// a smoothing setting for camera motion
	public bool useCurves;						// a setting for teaching purposes to show use of curves

	
	private Animator anim;							// a reference to the animator on the character
	private AnimatorStateInfo currentBaseState;			// a reference to the current state of the animator, used for base layer
	private AnimatorStateInfo layer2CurrentState;	// a reference to the current state of the animator, used for layer 2
	private CapsuleCollider col;					// a reference to the capsule collider of the character
	

	static int idleState = Animator.StringToHash("Base Layer.Idle");	
	static int locoState = Animator.StringToHash("Base Layer.Locomotion");			// these integers are references to our animator's states
	static int jumpState = Animator.StringToHash("Base Layer.Jump");				// and are used to check state for various actions to occur
	static int jumpDownState = Animator.StringToHash("Base Layer.JumpDown");		// within our FixedUpdate() function below
	static int fallState = Animator.StringToHash("Base Layer.Fall");
	static int rollState = Animator.StringToHash("Base Layer.Roll");
	static int waveState = Animator.StringToHash("Layer2.Wave");
	static int swimState = Animator.StringToHash("Base Layer.Swim");
	
	void Start ()
	{
		// initialising reference variables
		anim = GetComponent<Animator>();					  
		col = GetComponent<CapsuleCollider>();				

	}

		public void OnTriggerEnter(Collider other){
  		if(other.CompareTag ("Water")){
			anim.SetBool("Swim", true);
		}
	}
	
	public void OnTriggerExit(Collider other){
		if(other.CompareTag ("Water")){
			anim.SetBool("Swim", false);
		}
	}	
	
	void FixedUpdate ()
	{
		float h = Input.GetAxis("Horizontal");				// setup h variable as our horizontal input axis
		float v = Input.GetAxis("Vertical");				// setup v variables as our vertical input axis
		anim.SetFloat("Speed", v);							// set our animator's float parameter 'Speed' equal to the vertical input axis				
		anim.SetFloat("Direction", h); 						// set our animator's float parameter 'Direction' equal to the horizontal input axis		
		anim.speed = animSpeed;								// set the speed of our animator to the public variable 'animSpeed'
		anim.SetLookAtWeight(lookWeight);					// set the Look At Weight - amount to use look at IK vs using the head's animation
		currentBaseState = anim.GetCurrentAnimatorStateInfo(0);	// set our currentState variable to the current state of the Base Layer (0) of animation
		
		// STANDARD JUMPING
		
		// if we are currently in a state called Locomotion (see line 25), then allow Jump input (Space) to set the Jump bool parameter in the Animator to true
		if (currentBaseState.nameHash == locoState)
		{
			if(Input.GetButtonDown("Jump"))
			{
				anim.SetBool("Jump", true);
			}
		}
		
		// if we are in the jumping state... 
		else if(currentBaseState.nameHash == jumpState)
		{
			//  ..and not still in transition..
			if(!anim.IsInTransition(0))
			{
				if(useCurves)
					// ..set the collider height to a float curve in the clip called ColliderHeight
					col.height = anim.GetFloat("ColliderHeight");
				
				// reset the Jump bool so we can jump again, and so that the state does not loop 
				anim.SetBool("Jump", false);
			}
			
			// Raycast down from the center of the character.. 
			Ray ray = new Ray(transform.position + Vector3.up, -Vector3.up);
			RaycastHit hitInfo = new RaycastHit();
			
			if (Physics.Raycast(ray, out hitInfo))
			{
				// ..if distance to the ground is more than 1.75, use Match Target
				if (hitInfo.distance > 1.75f)
				{
					
					// MatchTarget allows us to take over animation and smoothly transition our character towards a location - the hit point from the ray.
					// Here we're telling the Root of the character to only be influenced on the Y axis (MatchTargetWeightMask) and only occur between 0.35 and 0.5
					// of the timeline of our animation clip
					anim.MatchTarget(hitInfo.point, Quaternion.identity, AvatarTarget.Root, new MatchTargetWeightMask(new Vector3(0, 1, 0), 0), 0.35f, 0.5f);
				}
			}
		}
		
		
		// JUMP DOWN AND ROLL 
		
		// if we are jumping down, set our Collider's Y position to the float curve from the animation clip - 
		// this is a slight lowering so that the collider hits the floor as the character extends his legs
		else if (currentBaseState.nameHash == jumpDownState)
		{
			col.center = new Vector3(0, anim.GetFloat("ColliderY"), 0);
		}
		
		// if we are falling, set our Grounded boolean to true when our character's root 
		// position is less that 0.6, this allows us to transition from fall into roll and run
		// we then set the Collider's Height equal to the float curve from the animation clip
		else if (currentBaseState.nameHash == fallState)
		{
			col.height = anim.GetFloat("ColliderHeight");
		}
		
		// if we are in the roll state and not in transition, set Collider Height to the float curve from the animation clip 
		// this ensures we are in a short spherical capsule height during the roll, so we can smash through the lower
		// boxes, and then extends the collider as we come out of the roll
		// we also moderate the Y position of the collider using another of these curves on line 128
		else if (currentBaseState.nameHash == rollState)
		{
			if(!anim.IsInTransition(0))
			{
				if(useCurves)
					col.height = anim.GetFloat("ColliderHeight");
				
				col.center = new Vector3(0, anim.GetFloat("ColliderY"), 0);
				
			}
		}
		// IDLE
		
		// check if we are at idle, if so, let us Wave!
		else if (currentBaseState.nameHash == idleState)
		{
			if(Input.GetButtonUp("Jump"))
			{
				anim.SetBool("Wave", true);

			}
		}
		// if we enter the waving state, reset the bool to let us wave again in future
		if(layer2CurrentState.nameHash == waveState)
		{
			anim.SetBool("Wave", false);
		}
	}
}

This is more the theory of how to move your character, The actual controller you need to move is in question to me.
I have a similar issue that I’m trying to resolve in this post.
http://forum.unity3d.com/threads/170324-Mecanim-Precise-animation-control.

As I say in that post I was trying to modify rootPosition, with no effect.
I’ve found that if I modify transform.position, It will move my character, and so far hasn’t broken my animation. Please note that I don’t know if this is the correct controller to do this on since I’m new to mecanim.
Regardless of what controller you apply the motion to, the code is the same. I’ll use transform.position in the example

First make sure you have the required variables at the top of your class:

public float swimSpeed = 1.0f;                      // This is your maximum swimming speed
public float swimHeadingSpeed = 1.0f;               // How fast can we turn left /right in water?
public float swimPitchSpeed = 1.0f;                 // How fast can we tilt up / down in water?
public float f = 0;                                 // Forward speed set by movement axis (left stick +vertical axis)
public float v = 0;	                                // Vertical Look / swim axis (right stick vertical axis)
public float h= 0;                                  // Horizontal Look / swim axis (right stick horizontal axis)

Then add the swimState to your Update() loop

//SwimState
if(currentBaseState.nameHash == swimState)
{
    transform.Rotate(Vector3.up, h * swimHeadingSpeed * Time.deltaTime, Space.World);   	// Spin object around world Y axis
    transform.Rotate(Vector3.right, v * swimPitchSpeed * Time.deltaTime, Space.Self);       // Spin object around local x axis
                                                                                            // This rotation setup will make sure you get predictable results and leaves the objects z rotation at 0
                                                                                            
    transform.position += transform.forward * f * speed * Time.deltaTime;                   // Move object along its forward axis a specified amount
}

Note also that this will not stop you from rotating your character upside down in the water, or handle keeping him upright when on the surface of the water. You’ll have to implement some checks to clamp his rotation in these events.
If your character will only ever swim on the surface of the water you can simply remove the 2nd Rotate() line and get rid of the swimPitchSpeed variable.

There may be a more concise way to write this that someone could point out, but I like splitting the axis’ into y and x so you can see whats happening.

After the rotation is set its as simple as adding the forward vector to the position to get him to move forward, you multiply the forward vector by the speed in this case the maxSpeed * the amount given by the input axis by deltaTime so that all numbers are in meters / sec.

As for your rigidbody… It may need to be set to isKinematic for this to work.