Day 8: Creating a Character in a First Person Shooter

Josh Unity VR Development Leave a Comment


Here we are on day 8! Yesterday we created a very simple map for a simple FPS feature.

Today, I went to look to see how we can move a character around in the scenery that was created.

Here’s what I got!

Creating our character

The first thing I figured out who was the “character”. Turns out for our game, our main character is someone we know well.

Here it is:

That’s right its Main Camera! Who knew?

If we think about it, it does make sense.

The camera object is what we see when we run the game. In an FPS game, we see what our character sees. Which means our camera must be part of the character, specifically their eyes.

Setting up our character

The first thing we do is to create an Empty Game object that we will call Player then we’ll make our main camera a child of player.

I’d imagine when we want a character model, we would have the camera be right in front of the face of the model, but for now, we don’t need one.

Make sure the camera is set at position (0, 1, 0) and the Player position to be (5, 1, 5).

The X and Z position is so that we don’t start falling through the edge of our map and the Y value would give us some height, otherwise we would be facing the floor.

Next, we need to add a collider to our Game Object so that we won’t fall through the world when we start the game.

I created a Capsule Collider and I set the height to be 2 and left the rest as the same.

I’ve also attached a RigidBody component, because later on, we’re going to have to write some code to make our camera move when we click on our keyboard.

And here we are, our player character:

And here’s what we’ll see when we play:

If you want, you can give our character a Mesh Renderer and a Mesh Filter so that you can see where our character is in the Scene tab. We also get a nice little shadow!

Creating Movement for our Character

Now that we have a character, the next thing we have to do is to move it.

I found that Unity actually offers some very good built in FPS controls from this thread: How to make a first-person player for my game.

If you don’t have the fps scripts like in the thread, you can go to Assets > Import Package > Player to get these scripts.

After downloading them, all you have to do is attach the scripts to the Player game object.

However, since I’m doing this for learning, I’m going to attempt to write my own script to do something similar.

The first thing I’m going to do is create a new script called PlayerController, this will be used to control our player’s position based off of the user’s input.

I actually had to spend a lot of time working on this and searching on Google, but here are the results.

using UnityEngine;

public class PlayerController : MonoBehaviour {

    public float Speed = 3f;

    private Vector3 _movement;
    private Rigidbody _playerRigidBody;

    private void Awake()
    {
        _playerRigidBody = GetComponent<Rigidbody>();
    }
    

    private void FixedUpdate()
    {
        float horizontal = Input.GetAxisRaw("Horizontal");
        float vertical = Input.GetAxisRaw("Vertical");

        Move(horizontal, vertical);
    }

    private void Move(float horizontal, float vertical)
    {
        _movement = (vertical * transform.forward) + (horizontal * transform.right);
        _movement = _movement.normalized * Speed * Time.deltaTime;
        _playerRigidBody.MovePosition(transform.position + _movement);
    }
}

The code is pretty straight forward, here’s the flow:

  • In Awake() we get our RigidBody so we can move our player
  • In FixedUpdate(), we use GetAxisRaw() to get our horizontal and vertical movement so that we can detect if the user is moving left/right and up/down.
  • In Move() we’re given the movement directions. We want to create a movement vector that captures the direction from where the user is facing, which we achieve with transform.forward and transform.right. By multiplying everything together, we get the direction that the player should move based off of their inputs.
  • Finally we normalize our movement vector and then we move our position with our new Vector.

It’s important to note that we must use the RigidBody to move.

Before I was trying to move the player with transform.Translate, what I found out is that this function is the equivalent of teleportation. What happens is that if there are obstacles in front of the player, our character will just teleport past them.

I also want to emphasis that it’s important that we use transform.forward and transform.right especially once we start trying to get the camera to follow the mouse.

When we start rotating our character to follow the mouse, our vertical and horizontal values don’t give us the direction adjusted for which direction we’re facing.

For example, if we were to face forward and press W to move forward, we would move forward. However, if we were to turn 90 degrees to the right, and press W again, instead of moving forward like you’d expect, you would move to the left where “forward” use to be.

transform.forward gives us vector that the player is facing and then we multiply that with our vertical value which tells us if we’re moving forward or back. We do the same with transform.right and horizontal.

Rotating our Camera

The next problem after making our character move is trying to figure out how to get the camera to follow the mouse.

There were a lot of answers, however I think the best explanation is from this Youtube video: How to construct a simple First Person Controller.

I only took the snippet for the camera controller.

I made another class called MouseCameraController and attached it to our Main
Camera
.

The script allows us to we want to be able to view up and down with the camera without moving the character, but we want to change our player rotation value as we look horizontally.

Here’s the code:

using UnityEngine;

public class MouseCameraContoller : MonoBehaviour
{
    public float Sensitivity = 5.0f;
    public float Smoothing = 2.0f;

    private Vector2 _mouseLook;
    private Vector2 _smoothV;

    private GameObject _player;

    void Awake()
    {
        _player = transform.parent.gameObject;
    }

    // Update is called once per frame
    void Update () {
        Vector2 mouseDirection = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));

        mouseDirection.x *= Sensitivity * Smoothing;
        mouseDirection.y *= Sensitivity * Smoothing;

        _smoothV.x = Mathf.Lerp(_smoothV.x, mouseDirection.x, 1f / Smoothing);
        _smoothV.y = Mathf.Lerp(_smoothV.y, mouseDirection.y, 1f / Smoothing);

        _mouseLook += _smoothV;
        _mouseLook.y = Mathf.Clamp(_mouseLook.y, -90, 90);

        transform.localRotation = Quaternion.AngleAxis(-_mouseLook.y, Vector3.right);
        _player.transform.rotation = Quaternion.AngleAxis(_mouseLook.x, _player.transform.up);
    }
}

There’s a lot of math to digest for this code, but let’s go through them.

Like mentioned, it’s important that the script is attached to the camera and not the main character body.

We want to rotate the characters body when we look left to right, but when we look up to down, we don’t want to rotate the body.

  • In our Start() function, we want to get the parent element (the Player Game Object) so we can rotate it as we turn.
  • In Update() we create a vector that would represent where our mouse is facing which we will use to determine where our camera will be facing by using our Mouse X and Mouse Y positions
  • Next we want to multiply the direction we created with 2 variables: Sensitivity and Smoothing
    1. Sensitivity is used to determine how big the vector of our mouseDirection would be. The higher the number, the more sensitive our mouse would be, allowing us to rotate more quickly.
    2. Smoothness is used to help us evenly move our camera from its starting location to the new location from the mouse location. We use lerp to help us make gradual movement instead of one massive movement
  • Once we calculate the new direction we’re facing into smoothV, we add that into our mouseLook variable which is where our character will be facing.
  • We make sure to clamp the y rotation, because we don’t want to be able to go from looking in front of us to behind us. It’s unnatural human anatomy.
  • Finally, for rotation. We want to rotate the localRotation of our camera, meaning we rotate the game object, relative to its parent.
    1. For example, if our player is facing 90 degree, if we were to set rotation of our camera to 90, we would be at 90, the same location. If we use localRotation, it would take in the fact our character is facing 90 degree and rotate 90 more.
    2. We create our angle by using our y value (the up and down) and rotating by the angle Vector3.right, which is the X axis.
  • Our last rotation is when we want to turn our player around instead of the camera. We don’t have to use localRotation, because the player doesn’t have a parent (which means it defaults to the game world as the parent), so we can just use rotation, though both would work.
    1. As for our rotation, we rotate our player by our left and right mouse movements using Vector3.up, which is the Y axis.

And that’s it! Play the game and you should be able to move your character around in the game world and bump into our environment that we setup.

Conclusion

Now wasn’t this short compared to the video tutorials?

Today we learned how to move and rotate the character around in the game screen.

It’s important to know that Unity provides a standard asset that makes movement easy for us, but I do think there’s value in being able to understand some of the inner workings of the code.

Either way, stay tune for Day 9, where we’ll hopefully start exploring how we can equip a gun in our player and maybe even start shooting something!

Day 7 | 100 Days of VR |Day 9

Subscribe To Our Weekly Newsletter!
Like these coding articles? Join my mailing list for the latest updates and influence the code that I write!
We hate spam. Your email address will not be sold or shared with anyone else.

Leave a Reply

Your email address will not be published. Required fields are marked *