- Increased SFX pool size from 8 to 16 in AudioManager - Enabled preloadAudioData for multiple SFX files - Removed unused VisualScripting import from Checkpoint script - Reordered using statements in several scripts for consistency - Added CoinCollectible component to Coin prefab with pickup effects - Updated sorting layers and mesh LOD settings in Coin prefabs - Fixed typo in Interfaces.cs: optionaAttachPoint to optionalAttachPoint - Modified AddCoin method to accept count parameter and handle coin overflow - Moved projectile scripts (BasicProjectile, EnemyBasicProjectile, PhysicsProjectile, PickableProjectile) - Cleaned up unused namespaces and imports across various scripts
211 lines
7.5 KiB
C#
211 lines
7.5 KiB
C#
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
|
||
using RR.Common.Interfaces;
|
||
using RR.Game2D.Core.Managers;
|
||
using RR.Game2D.Behaviour.Camera;
|
||
using RR.Game2D.Behaviour.ScriptableObjects;
|
||
|
||
namespace RR.Game2D.Behaviour
|
||
{
|
||
[DisallowMultipleComponent]
|
||
[RequireComponent(typeof(Rigidbody2D),
|
||
typeof(Collider2D),
|
||
typeof(Animator))]
|
||
public class PlayerController : MonoBehaviour, IDamageable
|
||
{
|
||
|
||
[Header("Управление")]
|
||
[SerializeField] private InputActionReference moveAction; // Value(Vector2), например "Move" (A/D, ←/→, stick)
|
||
[SerializeField] private InputActionReference jumpAction;
|
||
|
||
[Header("Горизонтальное движение")]
|
||
[SerializeField] private float moveSpeed = 8f;
|
||
[SerializeField] private float accel = 40f;
|
||
[SerializeField] private float decel = 20f;
|
||
|
||
[Header("Вертикальное движение")]
|
||
[SerializeField] public float jumpVelocity = 50f;
|
||
[SerializeField] private float jumpCut = 2f;
|
||
public bool IsGrounded => Grounded();
|
||
|
||
[Header("Проверка призмеления")]
|
||
[SerializeField] private Transform groundCheck;
|
||
[SerializeField] private float groundCheckRadius = 0.05f;
|
||
[SerializeField] private LayerMask groundMask;
|
||
|
||
[Header("Аниматор [Опционально]")]
|
||
[SerializeField] private Animator animator; // оставить в инспекторе на всякий случай
|
||
|
||
[Header("Здоровье")]
|
||
[SerializeField] private int maxHealth = 3;
|
||
private Health _health;
|
||
public float CurrentHealth => _health?.CurrentHealth ?? 0;
|
||
public float MaxHealth => _health?.MaxHealth ?? 0;
|
||
|
||
[Header("FX")]
|
||
[SerializeField] public SFXAsset stepSounds;
|
||
[SerializeField] public SFXAsset hurtSounds;
|
||
[SerializeField] public SFXAsset jumpSounds;
|
||
|
||
private Rigidbody2D rb;
|
||
// private Collider2D col;
|
||
|
||
private float _inputX;
|
||
private bool _jumpPressed, _jumpReleased, _jumpHold;
|
||
|
||
private float _fallSpeedYDampingChangeThreshold;
|
||
|
||
// Аниматор
|
||
private static readonly int Jump = Animator.StringToHash("Jump");
|
||
private static readonly int Speed = Animator.StringToHash("Speed");
|
||
private static readonly int VSpeed = Animator.StringToHash("VSpeed");
|
||
private static readonly int GroundedAnimator = Animator.StringToHash("Grounded");
|
||
private static readonly int Death = Animator.StringToHash("Death");
|
||
|
||
void OnEnable()
|
||
{
|
||
moveAction.action.Enable();
|
||
jumpAction.action.Enable();
|
||
}
|
||
|
||
void OnDisable()
|
||
{
|
||
moveAction.action.Disable();
|
||
jumpAction.action.Disable();
|
||
}
|
||
|
||
void Awake()
|
||
{
|
||
rb = GetComponent<Rigidbody2D>();
|
||
//col = GetComponent<Collider2D>();
|
||
rb.freezeRotation = true;
|
||
|
||
_fallSpeedYDampingChangeThreshold = CameraManager.instance._fallSpeedYDampingChangeThreshold;
|
||
|
||
// если не задано в инспекторе — находим автоматически
|
||
if (animator == null)
|
||
animator = GetComponent<Animator>();
|
||
if (animator == null)
|
||
animator = GetComponentInChildren<Animator>(true);
|
||
if (animator == null)
|
||
Debug.LogError($"[{name}] Animator не найден!");
|
||
|
||
_health = GetComponent<Health>();
|
||
if (_health == null)
|
||
{
|
||
_health = gameObject.AddComponent<Health>();
|
||
}
|
||
_health.ResetHealth();
|
||
_health.SetOwner(this);
|
||
_health.SetMaxHealth(maxHealth);
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
// --- Ввод ---
|
||
Vector2 move = moveAction ? moveAction.action.ReadValue<Vector2>() : Vector2.zero;
|
||
_inputX = Mathf.Abs(move.x) > 0.01f ? Mathf.Sign(move.x) : 0f;
|
||
|
||
_jumpPressed |= jumpAction.action.WasPressedThisFrame();
|
||
_jumpReleased |= jumpAction.action.WasReleasedThisFrame();
|
||
_jumpHold |= jumpAction.action.IsPressed();
|
||
|
||
if (_inputX != 0f)
|
||
transform.localEulerAngles = Mathf.Sign(_inputX) > 0 ? Vector3.zero : new Vector3(0, 180f, 0);
|
||
|
||
if (rb.linearVelocity.y < _fallSpeedYDampingChangeThreshold && !CameraManager.instance.IsLerpingYDamping && !CameraManager.instance.LerpedFromPlayerFalling)
|
||
{
|
||
CameraManager.instance.LerpYDamping(true);
|
||
}
|
||
|
||
if (rb.linearVelocity.y >= 0 && !CameraManager.instance.IsLerpingYDamping && CameraManager.instance.LerpedFromPlayerFalling)
|
||
{
|
||
CameraManager.instance.LerpedFromPlayerFalling = false;
|
||
CameraManager.instance.LerpYDamping(false);
|
||
}
|
||
|
||
// --- Анимации ---
|
||
animator.SetFloat(Speed, Mathf.Abs(rb.linearVelocity.x));
|
||
animator.SetFloat(VSpeed, rb.linearVelocity.y);
|
||
animator.SetBool(GroundedAnimator, IsGrounded);
|
||
}
|
||
|
||
private void FixedUpdate()
|
||
{
|
||
// Горизонтальное движение
|
||
float target = _inputX * moveSpeed;
|
||
float rate = (Mathf.Abs(target) > 0.01) ? accel : decel;
|
||
float velocityX = Mathf.MoveTowards(rb.linearVelocity.x, target, rate * Time.fixedDeltaTime);
|
||
|
||
Vector2 newVelocity = rb.linearVelocity;
|
||
newVelocity.x = velocityX;
|
||
|
||
// Вертикальное движение
|
||
if ((_jumpPressed || _jumpHold)&& IsGrounded)
|
||
newVelocity.y = jumpVelocity;
|
||
|
||
if (_jumpReleased && newVelocity.y > 0)
|
||
newVelocity.y /= Mathf.Max(1f, jumpCut);
|
||
|
||
rb.linearVelocity = newVelocity;
|
||
|
||
_jumpPressed = _jumpReleased = _jumpHold = false;
|
||
|
||
// Смерть
|
||
// if (transform.position.y < deathY) Die();
|
||
}
|
||
|
||
private void OnTriggerEnter2D(Collider2D collision)
|
||
{
|
||
// if (collision.CompareTag("Coin"))
|
||
// {
|
||
// GameManager.Instance.AddCoin();
|
||
// Destroy(collision.gameObject);
|
||
// }
|
||
|
||
if (collision.CompareTag("Finish"))
|
||
{
|
||
GameManager.Instance.NextLevel();
|
||
}
|
||
|
||
if (collision.CompareTag("Enemy"))
|
||
{
|
||
TakeDamage(1f);
|
||
}
|
||
}
|
||
|
||
public void TakeDamage(float amount)
|
||
{
|
||
_health.TakeDamage(amount);
|
||
}
|
||
public void Heal(float amount)
|
||
{
|
||
_health.Heal(amount);
|
||
}
|
||
|
||
public void Die()
|
||
{
|
||
gameObject.layer = LayerMask.NameToLayer("No Collision");
|
||
rb.constraints = RigidbodyConstraints2D.FreezeAll;
|
||
animator.SetTrigger(Death);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Метод для проверки соприкосновения с землей
|
||
/// </summary>
|
||
/// <returns>bool isGrounded - на земле ли игрок</returns>
|
||
bool Grounded()
|
||
{
|
||
Collider2D hit = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundMask);
|
||
bool isGrounded = hit != null
|
||
&& hit.CompareTag("Ground");
|
||
//&& rb.linearVelocity.y < 0.01f;
|
||
|
||
animator.SetBool(Jump, !isGrounded);
|
||
|
||
return isGrounded;
|
||
}
|
||
}
|
||
}
|