123 lines
4.9 KiB
C#
123 lines
4.9 KiB
C#
|
using UnityEngine;
|
|||
|
|
|||
|
namespace NeonTea.Quakeball.Player {
|
|||
|
/// <summary>The central glue class for players (both local and remote).</summary>
|
|||
|
/// <remarks>Other classes will handle netcode/inputs, and then speak to this class in order to make the little people run around.</remarks>
|
|||
|
[RequireComponent(typeof(CharacterController))]
|
|||
|
public class Player : MonoBehaviour {
|
|||
|
|
|||
|
/// <summary>How long after running off a cliff should the player be considered "on ground"?</summary>
|
|||
|
public float CoyoteTime;
|
|||
|
|
|||
|
public MoveStyle RunningMoveStyle;
|
|||
|
public MoveStyle CrouchingMoveStyle;
|
|||
|
public Transform Head;
|
|||
|
|
|||
|
[Header("Player head status")]
|
|||
|
/// <summary>The euler angle of the player's head.</summary>
|
|||
|
public Quaternion HeadRotation;
|
|||
|
|
|||
|
[Header("Player movement status")]
|
|||
|
/// <summary>The direction the player is going.</summary>
|
|||
|
public Vector3 MoveDirection;
|
|||
|
|
|||
|
/// <summary>The player's desire to jump currently.</summary>
|
|||
|
public bool Jumping;
|
|||
|
|
|||
|
/// <summary>The amount of movement the player wants to happen.</summary>
|
|||
|
/// <remarks>Without analog controls, always 0 or 1.</remarks>
|
|||
|
public float InputSpeed;
|
|||
|
|
|||
|
/// <summary>The way the player is moving.</summary>
|
|||
|
public MoveStyle MoveStyle;
|
|||
|
|
|||
|
[Header("Runtime computed values")]
|
|||
|
/// <summary>The speed at which the player is currently moving across the ground.</summary>
|
|||
|
public Vector3 GroundVelocity;
|
|||
|
|
|||
|
/// <summary>The speed at which the player is rising or falling.</summary>
|
|||
|
public Vector3 GravitationalVelocity;
|
|||
|
|
|||
|
/// <summary>The timestamp of when the player was last on the ground.</summary>
|
|||
|
public float GroundedTime;
|
|||
|
|
|||
|
[Header("Misc. technical knobs")]
|
|||
|
public float GroundCastLength = 0.2f;
|
|||
|
public LayerMask GroundLayer;
|
|||
|
|
|||
|
[Header("Debug settings")]
|
|||
|
public bool ShowGroundCast;
|
|||
|
|
|||
|
private CharacterController CharacterController;
|
|||
|
private Vector3 FeetPosition;
|
|||
|
|
|||
|
/// <summary>The normal of the ground below the player. If there is no ground, it's <c>Vector3.up</c> by default.</summary>
|
|||
|
public Vector3 GroundCast() {
|
|||
|
RaycastHit Hit;
|
|||
|
if (ShowGroundCast) {
|
|||
|
Debug.DrawLine(FeetPosition, FeetPosition - Vector3.up, Color.red, 1f);
|
|||
|
}
|
|||
|
if (Physics.Raycast(FeetPosition, -Vector3.up, out Hit, GroundCastLength, GroundLayer)) {
|
|||
|
return Hit.normal;
|
|||
|
} else {
|
|||
|
return Vector3.up;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool IsGrounded() {
|
|||
|
return Time.fixedTime - GroundedTime <= CoyoteTime;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Sets <c>Heading</c> via euler angles.</summary>
|
|||
|
public void SetRotation(float xAngle, float yAngle) {
|
|||
|
MoveDirection = Quaternion.Euler(xAngle, yAngle, 0) * Vector3.forward;
|
|||
|
}
|
|||
|
|
|||
|
private void Awake() {
|
|||
|
CharacterController = GetComponent<CharacterController>();
|
|||
|
FeetPosition = transform.position + CharacterController.center - Vector3.up * (CharacterController.height / 2 + CharacterController.skinWidth / 2);
|
|||
|
}
|
|||
|
|
|||
|
private void Update() {
|
|||
|
Head.localRotation = Quaternion.Lerp(Head.localRotation, HeadRotation, 10f * Time.deltaTime);
|
|||
|
}
|
|||
|
|
|||
|
private void FixedUpdate() {
|
|||
|
bool Grounded = IsGrounded();
|
|||
|
|
|||
|
if (Grounded) {
|
|||
|
GravitationalVelocity = Vector3.zero;
|
|||
|
} else {
|
|||
|
GravitationalVelocity += Physics.gravity * Time.fixedDeltaTime;
|
|||
|
}
|
|||
|
|
|||
|
float FrictionVelocityFactor = Mathf.Max(GroundVelocity.magnitude, MoveStyle.StopVelocity);
|
|||
|
float Deccel = FrictionVelocityFactor * Time.fixedDeltaTime;
|
|||
|
if (Grounded) {
|
|||
|
Deccel *= MoveStyle.Friction;
|
|||
|
} else {
|
|||
|
Deccel *= MoveStyle.AirFriction;
|
|||
|
}
|
|||
|
float FrictionedVelocity = Mathf.Max(0, GroundVelocity.magnitude - Deccel);
|
|||
|
GroundVelocity = GroundVelocity.normalized * FrictionedVelocity;
|
|||
|
|
|||
|
Vector3 GroundNormal = GroundCast();
|
|||
|
Vector3 FixedHeading = Vector3.ProjectOnPlane(MoveDirection, GroundNormal).normalized;
|
|||
|
float CurrentSpeed = Vector3.Dot(GroundVelocity, FixedHeading);
|
|||
|
float Acceleration = MoveStyle.TargetVelocity * Time.fixedDeltaTime;
|
|||
|
if (Grounded) {
|
|||
|
Acceleration *= MoveStyle.Acceleration;
|
|||
|
} else {
|
|||
|
Acceleration *= MoveStyle.AirAcceleration;
|
|||
|
}
|
|||
|
Acceleration = Mathf.Min(Acceleration, MoveStyle.TargetVelocity - CurrentSpeed);
|
|||
|
GroundVelocity += FixedHeading * Acceleration;
|
|||
|
|
|||
|
CharacterController.Move((GroundVelocity + GravitationalVelocity) * Time.fixedDeltaTime);
|
|||
|
if (CharacterController.isGrounded) {
|
|||
|
GroundedTime = Time.fixedTime;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|