quakeball/Assets/Scripts/Player/Player.cs

150 lines
6.3 KiB
C#
Raw Normal View History

2020-08-02 02:52:38 +02:00
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;
/// <summary>How often should the player's movement be updated? Used by Local and Remote -Player classes.</summary>
public float UpdateFrequency = 1f;
2020-08-06 19:58:10 +02:00
public MoveStyle[] MoveStyles;
2020-08-02 02:52:38 +02:00
public Transform Head;
public Transform Body;
2020-08-05 23:15:28 +02:00
public Transform Gun;
2020-08-06 19:58:10 +02:00
public Animator GunBobber;
2020-08-02 02:52:38 +02:00
[Header("Player rotation status")]
/// <summary>The pitch of the player's head.</summary>
public float Pitch;
/// <summary>The total yaw of the player. Head yaw is Yaw - BodyYaw.</summary>
public float Yaw;
/// <summary>The yaw of the player body. Calculated from MoveDirection.</summary>
public float BodyYaw;
2020-08-02 02:52:38 +02:00
[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>
2020-08-06 19:58:10 +02:00
public byte CurrentMoveStyle = 0;
public MoveStyle MoveStyle => MoveStyles[CurrentMoveStyle];
2020-08-02 02:52:38 +02:00
[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;
2020-08-05 23:15:28 +02:00
public bool ShowMoveVector;
2020-08-02 02:52:38 +02:00
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.time - GroundedTime <= CoyoteTime && Vector3.Dot(GravitationalVelocity, Vector3.down) >= 0;
2020-08-02 02:52:38 +02:00
}
private void Awake() {
CharacterController = GetComponent<CharacterController>();
FeetPosition = transform.position + CharacterController.center - Vector3.up * (CharacterController.height / 2 + CharacterController.skinWidth / 2);
}
private void Update() {
if (MoveDirection.magnitude > 0) {
BodyYaw = Vector3.SignedAngle(Vector3.forward, MoveDirection, Vector3.up);
}
Body.localRotation = Quaternion.Lerp(Body.localRotation, Quaternion.Euler(0, BodyYaw, 0), 20f * Time.deltaTime);
Head.localRotation = Quaternion.Lerp(Head.localRotation, Quaternion.Euler(Pitch, Yaw, 0), 15f * Time.deltaTime);
UpdateMovement();
2020-08-02 02:52:38 +02:00
}
private void UpdateMovement() {
2020-08-02 02:52:38 +02:00
bool Grounded = IsGrounded();
if (Grounded) {
if (Vector3.Dot(Vector3.down, GravitationalVelocity) > 0) {
GravitationalVelocity = Vector3.zero;
}
if (Jumping) {
GravitationalVelocity = Vector3.up * MoveStyle.JumpVelocity;
}
2020-08-02 02:52:38 +02:00
} else {
GravitationalVelocity += Physics.gravity * Time.deltaTime;
2020-08-02 02:52:38 +02:00
}
float FrictionVelocityFactor = Mathf.Max(GroundVelocity.magnitude, MoveStyle.StopVelocity);
float Deccel = FrictionVelocityFactor * Time.deltaTime;
2020-08-02 02:52:38 +02:00
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.deltaTime;
2020-08-02 02:52:38 +02:00
if (Grounded) {
Acceleration *= MoveStyle.Acceleration;
} else {
Acceleration *= MoveStyle.AirAcceleration;
}
Acceleration = Mathf.Min(Acceleration, MoveStyle.TargetVelocity - CurrentSpeed);
GroundVelocity += FixedHeading * Acceleration;
2020-08-05 23:15:28 +02:00
Vector3 FinalMoveVector = GroundVelocity + GravitationalVelocity;
CharacterController.Move(FinalMoveVector * Time.deltaTime);
2020-08-02 02:52:38 +02:00
if (CharacterController.isGrounded) {
GroundedTime = Time.time;
2020-08-02 02:52:38 +02:00
}
2020-08-05 23:15:28 +02:00
if (ShowMoveVector) {
Debug.DrawLine(
transform.position + CharacterController.center,
transform.position + CharacterController.center + FinalMoveVector,
Color.green, 1.0f
);
}
2020-08-06 19:58:10 +02:00
float TargetBobbiness = Grounded ? GroundVelocity.magnitude / MoveStyle.TargetVelocity : 0;
GunBobber.SetLayerWeight(1, Mathf.Lerp(GunBobber.GetLayerWeight(1), TargetBobbiness, 10f * Time.deltaTime));
2020-08-02 02:52:38 +02:00
}
}
}