I made this player controller for a game concept, in a nut shell the game plays like the Diablo and Torchlight series. Its built for a fixed camera 3rd person game. Although I see no reason it couldn’t be used with other 3rd person camera types (but not OTS). Used this script to get started: http://wiki.unity3d.com/index.php/RigidbodyFPSWalker. Its doesn’t give an author so that thanks to whoever wrote that. It also has a method for switching between idle, walk, and run animations based off speed. It consists of 4 Classes. MousePosition() goes on the player camera, and the other 3 go on the player character. This is free for anyone to use, and if you have an improvement or suggestion let me know.
This class needs a 3rd person camera like: http://wiki.unity3d.com/index.php/FollowTrackingCamera
Make sure you check ‘Freeze rotation x,z’ on the rigidbody or you will get strange results.
If you don’t have animations just comment out LateUpdate() in PlayerController().
Main Class(goes on player character):
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (Rigidbody))]
[RequireComponent (typeof (CapsuleCollider))]
/// <summary>
/// Player controller.
/// This is the main class for the physics based 3rd person player controller.
/// This class handels everything that requires Update(), LateUpdate(), and all physics methods.
/// This class calls PlayerAnim() to handle the animation states required for walking/running.
/// This class calls PlayerLocomotion() to handle all the applied force to let the player move.
/// </summary>
public class PlayerController : MonoBehaviour {
#region variables
//Caches the Transform data to speed things up.
private Transform myTransform;
//Initializes mouseWorldPosition, gets updated in PlayerLocomotion.Rotator().
private Vector3 mouseWorldPosition = Vector3.zero; //ref
//True if the player is touching the ground.
private bool grounded = false;
#endregion
// Use this for initialization
void Awake ()
{
myTransform = transform;
animation.Play("idle");
}
// Update is called once per frame
void Update ()
{
//updates the player's rotation to face the mouse.
PlayerLocomotion playerLocomotion = GetComponent<PlayerLocomotion>();
playerLocomotion.Rotator(ref mouseWorldPosition, myTransform);
}
void FixedUpdate ()
{
//this section handles walking/running and jumping.
PlayerLocomotion playerLocomotion = GetComponent<PlayerLocomotion>();
playerLocomotion.Walker(myTransform, mouseWorldPosition);
playerLocomotion.Jumper(myTransform, grounded);
//If the playerr is not colliding with floor then gorunded is false.
grounded = false;
}
void LateUpdate()
{
//This handles the animations for walking/running.
PlayerAnim playerAnim = GetComponent<PlayerAnim>();
playerAnim.WalkThresholds();
}
void OnCollisionStay (Collision collisionInfo) {
//if the player is colliding with the floor then grounded is true.
grounded = true;
}
}
Animation Class(goes on player character):
/// Player animation.
/// This class handles the animation mathods.
/// CalcSpeed() stores the player's vector3 from the last frame and compares it to the player's current vector3. It calculates the magnitude of the difference and returns a float speed.
/// WalkThresholds() uses speed from CalcSpeed() to judge which animation to use; idle, walk, run.
/// AnimState() is a finite state machine that handles the current move animtation.
///
/// </summary>
public class PlayerAnim : MonoBehaviour {
private enum AnimStates
{
idle,
walk,
run
}
AnimStates animstates = AnimStates.idle;
private Vector3 lastPosition = Vector3.zero;
//the velocity that triggers the walk animatoin.
public float walkThreshold;
//the velocity that triggers the run animation.
public float runThreshold;
//used to make the speed check less sensitive to change, by controlling how many consecutive checks have to reach a threshold to trigger an animation change.
private uint it = 0, wt = 0, rt=0;
private float playerSpeed = 0.0f;
//how many concecutive frames must happen to trigger a speed based animation change.
const int ANIM_THRESHOLD = 2;
public void WalkThresholds()
{
if(!animation.IsPlaying("jump"))
{
playerSpeed = CalcSpeed(ref lastPosition);
//if the player is not moving then play idle
if(playerSpeed < walkThreshold)
{
it++;
if(it >= ANIM_THRESHOLD)
{
wt=0;
rt=0;
animstates = AnimStates.idle;
AnimState(animstates);
}
}
//if player is moving but not running speed.
else if(walkThreshold < playerSpeed playerSpeed < runThreshold)
{
wt++;
if(wt >= ANIM_THRESHOLD)
{
it=0;
rt=0;
animstates = AnimStates.walk;
AnimState(animstates);
}
}
//if player is moving at running speeds.
else if(playerSpeed > runThreshold)
{
rt++;
if(rt >= ANIM_THRESHOLD)
{
it=0;
wt=0;
animstates = AnimStates.run;
AnimState(animstates);
}
}
}
}
private void AnimState(AnimStates tmp)
{
animation.wrapMode = WrapMode.Loop;
switch(tmp)
{
case AnimStates.idle:
animation.CrossFade("idle", 0.2f, PlayMode.StopAll);
//Debug.Log(tmp);
break;
case AnimStates.walk:
animation.CrossFade("walk", 0.2f, PlayMode.StopAll);
//Debug.Log(tmp);
break;
case AnimStates.run:
animation.CrossFade("run", 0.2f, PlayMode.StopAll);
//Debug.Log(tmp);
break;
default:
break;
}
}
private float CalcSpeed(ref Vector3 lPosition)
{
float speed = ((transform.position - lPosition).magnitude/ Time.deltaTime);
lastPosition = transform.position;
return speed;
}
}
Locomotion Class(Goes on player)
/// <summary>
/// Player locomotion.
/// This class handles all thier applied force tat moves the player as well as the rotator that turns the player to face the mouse.
/// </summary>
public class PlayerLocomotion : MonoBehaviour {
//this is the player camera for making the reference to MousePosition()
public GameObject playerCamera;
//how fast can the player turn
public float rotationSpeed;
//how much force to apply to move.
public float moveForce;
//the maximum velocity change
public float maxVelocityChange = 10.0f;
//how high can the player jump
public float jumpHeight;
//gravity while jumping
private float gravity = 9.81f;
//This rotates the player to face the mouse position, and also locks the x and z axis.
public void Rotator(ref Vector3 mouseWorldPosition, Transform myTransform)
{
MousePosition mousePosition = playerCamera.GetComponent<MousePosition>();
mouseWorldPosition = mousePosition.mouseToWorld(mouseWorldPosition);
myTransform.rotation = Quaternion.Slerp(myTransform.rotation, Quaternion.LookRotation(mouseWorldPosition - myTransform.position), rotationSpeed * Time.deltaTime);
myTransform.localEulerAngles = new Vector3(0, myTransform.localEulerAngles.y, 0);
}
//Moves the player local forward.
public void Walker(Transform myTransform, Vector3 mouseWorldPosition)
{
float distance = Vector3.Distance(myTransform.position, mouseWorldPosition);
if(Input.GetMouseButton(0) distance > 0.5f)
{
// Calculate how fast we should be moving
Vector3 targetVelocity = (mouseWorldPosition - myTransform.position);
//targetVelocity = transform.TransformDirection(targetVelocity);
targetVelocity *= moveForce;
// Apply a force that attempts to reach our target velocity
Vector3 velocity = myTransform.rigidbody.velocity;
Vector3 velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
myTransform.rigidbody.AddForce(velocityChange, ForceMode.VelocityChange);
}
}
public void Jumper(Transform myTransform, bool grounded)
{
Vector3 velocity = myTransform.rigidbody.velocity;
if (grounded Input.GetKeyUp(KeyCode.Space))
{
animation.wrapMode = WrapMode.Once;
animation.CrossFade("jump", 0.2f, PlayMode.StopAll);
rigidbody.velocity = new Vector3(velocity.x, CalculateJumpVerticalSpeed(), velocity.z);
}
}
private float CalculateJumpVerticalSpeed ()
{
// From the jump height and gravity we deduce the upwards speed
// for the character to reach at the apex.
return Mathf.Sqrt(2 * jumpHeight * gravity);
}
}
and finally mouse position class(goes on player camera.)
using UnityEngine;
using System.Collections;
/// <summary>
/// Mouse position.
///
/// this class handles getting the location in real space the mouse cursor is over.
///
/// </summary>
public class MousePosition: MonoBehaviour {
public Vector3 mouseToWorld(Vector3 oldMousePos)
{
Vector2 mousePos = Input.mousePosition;
Vector3 worldPos;
RaycastHit hit;
bool hitSomething;
Ray ray = camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0));
Debug.DrawRay(ray.origin, ray.direction * 10, Color.yellow);
hitSomething = Physics.Raycast(ray, out hit, Mathf.Infinity);
if(hitSomething == true)
{
worldPos = hit.point;
return worldPos;
}
else
{
return oldMousePos;
}
}
}
Suggested settings: