351 lines
9.0 KiB
C#
351 lines
9.0 KiB
C#
using Extensions;
|
|
using UnityEngine;
|
|
using NaughtyAttributes;
|
|
using Ktyl.Util;
|
|
using System.Collections;
|
|
using UnityEngine.UI;
|
|
|
|
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 float timeSinceStop;
|
|
|
|
[Header("Power Freeze")]
|
|
[SerializeField]
|
|
private SerialFloat objectTimeScale;
|
|
|
|
[SerializeField]
|
|
private SerialFloat frozenTime;
|
|
|
|
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 UpdateFreeze( float dt)
|
|
{
|
|
if ( _powers.Freeze.CanConsume && _inputHandler.InputState.Freeze.GetRawValue() )
|
|
{
|
|
_powers.Freeze.Consume();
|
|
objectTimeScale.Value = 0f;
|
|
Debug.Log( "[PlayerController] time frozen" );
|
|
}
|
|
|
|
if ( _powers.Freeze.timeSinceConsume > frozenTime && TimeIsFrozen )
|
|
{
|
|
UnfreezeTime();
|
|
Debug.Log( "[PlayerController] time unfrozen" );
|
|
}
|
|
|
|
}
|
|
|
|
public bool TimeIsFrozen
|
|
=> objectTimeScale.AsBool;
|
|
|
|
private void UnfreezeTime()
|
|
{
|
|
objectTimeScale.Value = 1f;
|
|
}
|
|
|
|
private void FreezeReset()
|
|
{
|
|
_powers.Freeze.Reset();
|
|
}
|
|
|
|
|
|
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 );
|
|
UpdateFreeze( 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 );
|
|
}
|
|
} |