I wanted to make 3rd person controller and got it to work pretty nicely.
So first of all. What do we need? Well we need a camera ofc and somekind of 3d model to animate ( you will need to make/download your own). Ground and some light should be in the scene too.
First lets do the the script that moves the player.
We need to attach RigidBody and collider to our model. I prefer using capsule collider altought you have to scale it to fit your character. Set rigidbody rotation to freeze from inspector and last remove gravity from rigidbody. We will also have to go to Edit->Project settings->Input->Horizontal and set our negative and positive alt buttons to q and e instead of a and d. Script will make character move forward/backward/left/right, run, turn left/right and jump.
Then to the script.
using UnityEngine; using System.Collections; public class thirdperson : MonoBehaviour { //Walking speed public float speed = 6; //Running speed of our character public float runSpeed = 9; //Speed of how fast our character turns public float rotateSpeed = 90; // is character grounded public bool grounded; //is character jumping public bool isJumping; // which way our character is moving. Defaul value is zero public Vector3 moveDirection = Vector3.zero; void Start() { //in start we set grounded and isjumping to false grounded = false; isJumping = false; } void Update () { //we set rigidbodys velocity to 0 rigidbody.velocity = Vector3.zero; //checks if there is anything under character if is set grounded to true if not set it to false if(Physics.Raycast(transform.GetChild(0).position,Vector3.down,transform.localScale.y/2)) { grounded = true; } else { grounded = false; } //checks if we are grounded so we cant jump again in air and if we press jump button. else we are not jumping so isjumping = false if(Input.GetKeyDown(KeyCode.Space) && grounded == true) { //Starts jump ienumerator StartCoroutine("jump"); } else { isJumping = false; } //If we press "d" we rotate our player as long as "d" is pressed down and if we press "a" rotate other way if(Input.GetKey(KeyCode.D)) { transform.Rotate(Vector3.up, Mathf.Clamp(180f * Time.deltaTime, 0f, 360f)); } if(Input.GetKey(KeyCode.A)) { transform.Rotate(Vector3.up, -Mathf.Clamp(180f * Time.deltaTime, 0f, 360f)); } //set out movedirection equal to our horizontal and vertical axis and out transform direction to those axis. moveDirection = new Vector3(Input.GetAxis("Horizontal"),0,Input.GetAxis("Vertical")); moveDirection = transform.TransformDirection(moveDirection); // checks if we push left shift if yes it multiplies our movedirection speed with runspeed variable if not we go with normal speed if(Input.GetKey(KeyCode.LeftShift)) { moveDirection *= runSpeed; } else { moveDirection *= speed; } //Checks if our character is not jumping and its not grounded. then moves character down "gravity" if(isJumping == false && grounded == false) { transform.Translate(Vector3.down * 3 * Time.deltaTime); } //we set rigidbodys velocity to our movedirection wich contains speed and rotation of our character rigidbody.velocity = moveDirection; } IEnumerator jump() { //First we ste player to jumping so gravity dsnt affect it isJumping = true; //We make loop that moves our player up multiple times if its not in touch with anything in front of it for(int i = 0; i < 30; i++) { if(Physics.Raycast(transform.position,Vector3.forward,transform.localScale.y/2 + 0.5f)) { break; } else { transform.Translate(Vector3.up * 15 * Time.deltaTime,Space.World); } yield return null; } //last we set jumping back to false isJumping = false; } }
Then we need third person camera, so we can look our character. I added 1st person mode aswell so player can look enviroment better. We make a camera and attach this script to it. To make script to work we will need to drag & drop our character into the player field in inspector. Camera will be moving depending on your mouse position and will never go under/behind anything. You can also increase distance between camera and player with mouse scroll wheel. Character will also change its X rotation to same as cameras X rotation if you press right mouse button.
Here is the code :
using UnityEngine; using System.Collections; public class cameraorbit : MonoBehaviour { //This camera public Camera mainCamera; //Our character public Transform player; //distance between character and camera public float distance = 5.0f; //x and y position of camera float x = 0.0f; float y = 0.0f; //x and y side speed, how fast your camera moves in x way and in y way public float xSpeed = 120.0f; public float ySpeed = 120.0f; //Minium and maximum distance between player and camera public float distanceMin = 0.5f; public float distanceMax = 15f; //checks if first person mode is on private bool click = false; //stores cameras distance from player private float curDist = 0; private void Start() { //make variable from our euler angles Vector3 angles = transform.eulerAngles; //and store y and x angles to different values x = angles.y; y = angles.x; //sets this camera to main camera mainCamera = Camera.main; } private void LateUpdate () { //gets mouse movement x and y and multiplies them with speeds and moves camera with them x += Input.GetAxis("Mouse X") * xSpeed * distance * 0.02f; y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f; //set rotation Quaternion rotation = Quaternion.Euler(y, x, 0); //changes distance between max and min distancy by mouse scroll distance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel")*5, distanceMin, distanceMax); //negative distance of camera Vector3 negDistance = new Vector3(0.0f, 0.0f, -distance); //cameras postion Vector3 position = rotation * negDistance + player.position; //rotation and position of our camera to different variables transform.rotation = rotation; transform.position = position; //cameras x rotation float cameraX = transform.rotation.x; //checks if right mouse button is pushed if(Input.GetMouseButton(1)) { //sets CHARACTERS x rotation to match cameras x rotation player.eulerAngles = new Vector3(cameraX,transform.eulerAngles.y,transform.eulerAngles.z); } //checks if middle mouse button is pushed down if(Input.GetMouseButtonDown(2)) { //if middle mouse button is pressed 1st time set click to true and camera in front of player and save cameras position before mmb. //if mmb is pressed again set camera back to it's position before we clicked mmb 1st time and set click to false if(click == false) { click = true; curDist = distance; distance = distance - distance - 1; } else { distance = curDist; click = false; } } //store raycast hit RaycastHit hit; //if camera detects something behind or under it move camera to hitpoint so it doesn't go throught wall/floor if(Physics.Raycast(player.position,(transform.position - player.position).normalized,out hit,(distance <= 0 ? -distance : distance))) { transform.position = hit.point; } } }
Then the animations. I’m no good in doing animations so I had a friend of mine to do them for me. Blender is the easiest tool to get into animating 3d models,
but I’m not going into the details how to do it (to get animation script to work you will need to have your own animations done and added to character model), but more into how to make animations play in script and to do it so they play nice and in correct order.
I had some problems in making first person mode to work well so I made empty game object and made that “the player” and character model its child,
so I could change characters center point more up. That is the reason why I’m calling my gameobjects 1st child’s animations instead of players animation as animation is
attached in the model not the empty gameobject.
I’m using a lot of “if”s which is propably not the best way to do it, but I got it to work anyway. I made different script instead of inserting animations into my movement script just to make it easier to read. Here is what I did in script.
using UnityEngine; using System.Collections; public class Animations : MonoBehaviour { void Start () { //calls this objects 1st childs animation and changes that animations speed. 1f is the curent speed of animation. //You can change animations speed by what looks best transform.GetChild(0).animation["Idle"].speed=0.8f; //sets animations mode to loop so it continues smoothly if it is repeating itself transform.GetChild(0).animation["Idle"].wrapMode = WrapMode.Loop; transform.GetChild(0).animation["Walk"].speed=1; transform.GetChild(0).animation["Walk"].wrapMode = WrapMode.Loop; transform.GetChild(0).animation["JumpForward"].speed=2.5f; transform.GetChild(0).animation["Run"].speed=1.5f; transform.GetChild(0).animation["Run"].wrapMode = WrapMode.Loop; transform.GetChild(0).animation["Landing"].speed=1; //sets so this animation only plays once when it playes and doesnt loop transform.GetChild(0).animation["Landing"].wrapMode = WrapMode.Once; } //is our character landing? bool landing = false; void Update () { //sets idle to true bool idle = true; //we only do otehr animations if jump animation is not playing if(!transform.GetChild(0).animation.IsPlaying("JumpForward")) { if(transform.GetComponent<thirdperson>().grounded) { //checks from anotehr script if we are in ground and we are landing and sets landing to false and plays landing animation if(transform.GetComponent<thirdperson>().grounded && landing == true) { landing = false; transform.GetChild(0).animation.Play("Landing"); } //checks if we press any movement keys and sets idle to false as we are moving and not doing idle animation if(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.Q) || Input.GetKey(KeyCode.E)) { idle = false; //checks if we press left shift with movement keys and plays running animation else it playes walking animation if(Input.GetKey(KeyCode.LeftShift) && transform.GetChild(0).animation.IsPlaying("Run") == false) { //crossfade makes animation changing smooth transform.GetChild(0).animation.CrossFade("Run",1f); } else if(transform.GetChild(0).animation.IsPlaying("Walk") == false && !Input.GetKey(KeyCode.LeftShift)) { transform.GetChild(0).animation.CrossFade("Walk",1f); } } //checks if we press space(jump) and sets idle to false and landing to true and plays jump animation if(Input.GetKey(KeyCode.Space)) { idle = false; landing = true; transform.GetChild(0).animation.CrossFade("JumpForward",1f); } //if idle is true aka no otehr animations are playing and idle is not playing already plays idle animation if(idle == true && !transform.GetChild(0).animation.IsPlaying("Idle")) { transform.GetChild(0).animation.CrossFade("Idle",1f); } } } } }
Here is a few Screen shots how it looked in my project.
Character running and playing running animation, camera next to character.
Camera behind, character playing idle animation.
Character jumping, camera behind, playing jump animation
Camera infront under character, playing idle animation, looking up with camera.
Thank you for reading. I hope this helps someone. And again as I’m still a student so if you have any better ways of doing something I’m all ears. 🙂