Unity Character Control
Character action control is a crucial part of the game, as games with strong operation capabilities can effectively attract players. Here, I will attempt to create a simple character control system where the character can perform basic movements, including walking and jumping.
Requirement
First, let's consider the specific requirements for character operations:
- Walking, able to walk on the surface of the rigid body, controlled by arrow keys for up, down, left, and right input, without considering the acceleration and deceleration process for now.
- The walking speed can be different in different directions, for example, moving backward should be slower than moving forward.
- Jumping, controlled by the jump key, the character leaves the ground with a certain initial velocity and gradually falls back to the ground.
The general idea is as follows: Use velocity to describe the movement of a character. The components of velocity in each direction can be calculated separately, and finally, multiplying velocity by time gives the character's position offset.
Character Component Settings
Before writing scripts to control and manipulate characters, some preparation work needs to be done to configure the relevant components of the character.
- In order to control the characters and give them some rigid physical behavior, it is necessary to add a
Character Controller Component
to the characters. - For a clearer structure, separate the input for character operations first, read and preprocess the input, and then pass the results to the character controller. Name this part of the script as
MyThirdPersonInput.cs
. - The script that actually controls the character's movement should be named
MyThirdPersonController.cs
.
The result after configuration is as follows:
Input
The input consists of up, down, left, right, and jump commands. The direction needs to be normalized:
// get movement from input
var direction = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
if (direction != Vector3.zero)
{
// constrain length to [0, 1]
var directionLength = direction.magnitude;
directionLength = Math.Min(1, directionLength);
direction = direction.normalized * directionLength;
}
person.inputMoveDirction = direction;
person.inputJump = Input.GetButton("Jump");
````
## Describing Movement and Jumping
We need to use some variables to describe the character's actions, such as movement speed, jumping speed, etc. Movement is described using the following variable:
```c#
[System.Serializable]
public class Movement
{
public float forwardSpeed = 5F;
public float backwardSpeed = 5F;
public float sidewardSpeed = 5F;
}
public Movement movement = new Movement();
[System.Serializable]
is used to expose these parameters in the Inspector. The description of jumping is as follows:
[System.Serializable]
public class Jumping
{
public bool enable = true; // true if can jump
public float jumpSpeed = 5F; // original speed when jump
public float gravity = 10F;
public float maxFallSpeed = 20F;
public bool jumping = false; // true if now in the air
}
public Jumping jumping = new Jumping();
Decomposition Speed
In order to facilitate the description of movement in different directions, the direction is divided into three components: front and back, left and right, and up and down, respectively, to be resolved.
The speed difference before and after varies. It can be determined based on the sign of the numerical value:
Same Speed on Both Sides:
Jumping is a bit troublesome as it requires determining the current status of the character:
- If already in the air, calculate the speed with gravity.
- If on the ground:
-
- If the jump key is pressed, the speed is the initial jump speed.
-
- Otherwise, the speed in the y direction is 0.
if (!isOnGround)
{
yVelocity = Math.Max(yVelocity - jumping.gravity * Time.deltaTime,
-jumping.maxFallSpeed);
}
else
{
if (jumping.enable && inputJump)
{
yVelocity = jumping.jumpSpeed;
}
else
yVelocity = 0F;
}
Update Character Location
The calculated velocity is assumed to be the velocity from the current frame. So, the velocity used to calculate the position in the current frame should be the one calculated in the previous frame. Therefore, before updating the velocity, calculate the character's new position first.
// move to new position
var collisionFlag = controller.Move(velocity * Time.deltaTime);
isOnGround = (collisionFlag & CollisionFlags.CollidedBelow) != 0;
controller.Move
will return CollisionFlags
to indicate the collision state, through which we can determine whether the character is standing on the ground or not.
Complete code:
(to_be_replaced1)
MyThirdPersonInput.cs:
using UnityEngine;
using System;
using System.Collections;
[RequireComponent(typeof(MyThirdPersonController))]
public class MyThirdPersonInput : MonoBehaviour {
private MyThirdPersonController person;
void Awake()
{
person = GetComponent<MyThirdPersonController>();
}
// Update is called once per frame
void Update ()
{
// get movement from input
var direction = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
if (direction != Vector3.zero)
{
// constrain length to [0, 1]
var directionLength = direction.magnitude;
directionLength = Math.Min(1, directionLength);
direction = direction.normalized * directionLength;
}
person.inputMoveDirction = direction;
person.inputJump = Input.GetButton("Jump");
}
}
MyThirdPersonController.cs:
using UnityEngine;
using System;
using System.Collections;
public class MyThirdPersonController : MonoBehaviour {
// The current global direction we want the character to move in.
[System.NonSerialized]
public Vector3 inputMoveDirction = Vector3.zero;
// Is the jump button held down? We use this interface instead of checking
// for the jump button directly so this script can also be used by AIs.
[System.NonSerialized]
public bool inputJump = false;
[System.Serializable]
public class Movement
{
public float forwardSpeed = 5F;
public float backwardSpeed = 5F;
public float sidewardSpeed = 5F;
}
public Movement movement = new Movement();
[System.Serializable]
public class Jumping
{
public bool enable = true; // true if can jump
public float jumpSpeed = 5F; // original speed when jump
public float gravity = 10F;
public float maxFallSpeed = 20F;
public bool jumping = false; // true if now in the air
}
public Jumping jumping = new Jumping();
private CharacterController controller;
private Vector3 velocity = Vector3.zero;
private bool isOnGround = true;
// Use this for initialization
void Start ()
{
controller = GetComponent<CharacterController>();
}
// Update is called once per frame
void FixedUpdate()
{
// move to new position
var collisionFlag = controller.Move(velocity * Time.deltaTime);
isOnGround = (collisionFlag & CollisionFlags.CollidedBelow) != 0;
// update velocity
float yVelocity = velocity.y;
velocity = Vector3.zero;
// x-z plane velocity
if (inputMoveDirction != Vector3.zero)
{
velocity.z = inputMoveDirction.z;
if (velocity.z > 0)
velocity.z *= movement.forwardSpeed;
else
velocity.z *= movement.backwardSpeed;
velocity.x = inputMoveDirction.x * movement.sidewardSpeed;
}
// y velocity
if (!isOnGround)
{
yVelocity = Math.Max(yVelocity - jumping.gravity * Time.deltaTime,
-jumping.maxFallSpeed);
}
else
{
if (jumping.enable && inputJump)
{
yVelocity = jumping.jumpSpeed;
}
else
yVelocity = 0F;
}
velocity = transform.rotation * velocity;
velocity.y = yVelocity;
}
}
Original: https://wiki.disenone.site/en
This post is protected by CC BY-NC-SA 4.0 agreement, should be reproduced with attribution.
Visitors. Total Visits. Page Visits.
This post is translated using ChatGPT, please feedback if any omissions.