Third Person move and Camera scripts (attached)

I have been working on scripts for Third person movement, camera and auto animation changes to make a simple 3rd person character. Much was gleaned from other WOW type scripts posted here, but I could not get them to work, so had to work them out myself, which was good as I now understand how Unity math scripting works.

These two scripts implement a character who:
1 - with right button held down - rotates the entire character right and left, plus moves the camera up and down (orbiting around the character, not tilting)
2 - with both mouse buttons down, it does the above plus moves forward - so you can “drive” the character with the mouse
3- when only the left button is held, the camera does the same vertical move, but the horizontal is orbiting around the character, allowing you to see all angles of the model. If you then move forward, or rotate the model with the right mouse, while the camera is not behind the character, it will swing back behind them at an adjustable rate
4 - mouse scroll wheel zooms the camera in and out
5 - the model will switch from idle to walk or run animations as it stops and starts moving
6 - R will toggle between run and walk mode (between idle and run and idle and walk)
(If your model does not have defined walk, run and idle animations, just don’t set the target and it will not do the animation part)
7 - All the camera movements include collision detection to stay between all objects and the character.

Since everyone’s math level is very different, I have commented all the math to help new folks understand how this all works and so they can alter it more easily. (And make suggestions/corrections to make it work better, I’m hoping.)

This seems to have been requested in different ways on the forums by folks just getting started, so I was hoping this would help get them up and running with a 3rd person character in Unity.

I have three issues that I would like help with if anyone knows why they do this:
1 - I only get the shift R sometimes, not always
2 - When the model first starts, the camera is sometimes at an odd orientation, sometimes not
3 - My biggest - when the scene starts, Unity seems to think the mouse buttons are down. I have to click each one to get normal behavior. Is there anyway to clear this?

See the second script for step by step setup instructions.
(Sorry - I don’t know how to attach the scripts without losing all the indentation)

The scripts: (both tested in Unity 2.6 Pro)

// ThirdPersonWalker.js

// this script adds two things to the default FPS walker
// - It adds movement forward by holding down both mouse keys
// - and it adds auto animation changing between idle, walk and run
// After adding this script to your controller, drag the character model to Target

var speed = 6.0; // standard running speed
var walkSpeed = .8; // walk speed adjust both for your charcter so feet don’t slide on ground
var jumpSpeed = 8.0;
var gravity = 20.0;
var Character : Transform;

private var moveDirection = Vector3.zero;
private var grounded : boolean = false;
// a flag to determine idle vs walking/running (so animations do not get started over and over)
private var walking : boolean = false;
// a flag to init idle animation at beginning
private var startup : boolean = true;
// a flag to determine walking vs running
private var running : boolean = true;

function FixedUpdate() {
if (grounded) {
// We are grounded, so recalculate movedirection directly from axes
moveDirection = new Vector3(Input.GetAxis(“Horizontal”), 0, Input.GetAxis(“Vertical”));

// if both mouse buttons are down, move forward
if (Input.GetMouseButton(0) Input.GetMouseButton(1))
{
moveDirection.z = 1;
}

moveDirection = transform.TransformDirection(moveDirection);
if(running == true){
moveDirection *= speed;
} else {
moveDirection *= walkSpeed;
}

if (Input.GetButton (“Jump”)) {
moveDirection.y = jumpSpeed;
}

// auto toggle between idle and walking animations - based on run / walk switch
if(Character){
// toggle between walk and run with R
if(Input.GetKey(KeyCode.LeftShift) Input.GetKeyDown(KeyCode.R)){
if(running == true) {
running = false;
if(walking == true) Character.animation.CrossFade(“walk”);
} else {
running = true;
if(walking == true) Character.animation.CrossFade(“run”);
}
}
if(startup == true){
startup = false;
Character.animation.Play(“idle”);
}
if((moveDirection == Vector3.zero)(walking == true)){
walking = false;
Character.animation.CrossFade(“idle”);
} else {
if((moveDirection != Vector3.zero)(walking == false)){
walking = true;
if(running == true){
Character.animation.CrossFade(“run”);
} else {
Character.animation.CrossFade(“walk”);
}
}
}
}
}

// Apply gravity
moveDirection.y -= gravity * Time.deltaTime;

// Move the controller
var controller : CharacterController = GetComponent(CharacterController);
var flags = controller.Move(moveDirection * Time.deltaTime);
grounded = (flags CollisionFlags.CollidedBelow) != 0;
}

@script RequireComponent(CharacterController)


// ThirdPersonLook.cs
using UnityEngine;
using System.Collections;

// ThirdPersonLook is used on the Controller with Axes set to ControllerLook and target is the Main Camera

// To make an third person style character from the default FPS character
// Add the FPS Controller prefab to the scene
// Drag the animated character you will be using to the controller, so it is first child of controller, above Graphics and Main Camera
// Adjust the position (mostly height) so your character fits into the graphic capsule (as it shows your collider)
// Uncheck Mesh Renderer on Graphics so it is not drawn
// Add this scrip to your CameraScripts folder
// Replace the mouseLook script in the Controller and Main Camera with this one
// On the controller, drag the Main Camera to target and select Axes Controller Look
// On the Main Camera, drag the Controller to the Target and set Axes to Camera Look
// Replace the FPSWalker script on the controller with the Third Person Controller script
// Try it out
// You may need to comment the Idle / Walk animation changes in the Walker script

[AddComponentMenu(“Camera-Control/Mouse Look”)]
public class ThirdPersonLook : MonoBehaviour {

// select for Controller or Camera, so we only have one script
public enum RotationAxes { ControllerLook = 0, CameraLook = 1}
public RotationAxes axes = RotationAxes.ControllerLook;

// select the other so we have that transform for calculations
public Transform target;

// xand Y mouse sensitivity
public float sensitivityX = 15F;
public float sensitivityY = 10F;

// horizontal rotation limits
public float minimumX = -360F;
public float maximumX = 360F;

// vertical rotation limits
public float minimumY = -90F;
public float maximumY = 90F;

// how close the camera is allowed to objects
public float offsetFromWall = 0.2f;

// Min and max for scroll wheel zoom
public float startDistance = 1.0f;
public float maxDistance = 20;
public float minDistance = 0.01f;

// how fast the camera swings back behind player when they start to move
public float CameraSwingBack = 250.0f;

// how fast the scroll wheel zoom works
public int zoomRate = 40;

// vertical offset so the camera will circle the player’s head
public float CameraYOffset = .6f;

private float desiredDistance;
private float rotationX = 0F;
private float rotationY = 0F;

Quaternion originalRotation;

void Update ()
{
// This section is for the Controller, just to rotate the whole character
if (axes == RotationAxes.ControllerLook)
{
if (Input.GetMouseButton(1))
{
rotationX += Input.GetAxis(“Mouse X”) * sensitivityX;
rotationX = ClampAngle (rotationX, minimumX, maximumX);

Quaternion xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
transform.rotation = xQuaternion;

}
}
// This section is for the camera to allow vertical and horizontal swing, plus zoom, all with collision detection of the camera
else if (axes == RotationAxes.CameraLook)
{
Vector3 vCamOffset;
RaycastHit hit = new RaycastHit();

// zoom in and out along current camera axis with scroll wheel
float Dis;
if(Input.GetAxis (“Mouse ScrollWheel”) != 0){
Dis = Input.GetAxis (“Mouse ScrollWheel”) * Time.deltaTime * zoomRate * Mathf.Abs (desiredDistance);
// without some limit the scroll speed gets very small close in!
if(Mathf.Abs (Dis) < .1f) {
if(Dis < 0) Dis = -.1f;
else Dis = .1f;
}
desiredDistance -= Dis;
desiredDistance = Mathf.Clamp (desiredDistance, minDistance, maxDistance);

if (target) {
// raycast needs world values, position is world, but we need to add the controller rotation to the camera ray to know which way it really points
// if a hit is found, shorten up the camera distance to stay between character and object hit. We don’t care what it was
if(Physics.Raycast(target.position,target.rotation * transform.localPosition,out hit,desiredDistance + offsetFromWall)){
desiredDistance = hit.distance - offsetFromWall;
}
}
// for simplicity, remove the camera rotation from the position to get the initial ray where z is the distance we want
Quaternion rot = Quaternion.Inverse(transform.localRotation);
vCamOffset = rot * transform.localPosition;
// set to new distance
vCamOffset.z = -desiredDistance;
// put it back in with the rotation
transform.localPosition = transform.localRotation * vCamOffset;
}

if (!target) return;

Vector3 vTargetOffset;

// tilt camera up and down if EITHER mouse button is down (so vertical camera works while moving, but character, not camera rotates while moving
if ((Input.GetMouseButton(0))||(Input.GetMouseButton(1)))
{
rotationY += Input.GetAxis(“Mouse Y”) * sensitivityY;
rotationY = ClampAngle (rotationY, minimumY, maximumY);

// if ONLY left mouse button is down swing camera around
if (Input.GetMouseButton(0) !Input.GetMouseButton(1))
{
rotationX += Input.GetAxis(“Mouse X”) * sensitivityX;
rotationX = ClampAngle (rotationX, minimumX, maximumX);
} else {
// if right only or both buttons are down, swing the camera back behind the character
if(rotationX > 0){
rotationX -= CameraSwingBack * Time.deltaTime;
if(rotationX < 0) rotationX = 0;
} else if(rotationX < 0){
rotationX += CameraSwingBack * Time.deltaTime;
if(rotationX > 0) rotationX = 0;
}
}

Quaternion yQuaternion = Quaternion.AngleAxis (rotationY, Vector3.left);

Quaternion xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
// add rotations in the right order (around then up and down) to get the desired movement
xQuaternion *= yQuaternion;
yQuaternion = xQuaternion;

// yQuaternion now has both rotations in the correct order

vTargetOffset = new Vector3 (0, CameraYOffset, -desiredDistance);
transform.localPosition = yQuaternion * vTargetOffset;
// check to make sure camera rotation hasn’t made it collide with anything
if(Physics.Raycast(target.position,target.rotation * transform.localPosition,out hit,desiredDistance + offsetFromWall)){
desiredDistance = hit.distance - offsetFromWall;
vTargetOffset = new Vector3 (0, CameraYOffset, -desiredDistance);
transform.localPosition = yQuaternion * vTargetOffset;
}
// apply it
transform.localRotation = originalRotation * yQuaternion;
}
}
}

void Start ()
{
desiredDistance = startDistance;

// Make the rigid body not change rotation
if (rigidbody)
rigidbody.freezeRotation = true;
originalRotation = transform.localRotation;
}

public static float ClampAngle (float angle, float min, float max)
{
if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp (angle, min, max);
}
}

First off thanks for the script. Once I got all the pieces in place it went alright.

I am having problems though with animations. Basically what is happening is that he’ll be idle which is great and then I go to move and he’ll play the animation for about 5 steps and then freezes.

I stop and then he reverts to idle but then when I move he does the 5 steps and then freezes again.

I am using the Protopack character and the animation list shows and run an walk are loop.

Any ideas.

Is it loop the check box (which doesn’t make it loop, but adds an extra frame FOR looping. or loop in the pull down box? If the pull down still says default, you must change it to loop, the loop check box can be selected or not, either way it will then loop.
That’s what the problem sounds like.

I added a flying option today, but had to make both scripts c# so they could see each other. Still trying to finish the flaying part (and need to add a flying animation to my model, it just floats around at the moment.

David

Yep that worked great. I would love to see the updated scripts. I do not mind C# I actually prefer it to Javascript.

Thanks

I have te same problem but I can’t seem to find the dropdown box which has to be put to loop. could you please tell me where I can find it? As I am completely new to this.

Looks like a wunderbar script.
I will spare some time to try this baby.

God Bless u!

whenever i click the left mouse click its totally getting zoomed and the background beciomes dark as if it is in the deep of the sea. If i wont click its moving but without any mouse look

i dont know what loop button you were on about but i added the line
animation.wrapMode = WrapMode.Loop;
which makes it loop, also not all animations are handled in this script but Im adding things like jump,

still thanks script was different from others, good read :slight_smile: