Commit 57155cfc authored by David Huss's avatar David Huss 💬
Browse files

Simplify Movement Script

parent b3ca5151
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Generic First-Person-Shooter-like Controller
[RequireComponent(typeof(CharacterController))]
public class FPSController : MonoBehaviour
{
[Tooltip("How fast the player moves when walking (default move speed).")]
[SerializeField]
private float movementWalkSpeed = 6.0f;
[Tooltip("How fast the player moves when running.")]
[SerializeField]
private float movementRunSpeed = 11.0f;
[Tooltip("If true, diagonal speed (when strafing + moving forward or back) can't exceed normal move speed; otherwise it's about 1.4 times faster.")]
[SerializeField]
public bool movementLimitDiagonalSpeed = true;
[Tooltip("If checked, the run key toggles between running and walking. Otherwise player runs if the key is held down.")]
[SerializeField]
private bool movementToggleRun = false;
[Tooltip("How high the player jumps when hitting the jump button.")]
[SerializeField]
private float movementJumpSpeed = 8.0f;
[Tooltip("How fast the player falls when not standing on anything.")]
[SerializeField]
private float movementGravity = 20.0f;
[Tooltip("Units that player can fall before a falling function is run. To disable, type \"infinity\" in the inspector.")]
[SerializeField]
private float movementFallingThreshold = 10.0f;
[Tooltip("If the player ends up on a slope which is at least the Slope Limit as set on the character controller, then he will slide down.")]
[SerializeField]
private bool movementSlideWhenOverSlopeLimit = false;
[Tooltip("If checked and the player is on an object tagged \"Slide\", he will slide down it regardless of the slope limit.")]
[SerializeField]
private bool movementSlideOnTaggedObjects = false;
[Tooltip("How fast the player slides when on slopes as defined above.")]
[SerializeField]
private float movementSlideSpeed = 12.0f;
[Tooltip("If checked, then the player can change direction while in the air.")]
[SerializeField]
private bool movementAirControl = false;
[Tooltip("Small amounts of this results in bumping when walking down slopes, but large amounts results in falling too fast.")]
[SerializeField]
private float movementAntiBumpFactor = .75f;
[Tooltip("Player must be grounded for at least this many physics frames before being able to jump again; set to 0 to allow bunny hopping.")]
[SerializeField]
private int movementAntiBunnyHopFactor = 1;
private Vector3 movementMoveDirection = Vector3.zero;
private bool movementGrounded = false;
private CharacterController movementController;
private Transform movementTransform;
private float movementSpeed;
private RaycastHit movementHit;
private float movementFallStartLevel;
private bool movementFalling;
private float movementSlideLimit;
private float movementRayDistance;
private Vector3 movementContactPoint;
private bool movementPlayerControl = false;
private int movementJumpTimer;
private void Start()
{
// Saving component references to improve performance.
movementTransform = GetComponent<Transform>();
movementController = GetComponent<CharacterController>();
// Setting initial values.
movementSpeed = movementWalkSpeed;
movementRayDistance = movementController.height * .5f + movementController.radius;
movementSlideLimit = movementController.slopeLimit - .1f;
movementJumpTimer = movementAntiBunnyHopFactor;
}
private void Update()
{
// If the run button is set to toggle, then switch between walk/run speed. (We use Update for this...
// FixedUpdate is a poor place to use GetButtonDown, since it doesn't necessarily run every frame and can miss the event)
if (movementToggleRun && movementGrounded && Input.GetButtonDown("Run"))
{
movementSpeed = (movementSpeed == movementWalkSpeed ? movementRunSpeed : movementWalkSpeed);
}
}
private void FixedUpdate()
{
float inputX = Input.GetAxis("Horizontal");
float inputY = Input.GetAxis("Vertical");
// If both horizontal and vertical are used simultaneously, limit speed (if allowed), so the total doesn't exceed normal move speed
float inputModifyFactor = (inputX != 0.0f && inputY != 0.0f && movementLimitDiagonalSpeed) ? .7071f : 1.0f;
if (movementGrounded)
{
bool sliding = false;
// See if surface immediately below should be slid down. We use this normally rather than a ControllerColliderHit point,
// because that interferes with step climbing amongst other annoyances
if (Physics.Raycast(movementTransform.position, -Vector3.up, out movementHit, movementRayDistance))
{
if (Vector3.Angle(movementHit.normal, Vector3.up) > movementSlideLimit)
{
sliding = true;
}
}
// However, just raycasting straight down from the center can fail when on steep slopes
// So if the above raycast didn't catch anything, raycast down from the stored ControllerColliderHit point instead
else
{
Physics.Raycast(movementContactPoint + Vector3.up, -Vector3.up, out movementHit);
if (Vector3.Angle(movementHit.normal, Vector3.up) > movementSlideLimit)
{
sliding = true;
}
}
// If we were falling, and we fell a vertical distance greater than the threshold, run a falling damage routine
if (movementFalling)
{
movementFalling = false;
if (movementTransform.position.y < movementFallStartLevel - movementFallingThreshold)
{
OnFell(movementFallStartLevel - movementTransform.position.y);
}
}
// If running isn't on a toggle, then use the appropriate speed depending on whether the run button is down
if (!movementToggleRun)
{
movementSpeed = Input.GetKey(KeyCode.LeftShift) ? movementRunSpeed : movementWalkSpeed;
}
// If sliding (and it's allowed), or if we're on an object tagged "Slide", get a vector pointing down the slope we're on
if ((sliding && movementSlideWhenOverSlopeLimit) || (movementSlideOnTaggedObjects && movementHit.collider.tag == "Slide"))
{
Vector3 hitNormal = movementHit.normal;
movementMoveDirection = new Vector3(hitNormal.x, -hitNormal.y, hitNormal.z);
Vector3.OrthoNormalize(ref hitNormal, ref movementMoveDirection);
movementMoveDirection *= movementSlideSpeed;
movementPlayerControl = false;
}
// Otherwise recalculate moveDirection directly from axes, adding a bit of -y to avoid bumping down inclines
else
{
movementMoveDirection = new Vector3(inputX * inputModifyFactor, -movementAntiBumpFactor, inputY * inputModifyFactor);
movementMoveDirection = movementTransform.TransformDirection(movementMoveDirection) * movementSpeed;
movementPlayerControl = true;
}
// Jump! But only if the jump button has been released and player has been grounded for a given number of frames
if (!Input.GetButton("Jump"))
{
movementJumpTimer++;
}
else if (movementJumpTimer >= movementAntiBunnyHopFactor)
{
movementMoveDirection.y = movementJumpSpeed;
movementJumpTimer = 0;
}
}
else
{
// If we stepped over a cliff or something, set the height at which we started falling
if (!movementFalling)
{
movementFalling = true;
movementFallStartLevel = movementTransform.position.y;
}
// If air control is allowed, check movement but don't touch the y component
if (movementAirControl && movementPlayerControl)
{
movementMoveDirection.x = inputX * movementSpeed * inputModifyFactor;
movementMoveDirection.z = inputY * movementSpeed * inputModifyFactor;
movementMoveDirection = movementTransform.TransformDirection(movementMoveDirection);
}
}
// Apply gravity
movementMoveDirection.y -= movementGravity * Time.deltaTime;
// Move the controller, and set grounded true or false depending on whether we're standing on something
movementGrounded = (movementController.Move(movementMoveDirection * Time.deltaTime) & CollisionFlags.Below) != 0;
}
// Store point that we're in contact with for use in FixedUpdate if needed
private void OnControllerColliderHit(ControllerColliderHit hit)
{
movementContactPoint = hit.point;
}
// This is the place to apply things like fall damage. You can give the player hitpoints and remove some
// of them based on the distance fallen, play sound effects, etc.
private void OnFell(float fallDistance)
{
print("Ouch! Fell " + fallDistance + " units!");
}
}
\ No newline at end of file
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class Movement : MonoBehaviour
{
// The Speed at which the Player can move in units per second
[Tooltip("How fast the character moves when walking (units per second).")]
[SerializeField] float moveSpeed = 4f;
// Allow Jumping
[Tooltip("Should Jumping be allowed?")]
[SerializeField] bool allowJump = true;
// The height of a jump, in units
[Tooltip("How high should the character jump (in units).")]
[SerializeField] float jumpHeight = 1.5f;
// The rate at which our vertical speed will be reduced in units per second
[Tooltip("Gravity in units per second.")]
[SerializeField] float gravity = 30f;
// The degree to which movement can be controlled midair
[Tooltip("How much maneuverability should remain in the air.")]
[Range(0,10), SerializeField] float airControl = 5;
// Movement Direction, can be only controlled on the ground
Vector3 moveDirection = Vector3.zero;
// Reference to the Character Controller (cached)
CharacterController controller;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController>();
}
// FixedUpdate must be used if the Player should interact with Physics Objects
void FixedUpdate()
{
// This vector describes the desired plane of movement. If we are in the
// air we will interpolate the current movement with this vector to
// simulate a form of momentum
var input = new Vector3(
Input.GetAxis("Horizontal"),
0,
Input.GetAxis("Vertical")
);
// Multiply the movement with the movement speed
input *= moveSpeed;
// Convert the rotation to world-space coordinates (as needed by
// the CharacterController Type)
input = transform.TransformDirection(input);
// Is the Controllers bottom-most point touching the Ground?
if (controller.isGrounded) {
moveDirection = input;
// If jumping is allowed and the Jump Button is pressed
if (allowJump && Input.GetButton("Jump")) {
moveDirection.y = Mathf.Sqrt(2 * gravity * jumpHeight);
}else{
// Player is on the Ground, cancel Y-Axis Movement
moveDirection.y = 0;
}
}else{
// We are falling, so Ignore Y-Axis-Input, but allow user to steer
// the fall on the X/Z-Axis
input.y = moveDirection.y;
moveDirection = Vector3.Lerp(moveDirection, input, airControl * Time.deltaTime);
}
// Subtract the gravity from any Y-Axis movement
moveDirection.y -= gravity * Time.deltaTime;
// Finally: Move the controller into the desired direction.
// Note: if this would hit a collider or the controller would refuse
// to move further – however – if the other collider would move
// into this object it won't budge..
controller.Move(moveDirection * Time.deltaTime);
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
These two Scripts allow you to implement a simple First-Person Control for Unity. These two Scripts allow you to implement a simple First-Person Control for Unity.
### Script 1: FPSController ### Script 1: Movement
This enables the player to change position by walking, running and jumping by pressing the following keys: This enables the player to change position by walking, running and jumping by pressing the following keys:
...@@ -10,8 +10,7 @@ This enables the player to change position by walking, running and jumping by pr ...@@ -10,8 +10,7 @@ This enables the player to change position by walking, running and jumping by pr
- <kbd>A</kbd> or <kbd></kbd> Moves sidways to the left - <kbd>A</kbd> or <kbd></kbd> Moves sidways to the left
- <kbd>S</kbd> or <kbd></kbd> Moves backwards - <kbd>S</kbd> or <kbd></kbd> Moves backwards
- <kbd>D</kbd> or <kbd></kbd> Moves sidways to the right - <kbd>D</kbd> or <kbd></kbd> Moves sidways to the right
- <kbd>Shift</kbd> "Run" (move faster) - <kbd>Space</kbd> Jump (if enabled)
- <kbd>Space</kbd> Jump
### Script 2: SmoothMouseLook ### Script 2: SmoothMouseLook
...@@ -27,7 +26,7 @@ This allows users to use the mouse for looking around, with seperate sensitivite ...@@ -27,7 +26,7 @@ This allows users to use the mouse for looking around, with seperate sensitivite
Now things should look like in the picture above (highlighted in blue). Next we can add the Scripts. Now things should look like in the picture above (highlighted in blue). Next we can add the Scripts.
1. Drag the `FPSController.cs`-script from the Assets onto the `Player`-Empty 1. Drag the `Movement.cs`-script from the Assets onto the `Player`-Empty
2. Drag the `SmoothMouseLook.cs`-script from the Assets onto the `Player`-Empty. 2. Drag the `SmoothMouseLook.cs`-script from the Assets onto the `Player`-Empty.
## Tweaking ## Tweaking
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment