Camera Relative Movement

Description

In many older 3D games (such as Mario 64 and the Legend of Zelda: Ocarina of Time)  for consoles like the N64 and Playstation games used a simple movement scheme called camera relative movement. In camera relative movement the player moves according to where the camera is. For example if the player holds the left button, the players avatar will run to the left in a circle around the camera. Using the two C# scripts at the bottom of this post you can do the same in just about any Unity project so long as you assign the proper variables and create the scripts necessary to interact with the player. An example of these scripts in action can be found at: Adventure Game Test.

Code

CameraRelativeMovement.cs

using UnityEngine;
using System.Collections;

public class CameraRelativeMovement : MonoBehaviour
{
    public Transform targetCamera;
    public Transform lookAtTarget;
    public Transform watchTarget;        
    public float forwardDirection;
    public float sideDirection;
    public float maxSpeed = 10.0f;        

    private CharacterMovement movement;
    private Transform myTransform;

    // Use this for initialization
    void Awake()
    {
        myTransform = transform;
        movement = GetComponent<CharacterMovement>();
    }

    // Update is called once per frame
    void Update()
    {
        Vector2 input = new Vector2(sideDirection, forwardDirection);
        float speedScale = input.magnitude;

        if (speedScale > 1.0f)
        {
            speedScale = 1.0f;
        }

        Vector3 moveRequest = GetMovementRequest(input);

        if (lookAtTarget != null)
        {
             Vector3 direction = lookAtTarget.position - transform.position;
             direction.y = 0.0f;
             movement.RequestRotateTowards(direction.normalized);
        }
        else
        {
             movement.RequestRotateTowards(moveRequest.normalized);
        }

        if (watchTarget != null)
        {
            movement.RequestWatch(watchTarget.position);
        }
        else
        {
            movement.RequestWatch();
        }

        movement.RequestMovement(moveRequest, speedScale * maxSpeed);
    }

    private Vector3 GetMovementRequest(Vector2 input)
    {
        Vector3 forward = myTransform.position - targetCamera.position;
        Vector3 right = Vector3.Cross(myTransform.up, forward);
        Vector3 moveRequest = input.y * forward + input.x * right;
        moveRequest.y = 0;
        return moveRequest;
    }
}

CameraMovement.cs

using UnityEngine;
using System.Collections;
public class CameraMovement : MonoBehaviour
{
    public float movementDamping = 5.0f;
    public float rotationDamping = 5.0f;

    private Transform myTransform;

    // Use this for initialization
    void Awake()
    {
        myTransform = transform;
    }

    public void SpringToTarget(Transform target, float preferredDistance, float preferredHeight)
    {
        float distance = Vector2.Distance(new Vector2(target.position.x, target.position.z),
                                          new Vector2(myTransform.position.x, myTransform.position.z));
        Vector3 moveDirection = target.position - myTransform.position;
        moveDirection.y = 0.0f;
        moveDirection = moveDirection.normalized * (distance - preferredDistance) + myTransform.position;
        moveDirection.y = target.position.y + preferredHeight;
        MoveToPosition(target.position, moveDirection);
    }

    public void SpringToTargetWithConstantOffset(Transform target, float preferredDistance, float preferredHeight)
    {
        float distance = Vector2.Distance(new Vector2(target.position.x, target.position.z),
                                          new Vector2(myTransform.position.x, myTransform.position.z));
        float z = Mathf.Sin(myTransform.rotation.y) * distance;
        float x = Mathf.Cos(myTransform.rotation.y) * distance;

        Vector3 moveDirection = new Vector3(x, 0, z);
        moveDirection.y = 0.0f;
        moveDirection = target.position - moveDirection.normalized * preferredDistance;
        moveDirection.y = target.position.y + preferredHeight;
        MoveToPosition(target.position, moveDirection);
    }

    // returns true if camera is pretty much behind the target
    public bool MoveBehindTarget(Transform target, float preferredDistance, float preferredHeight)
    {
        Vector3 moveDirection = target.forward;
        moveDirection.y = 0;
        moveDirection = moveDirection.normalized * -preferredDistance + target.position;
        moveDirection.y += preferredHeight;
        MoveToPosition(target.position, moveDirection);
        return (myTransform.position - moveDirection).magnitude < 0.5f;
    }

    public void RotateAroundPoint(Vector3 target, float rotation)
    {
        Vector3 forward = myTransform.position - target;
        Vector3 right = Vector3.Cross(myTransform.up, forward);
        float distance = Vector3.Distance(myTransform.position, target);

        Vector3 targetPosition = forward + right * rotation;
        targetPosition = targetPosition.normalized * distance;
        targetPosition.y = myTransform.position.y;
        targetPosition.x += target.x;
        targetPosition.z += target.z;
        MoveToPosition(target, targetPosition);
    }

    public void LookAtTarget(Vector3 direction)
    {
        transform.rotation = Quaternion.Slerp(myTransform.rotation, Quaternion.LookRotation(direction), rotationDamping * Time.deltaTime);
    }

    private void MoveToPosition(Vector3 lookAtTarget, Vector3 targetPosition)
    {
        Vector3 direction = (targetPosition - lookAtTarget).normalized;
        float distance = Vector3.Distance(lookAtTarget, targetPosition);

        RaycastHit hit;
        if (Physics.Raycast(lookAtTarget, direction, out hit, distance) &&
            (hit.collider.CompareTag("Wall") || hit.collider.CompareTag("Door")))
        {
            myTransform.position = Vector3.Lerp(myTransform.position, hit.point, movementDamping * Time.deltaTime);
        }
        else
        {
            myTransform.position = Vector3.Lerp(myTransform.position, targetPosition, movementDamping * Time.deltaTime);
        }
    }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s