Created
April 3, 2017 16:40
-
-
Save Maxstupo/387c0866d66dcde716b5a04593105075 to your computer and use it in GitHub Desktop.
MovementController.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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