Skip to content

Instantly share code, notes, and snippets.

@Maxstupo
Created April 3, 2017 16:40
Show Gist options
  • Save Maxstupo/387c0866d66dcde716b5a04593105075 to your computer and use it in GitHub Desktop.
Save Maxstupo/387c0866d66dcde716b5a04593105075 to your computer and use it in GitHub Desktop.
MovementController.cs
using System;
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class MovementController : MonoBehaviour {
[Serializable]
public enum Movement {
FORWARD, BACKWARD, STOPPED
}
[Serializable]
public enum Strafe {
LEFT, RIGHT, STOPPED
}
[Header("Movement Settings")]
public bool disableMovementLogic;
public float forwardSpeed = 5f;
public float backwardSpeed = 3f;
public float strafeSpeed = 4f;
public float sprintMultiplier = 1.5f;
public float diagonallyWalkingWeight = 1.5f;
public AnimationCurve slopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f));
public float jumpForce = 6f;
[Header("Advanced Settings")]
public float groundDrag = 15f;
public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this )
public float stickToGroundHelperDistance = 0.5f; // stops the character
public bool airControl = true; // can the user control the direction that is being moved in the air
[Header("Movement States")]
[SerializeField]
private Movement moving = Movement.STOPPED; // The current direction moving.
[SerializeField]
private Strafe strafing = Strafe.STOPPED; // The current strafing direction.
[SerializeField]
private bool isSprinting = false;
[SerializeField]
private bool isJumping = false;
// Ground check
private Vector3 groundContactNormal;
private bool isPreviouslyGrounded;
private float defaultDrag;
[Header("Debug")]
[SerializeField]
private bool isGrounded;
[SerializeField]
private float currentSpeed;
[SerializeField]
private float dirX;
[SerializeField]
private float dirY;
private Rigidbody rigidBody;
private CapsuleCollider capsule;
private Animator animator; // Animator might be "null"
public bool IsGrounded { get { return isGrounded; } }
private void Start() {
rigidBody = GetComponent<Rigidbody>();
capsule = GetComponent<CapsuleCollider>();
animator = GetComponent<Animator>();
if (animator != null) {
if (animator.applyRootMotion) { // Disable movement logic for this controller if the animator has root motion enabled.
disableMovementLogic = true;
Debug.Log("Disabled controller movement logic, because animator has root motion enabled!");
}
}
rigidBody.freezeRotation = true;
defaultDrag = rigidBody.drag;
StopMovement();
}
private void FixedUpdate() {
GroundCheck();
CalculateSpeed();
dirX = (strafing == Strafe.LEFT) ? -1 : ((strafing == Strafe.RIGHT) ? 1 : 0);
dirY = (moving == Movement.BACKWARD) ? -1 : ((moving == Movement.FORWARD) ? 1 : 0);
UpdateAnimatorParams();
if (!disableMovementLogic) {
if ((moving != Movement.STOPPED || strafing != Strafe.STOPPED) && (airControl || isGrounded)) {
// always move along the capsule forward as it is the direction we are looking.
Vector3 desiredMove = capsule.transform.forward * dirY + capsule.transform.right * dirX;
desiredMove = Vector3.ProjectOnPlane(desiredMove, groundContactNormal).normalized;
desiredMove *= currentSpeed;
if (rigidBody.velocity.sqrMagnitude < (currentSpeed * currentSpeed))
rigidBody.AddForce((desiredMove * SlopeMultiplier()) * rigidBody.mass, ForceMode.Impulse);
}
if (isJumping && isGrounded) {
rigidBody.AddForce(Vector3.up*(jumpForce*rigidBody.mass), ForceMode.Impulse);
GroundCheck();
}
}
if (IsGrounded) {
rigidBody.drag = groundDrag;
} else {
rigidBody.drag = defaultDrag;
if (isPreviouslyGrounded)
StickToGroundHelper();
}
}
private void UpdateAnimatorParams() {
if (animator != null) {
float speed = 0;
if (isSprinting && moving == Movement.FORWARD) { // Sprinting
speed = 1f;
} else if (!isSprinting && moving == Movement.FORWARD) { // Walking
speed = 0.5f;
} else if (moving == Movement.STOPPED) {
speed = 0f;
} else if (moving == Movement.BACKWARD) { // Backstepping
speed = -1f;
}
animator.SetFloat("Speed", speed);
animator.SetFloat("Sidestep", dirX);
}
}
private void CalculateSpeed() {
// Calculate speed for forward or backwards movement.
currentSpeed = (moving == Movement.FORWARD) ? forwardSpeed : ((moving == Movement.BACKWARD) ? backwardSpeed : 0);
// Apply sprint multiplier if sprinting and moving forward.
if (isSprinting && moving == Movement.FORWARD)
currentSpeed *= sprintMultiplier;
if (moving == Movement.STOPPED && strafing != Strafe.STOPPED) { // If strafing side-to-side.
currentSpeed = strafeSpeed;
} else if (moving != Movement.STOPPED && strafing != Strafe.STOPPED) { // If strafing and moving forwards or backwards.
float diagonalSpeed = (currentSpeed + strafeSpeed) / diagonallyWalkingWeight;
currentSpeed = Mathf.Clamp(diagonalSpeed, 0, currentSpeed); // Prevent diagonal speed being more than forward speed.
}
}
private float SlopeMultiplier() {
float angle = Vector3.Angle(groundContactNormal, Vector3.up);
return slopeCurveModifier.Evaluate(angle);
}
private void StickToGroundHelper() {
RaycastHit hitInfo;
if (Physics.SphereCast(transform.position, capsule.radius, Vector3.down, out hitInfo, ((capsule.height / 2f) - capsule.radius) + stickToGroundHelperDistance)) {
if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f)
rigidBody.velocity = Vector3.ProjectOnPlane(rigidBody.velocity, hitInfo.normal);
}
}
// Sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom
private void GroundCheck() {
isPreviouslyGrounded = isGrounded;
RaycastHit hitInfo;
if (Physics.SphereCast(capsule.transform.position + capsule.center, capsule.radius, Vector3.down, out hitInfo, (capsule.height / 2f) - capsule.radius + groundCheckDistance)) {
isGrounded = true;
groundContactNormal = hitInfo.normal;
} else {
isGrounded = false;
groundContactNormal = Vector3.up;
}
}
public void ControlMovement(Movement moving, Strafe strafe, bool isSprinting, bool isJumping) {
this.moving = moving;
this.strafing = strafe;
this.isSprinting = isSprinting;
this.isJumping = isJumping;
}
public void StopMovement() {
this.moving = Movement.STOPPED;
this.strafing = Strafe.STOPPED;
this.isSprinting = false;
this.isJumping = false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment