[SOLVED] Rise of the Tomb Raider-esque Movement?

Hi there, so right now I’m trying to create the foundation of a third-person adventure game like not really like Tomb Raider but I’m playing it right now and it gave me inspiration to work on something with the same movement and controls. I am designing it around the usage of an xbox controller as I just had shoulder surgery and keyboard and mouse isn’t really possible for me. So the movement is meant to be centered around joystick movement. Not sure if that’s necessary information, but I’m trying to be thorough.

So I have the character controller set-up and I want it to:

  • Rotate to face the direction the joystick is being pushed and then start moving in that direction
  • Move dependent on the direction the camera is facing.
  • If the camera is moved forward is always moving away from the camera.

Here is the code I’m using, it is mainly just the Controller.Move API reference material and some mecanim stuff.:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour {

    public float speed = 6.0f;
    private float playerSpeed;
    private float playerDirection;
    public float jumpSpeed = 8.0f;
    public float gravity = 20.0f;
    Vector3 moveDirection = Vector3.zero;
    Quaternion targetRotation;
    public float rotationSpeed = 0.7f;

    private Animator anim;

    private void Start()
    {
        anim = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update () {

        CharacterController character = GetComponent<CharacterController>();
        if (character.isGrounded)
        {
            moveDirection = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
            if(moveDirection != Vector3.zero)
            {
                transform.forward = moveDirection;
            }
            moveDirection = transform.TransformDirection(moveDirection);
            moveDirection *= speed;
            playerSpeed = Input.GetAxisRaw("Vertical");
            playerDirection = Input.GetAxisRaw("Horizontal");
            if (Input.GetButton("Jump"))
            {
                moveDirection.y = jumpSpeed;
            }
        }
        moveDirection.y -= gravity * Time.deltaTime;
        character.Move(moveDirection * Time.deltaTime);
        anim.SetFloat("Speed", playerSpeed);
    }
}

So you can see I tried to use transform.forward but that turns the character instantly which I know I can fix using a Quaternion.Slerp but it then strafes instead of then moving to the right of the camera.

Any help would be appreciated thanks! :slight_smile:

If you need reference material from the game I can provide that too if you have not played it.

You probably need a LookRotation in there along with the movement.

you actually want to use the camera forward(after flattening it on the y axis) and right vectors. then you just need to rotate your players forward vector to match the cameras forward vector. so the move direction would actually be:

Vector3 moveDir = Camera.main.transform.forward * moveInputVertical + Camera.main.transform.right * moveInputHorizontal;

moveDir.y = 0;
moveDir.Normalize();

after that you could then rotate your players forward to match the direction, or, get the angle and speed at feed that to the animator to move with animations, if you wanted to.

something like that anyway.

I think I can solve the instantaneous turning with something like Quaternion.Slerp(transform.rotation, Quaternion.LookRotation, rotationSpeed * Time.deltaTime) but the relative movement to the camera is the biggest thing I’m stumped on and haven’t found anything helpful through a lot of Googling.

Relative to the camera is a little tough because the camera is facing downwards. One thing you might try is parenting the camera to an empty, and moving the empty around. That would be at least facing in the direction we sort of think the camera is facing. Then I think you would use the camera parent transform forward, right, and back rather than the player. I haven’t done any of this, btw. There are all kinds of 3rd person controller vids on youtube that would probably be helpful.

veDi

So revised code with your implementation is:

void Update () {
        float moveVertical = Input.GetAxis("Vertical");
        float moveHorizontal = Input.GetAxis("Horizontal");
    CharacterController character = GetComponent<CharacterController>();
        if (character.isGrounded)
        {
            moveDirection = Camera.main.transform.forward * moveHorizontal + Camera.main.transform.forward * moveVertical;
            moveDirection.y = 0;
            if(moveDirection != Vector3.zero)
            {
                //transform.forward = moveDirection;
            }
            moveDirection = transform.TransformDirection(moveDirection);
            moveDirection *= speed;
            playerSpeed = Input.GetAxisRaw("Vertical");
            playerDirection = Input.GetAxisRaw("Horizontal");
            if (Input.GetButton("Jump"))
            {
                moveDirection.y = jumpSpeed;
            }
        }
        moveDirection.y -= gravity * Time.deltaTime;
        character.Move(moveDirection * Time.deltaTime);
        anim.SetFloat("Speed", playerSpeed);
    }

In that implementation horizontal movement of the analog stick either to the left or right causes the character to move forward and backward. I attempted implementing the rotation as it was before but this doesn’t seem to work. I’m assuming the forward and backward movement is intended as if the character rotates sideways it would then be continuing to move forward? I could be wrong here though.

EDIT: Oof I see the mistake with using transform.forward twice. I will do some more tests.

Well my camera is actually facing forwards in a behind the back sort of thing, I’ve looked into a few of the youtube ones that I’ve found which seem to want to make tank controls rather than what I’m trying to implement. It also saves on needed animations as I can just use the same walk to jog animation in every direction.

So after fixing the movement which works exactly as I wanted my issue now falls on the proper rotation as using transform.forward cause the character to turn and face to the right but then begin sliding to it’s now right or towards the camera rather than continue to run to what would now be forwards. Does this have to do with where I am implementing the rotation? I think it needs to be checking what direction the camera is facing after the direction change.

Try this

//move dir is as before
float smoothing = 5;

Quaternion desiredRotation = Quaternion.LookDirection(moveDir);

playerTransform.rotation = Quarternio. Slerp(playerTransfor.rotation, moveDir, smoothing * Time.deltaTime) ;

Keep in mind that to move the character controller you want to either use root motion, or you want to move it using the cameras forward and right vectors, not the players vectors.

So not exactly what I was looking for. The character now flops around and then spins uncontrollably when pushing the stick to the right or left.

float moveVertical = Input.GetAxis("Vertical");
        float moveHorizontal = Input.GetAxis("Horizontal");
        targetRotation = Quaternion.LookRotation(moveDirection);
    CharacterController character = GetComponent<CharacterController>();
        if (character.isGrounded)
        {
            moveDirection = Camera.main.transform.forward * moveVertical + Camera.main.transform.right * moveHorizontal;
            moveDirection.y = 0;
            if (moveDirection != Vector3.zero)
            {
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smoothing);
            }
           
            moveDirection = transform.TransformDirection(moveDirection);
            moveDirection *= speed;
            playerSpeed = Input.GetAxisRaw("Vertical");
            playerDirection = Input.GetAxisRaw("Horizontal");
            if (Input.GetButton("Jump"))
            {
                moveDirection.y = jumpSpeed;
            }
        }

This is with the additions, where you included moveDirection isn’t allowed since it’s a Vector3 so I assumed you meant the targetRotaion/ desiredRotation value.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(CharacterController))]
public class SamplePlayerMotor : MonoBehaviour
{
    public float rotationSpeed = 15f;
    public float moveSpeed = 3f;

    CharacterController motor;

    Quaternion desiredRotation;

    // Use this for initialization
    void Start ()
    {
        motor = GetComponent<CharacterController>();
        desiredRotation = transform.rotation;       
    }
   
    // Update is called once per frame
    void Update ()
    {
        float inputX = Input.GetAxis("Horizontal");
        float inputY = Input.GetAxis("Vertical");

        Vector3 moveDir = (Camera.main.transform.forward * inputY) + (Camera.main.transform.right * inputX);
        moveDir.y = 0f;
        moveDir.Normalize();

        if(moveDir != Vector3.zero)
            desiredRotation = Quaternion.LookRotation(moveDir);

        transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, rotationSpeed * Time.deltaTime);

        motor.Move(moveDir * moveSpeed * Time.deltaTime);
    }
}

is this what you are sort of shooting for? Hopefully it is cause this is what i thought you were trying to do. let me know if not.

Yes! That is exactly what I was looking for! Thanks so much for the help! Sorry I didn’t get back to you sooner had some stuff going on. I greatly appreciate the time you took to help me. :slight_smile: