revival/game/Assets/Scripts/Player/PlayerController.cs

306 lines
8.0 KiB
C#

using Extensions;
using UnityEngine;
using NaughtyAttributes;
public class PlayerController : MonoBehaviour
{
[Header("Config")]
[SerializeField]
[Expandable]
private PlayerMovementSettings _movementSettings;
[Header("References")]
[SerializeField]
private PlayerInputHandler _inputHandler;
[SerializeField]
private CharacterController _controller;
[SerializeField]
private PlayerAnimationController _animController;
[SerializeField]
private PlayerPowers _powers;
[SerializeField]
private Transform _graphics;
[SerializeField]
private Renderer[] _renderers;
[Header( "Debug" )]
[SerializeField]
private Color _groundedColor;
[SerializeField]
private Color _jumpColor;
[SerializeField]
private Color _boostColor;
[SerializeField]
private Color _exhaustedColor;
[ShowNonSerializedField]
private Vector2 _surfVelocity;
[ShowNonSerializedField]
private Vector2 _overrideDelta;
[ShowNonSerializedField]
private float _lookAngle;
[ShowNonSerializedField]
private float _yVelocity;
[ShowNonSerializedField]
private bool _grounded;
[ShowNativeProperty]
private bool Still => Mathf.Approximately(_inputHandler?.InputState?.Move.Value.sqrMagnitude ?? 0f, 0f);
[ShowNonSerializedField]
private float _boostTime;
[ShowNonSerializedField]
private float _fallTime;
public bool CanJump => _grounded || _fallTime < _movementSettings.CoyoteTime;
private const float PI = Mathf.PI;
private const float TAU = Mathf.PI * 2f;
private const float HALF_PI = Mathf.PI / 2f;
private enum JumpState
{
None,
Jump,
Boost,
Exhausted,
COUNT,
}
[ShowNonSerializedField]
private JumpState _jumpState;
private void Start()
{
}
private Color GetDebugColor()
{
switch ( _jumpState )
{
case JumpState.None:
return _groundedColor;
case JumpState.Jump:
return _jumpColor;
case JumpState.Boost:
return _boostColor;
case JumpState.Exhausted:
return _exhaustedColor;
}
return Color.red;
}
private float normaliseAngle( float angle )
{
while ( angle > PI )
{
angle -= TAU;
}
while ( angle < -PI )
{
angle += TAU;
}
return angle;
}
private void Land()
{
_powers.Blink.Reset();
_powers.Boost.Reset();
}
private void UpdateLook( float dt )
{
if ( _surfVelocity.sqrMagnitude > 0.01f )
{
float targetAngle = -Vector2.SignedAngle( Vector2.up, _surfVelocity.normalized ) * Mathf.Deg2Rad;
float delta = targetAngle - _lookAngle;
float ls = _movementSettings.LookSpeed * dt;
_lookAngle += Mathf.Clamp( normaliseAngle(delta), -ls, ls );
_lookAngle = normaliseAngle( _lookAngle );
}
}
private void UpdateBlink( float dt )
{
if ( _powers.Blink.CanConsume && _inputHandler.InputState.Blink.GetDown() )
{
_powers.Blink.Consume();
Vector2 blinkDirection = Vector2.up.Rotate( -_lookAngle );
_overrideDelta += blinkDirection * _movementSettings.BlinkDistance;
_surfVelocity += blinkDirection * _movementSettings.BlinkVelocity;
}
}
private void UpdateJump(float dt)
{
_fallTime = _grounded ? 0f : (_fallTime + dt);
switch ( _jumpState )
{
case JumpState.None:
if ( CanJump )
{
if ( _inputHandler.InputState.Jump.GetDown() )
{
_grounded = false;
_jumpState = JumpState.Jump;
_yVelocity = Mathf.Sqrt( 2f * _movementSettings.JumpHeight * _movementSettings.UpGravity );
// always make sure we can blink once per jump
_powers.Blink.Reset();
}
}
else
{
_jumpState = JumpState.Jump;
}
break;
case JumpState.Jump:
if ( _powers.Boost.CanConsume && _inputHandler.InputState.Jump.GetDown( consume: false ) )
{
_powers.Boost.Consume();
_boostTime = 0f;
_jumpState = JumpState.Boost;
if ( _yVelocity < _movementSettings.MinBoostVelocity )
{
_yVelocity = _movementSettings.MinBoostVelocity;
}
}
break;
case JumpState.Boost:
if ( _inputHandler.InputState.Jump.GetRawValue() )
{
_yVelocity += _movementSettings.BoostForce * dt;
_boostTime += dt;
if ( _boostTime > _movementSettings.BoostTime )
{
_jumpState = JumpState.Exhausted;
}
}
break;
default:
break;
}
}
private void UpdateGravity( float dt )
{
if ( _grounded )
{
_yVelocity = _movementSettings.GroundedYVelocity;
_jumpState = JumpState.None;
}
else
{
if ( _yVelocity > 0f && _jumpState == JumpState.Jump )
{
_yVelocity -= ( _movementSettings.UpGravity * dt );
}
else
{
_yVelocity -= ( _movementSettings.DownGravity * dt );
}
}
}
private void UpdateDrag( float dt )
{
Vector2 drag = -_surfVelocity;
float coefficient
= _grounded
? ( Still
? _movementSettings.StillDragCoefficient
: _movementSettings.DragCoefficient )
: ( _jumpState == JumpState.Boost
? _movementSettings.BoostDragCoefficient
: _movementSettings.AirDragCoefficient );
_surfVelocity += drag * (dt * coefficient);
}
private void UpdateMovement( float dt )
{
Vector2 moveVector = _inputHandler.InputState.Move.Value;
_surfVelocity += moveVector * (_movementSettings.BaseMovementForce * dt);
}
private void UpdateDebug()
{
Color debugColor = GetDebugColor();
for ( int i = 0; i < _renderers.Length; ++i )
{
_renderers[ i ].material.color = debugColor;
}
}
private void UpdateAnims()
{
var animParams = new PlayerAnimationController.AnimationParams();
animParams.IsWalking = _controller.velocity.sqrMagnitude > 0.01f;
animParams.InJumpState = _jumpState != JumpState.None;
animParams.InBoostState = _jumpState == JumpState.Boost || _jumpState == JumpState.Exhausted;
_animController.ProcessAnimUpdate( animParams );
}
private void FixedUpdate()
{
float dt = Time.fixedDeltaTime;
_overrideDelta = Vector2.zero;
if ( !_grounded && _controller.isGrounded )
{
Land();
}
_grounded = _controller.isGrounded;
_powers.UpdatePowers( dt, _grounded );
UpdateJump( dt );
UpdateLook( dt );
UpdateBlink( dt );
UpdateGravity( dt );
UpdateDrag( dt );
UpdateMovement( dt );
UpdateDebug();
UpdateAnims();
_controller.Move(
new Vector3(
_surfVelocity.x * dt + _overrideDelta.x,
_yVelocity * dt,
_surfVelocity.y * dt + _overrideDelta.y
)
);
_graphics.rotation = Quaternion.Euler( 0f, _lookAngle * Mathf.Rad2Deg, 0f );
}
}