2026-04-25 15:29:44 -05:00

275 lines
9.0 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using Management;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Player
{
[RequireComponent(typeof(Rigidbody2D))]
public class Movement : MonoBehaviour
{
public event Action OnDeath;
public event Action EnterSpring;
public event Action GetPickup;
private static readonly int Spring = Animator.StringToHash("spring");
private static readonly int Grounded = Animator.StringToHash("grounded");
private static readonly int Speed = Animator.StringToHash("speed");
private static readonly int Vspeed = Animator.StringToHash("vspeed");
[SerializeField] private float runSpeed;
[SerializeField] private float springPower;
[SerializeField] private float jumpForce;
[SerializeField] private float airJumpForce;
[SerializeField] private SpriteRenderer spriteRenderer;
[SerializeField] private float turnDelay;
[SerializeField] private float impactPauseTime;
[SerializeField] private float bouncePower;
[SerializeField] private Animator animator;
[SerializeField] private GameObject deathScreenPrefab;
[Header("Audio")]
[SerializeField] private AudioClip jumpSound;
[SerializeField] private AudioClip airJumpSound;
[SerializeField] private AudioClip pickupSound;
[SerializeField] private AudioClip springSound;
[SerializeField] private AudioClip bumpSound;
[Header("Wind Sound")]
[SerializeField] private AudioSource windSound;
[SerializeField] private float maxSpeedWindSound;
private Animator _springAnimator;
private Rigidbody2D _rb;
private bool _grounded = true;
private bool _facingLeft;
private float _turnDelayTimer;
private float _groundedCheckTimer;
private bool _interact;
private int _airJumpCharges;
private bool _jumpPressedThisFrame;
private bool _fireSpring;
private bool _jump;
private bool _airJump;
private Vector2 _airJumpDir;
private bool _autoDrive = true;
private bool _deathArmed;
private void Awake()
{
_rb = GetComponent<Rigidbody2D>();
}
private void Start()
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
Services.Instance.InputRouter.OnInteract += HandleInteraction;
}
private void OnDestroy()
{
Services.Instance.InputRouter.OnInteract -= HandleInteraction;
}
private void HandleInteraction(bool pressed)
{
_interact = pressed;
_jumpPressedThisFrame = _interact;
}
private void Update()
{
Vector2 mouseScreenPos = Mouse.current.position.ReadValue();
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(
new Vector3(mouseScreenPos.x, mouseScreenPos.y, -Camera.main.transform.position.z)
);
_airJumpDir = ((Vector2)mouseWorldPos - (Vector2)transform.position).normalized;
_groundedCheckTimer += Time.deltaTime;
_turnDelayTimer += Time.deltaTime;
if (!_autoDrive && Mathf.Abs(_rb.linearVelocityX) > 0.1f)
_facingLeft = _rb.linearVelocity.x < 0;
spriteRenderer.flipX = !_facingLeft;
if (_springAnimator && _jumpPressedThisFrame)
{
_groundedCheckTimer = 0f;
_springAnimator.SetTrigger(Spring);
_springAnimator = null;
_grounded = false;
_fireSpring = true;
_autoDrive = false;
_deathArmed = true;
}
if (_grounded && _jumpPressedThisFrame)
{
_grounded = false;
_groundedCheckTimer = 0f;
_jump = true;
_jumpPressedThisFrame = false;
}
if (!_grounded && _airJumpCharges > 0 && _jumpPressedThisFrame)
{
_airJump = true;
}
windSound.volume = Mathf.Lerp(0, 1, Mathf.Abs(_rb.linearVelocityY / maxSpeedWindSound));
_jumpPressedThisFrame = false;
}
private void FixedUpdate()
{
if (_jump)
{
Services.Instance.SFX.PlayOneShot(jumpSound);
_jump = false;
_grounded = false;
_rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
}
if (_airJump)
{
Services.Instance.SFX.PlayOneShot(airJumpSound);
StartCoroutine(ImpactPause(impactPauseTime));
_airJump = false;
_airJumpCharges--;
_rb.linearVelocity = Vector3.zero;
_rb.AddForce(_airJumpDir * airJumpForce, ForceMode2D.Impulse);
}
if (_fireSpring)
{
Services.Instance.SFX.PlayOneShot(springSound);
Services.Instance.BGM.FadeIn(BGM.TrackType.Core);
_fireSpring = false;
_rb.linearVelocity = Vector2.zero;
_rb.AddForce(Vector2.up * springPower, ForceMode2D.Impulse);
}
if (_autoDrive)
{
_rb.linearVelocityX = _facingLeft ? -runSpeed : runSpeed;
}
else
{
if (_rb.linearVelocityX < 0)
{
_facingLeft = true;
}
if (_rb.linearVelocityX > 0)
{
_facingLeft = false;
}
}
animator.SetBool(Grounded, _grounded);
animator.SetFloat(Speed, Mathf.Abs(_rb.linearVelocity.x));
animator.SetFloat(Vspeed, _rb.linearVelocity.y);
}
private IEnumerator ImpactPause(float pauseTime)
{
Time.timeScale = 0f;
yield return new WaitForSecondsRealtime(pauseTime);
Time.timeScale = 1f;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (_deathArmed && other.gameObject.layer == LayerMask.NameToLayer("DeathBox"))
{
HandleDeath();
return;
}
if (other.gameObject.layer == LayerMask.NameToLayer("Spring"))
{
EnterSpring?.Invoke();
_springAnimator = other.gameObject.GetComponent<Animator>();
}
if (other.gameObject.layer == LayerMask.NameToLayer("Pickup"))
{
GetPickup?.Invoke();
Services.Instance.SFX.PlayOneShot(pickupSound);
_airJumpCharges++;
Destroy(other.gameObject);
}
}
private void HandleDeath()
{
OnDeath?.Invoke();
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.layer == LayerMask.NameToLayer("Spring"))
{
_springAnimator = null;
}
}
private void OnCollisionEnter2D(Collision2D other)
{
CheckCollisions(other);
}
private void OnCollisionStay2D(Collision2D other)
{
CheckCollisions(other);
}
private void CheckCollisions(Collision2D other)
{
var contacts = new List<ContactPoint2D>();
other.GetContacts(contacts);
if (_turnDelayTimer >= turnDelay &&
other.gameObject.layer != LayerMask.NameToLayer("Spring") &&
other.gameObject.layer != LayerMask.NameToLayer("Pickup"))
{
foreach (var c in contacts)
{
var x = c.normal.x;
if (Mathf.Abs(x) > 0.1f)
{
_turnDelayTimer = 0f;
if (!_autoDrive)
{
_rb.AddForce(new Vector2(x, 0f) * bouncePower, ForceMode2D.Impulse);
}
else
{
_rb.linearVelocityX = -_rb.linearVelocityX;
_facingLeft = !_facingLeft;
}
Services.Instance.SFX.PlayOneShot(bumpSound);
break;
}
}
}
_grounded = false;
if (_groundedCheckTimer > turnDelay)
{
foreach (var c in contacts)
{
var y = c.normal.y;
_grounded = Mathf.Approximately(1f, y);
if (_grounded)
{
_autoDrive = true;
return;
}
}
}
}
}
}