Files
decay-grid/Assets/Scripts/Infrastructure/Unity/PlayerController.cs

286 lines
9.1 KiB
C#

using System;
using Core.Domain.Status;
using Core.Domain.Status.Effects;
using KBCore.Refs;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Infrastructure.Unity
{
[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
[Header("Movement Settings")]
[SerializeField]
private float moveSpeed = 8f;
[SerializeField] private float maxVelocityChange = 10f;
[SerializeField] private float snapForce = 15f;
[Header("Jump Settings")]
[SerializeField] private float jumpForce = 12f; // Adjusted for snappy feel
[SerializeField] private float coyoteTime = 0.1f; // Grace period after falling
[SerializeField] private float gravityMultiplier = 2.5f; // Stronger gravity for less "floaty" feel
[Header("Controls")]
[SerializeField] private bool useCameraRelativeMovement = true;
[Header("Interaction")]
[SerializeField] private LayerMask tileLayer;
[SerializeField] private float groundCheckDistance = 1.1f; // Slightly more than half player height
[Self][SerializeField] private Rigidbody rb;
[Self][SerializeField] private MeshRenderer meshRenderer;
private InputSystem_Actions _actions;
private Vector2 _moveInput;
private Transform _camTransform;
private MaterialPropertyBlock _propBlock;
private static readonly int ColorProperty = Shader.PropertyToID("_BaseColor");
private static readonly int EmissionColorProperty = Shader.PropertyToID("_EmissionColor");
private Color _defaultColor = Color.white;
private bool _isGrounded;
private float _coyoteTimeCounter;
private bool _jumpPressed;
public Rigidbody Rigidbody => rb;
public StatusManager Status { get; private set; }
private void OnEnable()
{
_actions.Player.Enable();
_actions.Player.Move.performed += OnMovePerformed;
_actions.Player.Move.canceled += OnMoveCanceled;
_actions.Player.Jump.performed += OnJumpPerformed;
}
private void OnDisable()
{
_actions.Player.Move.performed -= OnMovePerformed;
_actions.Player.Move.canceled -= OnMoveCanceled;
_actions.Player.Jump.performed -= OnJumpPerformed;
_actions.Player.Disable();
}
private void Awake()
{
_actions = new InputSystem_Actions();
Status = new StatusManager();
if (Camera.main)
{
_camTransform = Camera.main.transform;
}
rb.freezeRotation = true;
rb.useGravity = false;
_propBlock = new MaterialPropertyBlock();
if (meshRenderer.material.HasProperty(ColorProperty))
{
_defaultColor = meshRenderer.material.GetColor(ColorProperty);
}
}
private void Update()
{
Status.Tick(Time.deltaTime);
UpdateVisuals();
if (_isGrounded)
{
_coyoteTimeCounter = coyoteTime;
}
else
{
_coyoteTimeCounter -= Time.deltaTime;
}
if (_jumpPressed)
{
_jumpPressed = false;
if (_coyoteTimeCounter > 0f || Status.CurrentCapabilities.CanHover)
{
PerformJump();
}
}
}
private void FixedUpdate()
{
CheckGround();
HandleMovement();
ApplyGravity();
// Interaction with tiles (Decay Logic)
if (_isGrounded && Status.CurrentCapabilities.CanTriggerDecay)
{
InteractWithGround();
}
}
private void CheckGround()
{
_isGrounded = Physics.Raycast(transform.position, Vector3.down, groundCheckDistance, tileLayer);
}
private void PerformJump()
{
var vel = rb.linearVelocity;
vel.y = 0;
rb.linearVelocity = vel;
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
_coyoteTimeCounter = 0f;
_isGrounded = false;
}
private void ApplyGravity()
{
if (Status.CurrentCapabilities.CanHover) return;
var gravity = Physics.gravity.y * gravityMultiplier;
rb.AddForce(Vector3.up * gravity, ForceMode.Acceleration);
}
private void HandleMovement()
{
var currentSpeed = moveSpeed * Status.CurrentCapabilities.SpeedMultiplier;
var targetVelocity = Vector3.zero;
var snapAxis = Vector3.zero;
Vector3 desiredDirection;
if (_moveInput.sqrMagnitude < 0.01f)
{
desiredDirection = Vector3.zero;
}
else if (useCameraRelativeMovement && _camTransform)
{
var camForward = _camTransform.forward;
var camRight = _camTransform.right;
camForward.y = 0;
camRight.y = 0;
camForward.Normalize();
camRight.Normalize();
desiredDirection = (camForward * _moveInput.y + camRight * _moveInput.x).normalized;
}
else
{
desiredDirection = new Vector3(_moveInput.x, 0, _moveInput.y).normalized;
}
if (desiredDirection.sqrMagnitude > 0.01f)
{
if (Mathf.Abs(desiredDirection.x) > Mathf.Abs(desiredDirection.z))
{
targetVelocity = new Vector3(Mathf.Sign(desiredDirection.x) * currentSpeed, 0, 0);
snapAxis = Vector3.forward;
}
else
{
targetVelocity = new Vector3(0, 0, Mathf.Sign(desiredDirection.z) * currentSpeed);
snapAxis = Vector3.right;
}
}
var velocity = rb.linearVelocity;
var velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0f;
rb.AddForce(velocityChange, ForceMode.VelocityChange);
if (snapAxis != Vector3.zero)
{
ApplySnapping(snapAxis);
}
else
{
ApplySnapping(Vector3.right);
ApplySnapping(Vector3.forward);
}
}
private void ApplySnapping(Vector3 axis)
{
var currentPos = Vector3.Dot(transform.position, axis);
var targetPos = Mathf.Round(currentPos);
var diff = targetPos - currentPos;
var correction = axis * (diff * snapForce);
rb.AddForce(correction, ForceMode.Acceleration);
}
private void InteractWithGround()
{
if (Physics.Raycast(transform.position, Vector3.down, out var hit, groundCheckDistance, tileLayer))
{
if (hit.collider.TryGetComponent<TileViewAdapter>(out var tileAdapter))
{
tileAdapter.OnPlayerStep();
}
}
}
private void OnMovePerformed(InputAction.CallbackContext ctx)
{
_moveInput = ctx.ReadValue<Vector2>();
}
private void OnMoveCanceled(InputAction.CallbackContext ctx)
{
_moveInput = Vector2.zero;
}
private void OnJumpPerformed(InputAction.CallbackContext ctx)
{
if (ctx.performed) _jumpPressed = true;
}
private void UpdateVisuals()
{
var targetColor = _defaultColor;
var emissionColor = Color.black;
var caps = Status.CurrentCapabilities;
if (caps.CanHover)
{
targetColor = EffectColors.HoverColor;
emissionColor = EffectColors.HoverColor * 0.5f;
}
else if (!caps.CanTriggerDecay)
{
targetColor = EffectColors.LightFootedColor;
}
else if (caps.SpeedMultiplier > 1.2f)
{
targetColor = EffectColors.SpeedBoostColor;
emissionColor = EffectColors.SpeedBoostColor * 0.5f;
}
meshRenderer.GetPropertyBlock(_propBlock);
var currentColor = _propBlock.GetColor(ColorProperty);
if (currentColor.a == 0) currentColor = _defaultColor;
var newColor = Color.Lerp(currentColor, targetColor, Time.deltaTime * 5f);
_propBlock.SetColor(ColorProperty, newColor);
_propBlock.SetColor(EmissionColorProperty, emissionColor);
meshRenderer.SetPropertyBlock(_propBlock);
}
}
}