158 lines
5.4 KiB
C#
158 lines
5.4 KiB
C#
using UnityEngine;
|
||
using RR.Game2D.Core;
|
||
|
||
namespace RR.Game2D.Behaviour
|
||
{
|
||
[DisallowMultipleComponent]
|
||
[RequireComponent(typeof(Rigidbody2D),
|
||
typeof(Collider2D),
|
||
typeof(Animator))]
|
||
public class PlayerController : MonoBehaviour
|
||
{
|
||
|
||
[Header("Горизонтальное движение")]
|
||
[SerializeField] private float moveSpeed = 8f;
|
||
[SerializeField] private float accel = 40f;
|
||
[SerializeField] private float decel = 20f;
|
||
|
||
[Header("Вертикальное движение")]
|
||
[SerializeField] private float maxJumpHeight = 5f;
|
||
[SerializeField] private float jumpCooldown = 0.1f;
|
||
[SerializeField] private float jumpBuffer = 0.1f;
|
||
[SerializeField, Range(0f, 1f)] private float jumpCut = 0.5f;
|
||
|
||
private float spriteScale = 0.35f;
|
||
|
||
private Rigidbody2D rb;
|
||
private Collider2D col;
|
||
|
||
float inputX;
|
||
float lastGroundedTime;
|
||
float lastJumpPressedTime;
|
||
|
||
bool IsGrounded => Grounded();
|
||
|
||
[Header("Аниматор [Опционально]")]
|
||
[SerializeField] private Animator animator; // оставить в инспекторе на всякий случай
|
||
|
||
void Awake()
|
||
{
|
||
rb = GetComponent<Rigidbody2D>();
|
||
col = GetComponent<Collider2D>();
|
||
rb.freezeRotation = true;
|
||
lastJumpPressedTime = -999f;
|
||
|
||
// если не задано в инспекторе — находим автоматически
|
||
if (animator == null)
|
||
animator = GetComponent<Animator>();
|
||
if (animator == null)
|
||
animator = GetComponentInChildren<Animator>(true);
|
||
|
||
if (animator == null)
|
||
Debug.LogError($"[{name}] Animator не найден!");
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
// Передвижение
|
||
inputX = Input.GetAxisRaw("Horizontal");
|
||
|
||
//if (Input.GetButtonDown("Jump"))
|
||
// lastJumpPressedTime = Time.time;
|
||
|
||
if (Input.GetButton("Jump"))
|
||
lastJumpPressedTime = Time.time;
|
||
|
||
if (Input.GetButtonUp("Jump")) // && rb.linearVelocity.y > 0f)
|
||
rb.linearVelocity = new Vector2(rb.linearVelocity.x, rb.linearVelocity.y * jumpCut);
|
||
|
||
if (inputX != 0f)
|
||
transform.localScale = new Vector3(Mathf.Sign(inputX) * spriteScale, spriteScale, spriteScale);
|
||
|
||
if (IsGrounded) lastGroundedTime = Time.time;
|
||
|
||
// Данные для анимаций
|
||
animator.SetFloat("Speed", Mathf.Abs(rb.linearVelocity.x));
|
||
animator.SetFloat("VSpeed", rb.linearVelocity.y);
|
||
animator.SetBool("Grounded", IsGrounded);
|
||
}
|
||
|
||
|
||
|
||
void FixedUpdate()
|
||
{
|
||
float target = inputX * moveSpeed;
|
||
float rate = (Mathf.Abs(target) > 0.01f) ? accel : decel;
|
||
float vx = Mathf.MoveTowards(rb.linearVelocity.x, target, rate * Time.fixedDeltaTime);
|
||
|
||
if (transform.position.y < -5) Die();
|
||
|
||
// Возможность прыжка
|
||
bool canJump = (Time.time - lastGroundedTime) <= jumpCooldown;
|
||
bool wantJump = (Time.time - lastJumpPressedTime) <= jumpBuffer;
|
||
|
||
if (wantJump && canJump && IsGrounded)
|
||
{
|
||
float vy = CalcJumpSpeed(maxJumpHeight);
|
||
rb.linearVelocity = new Vector2(vx, vy);
|
||
animator.SetBool("Jump", true);
|
||
lastJumpPressedTime = -999f;
|
||
return;
|
||
}
|
||
|
||
rb.linearVelocity = new Vector2(vx, rb.linearVelocity.y);
|
||
}
|
||
|
||
private void OnTriggerEnter2D(Collider2D collision)
|
||
{
|
||
if (collision.CompareTag("Coin"))
|
||
{
|
||
GameManager.Instance.AddCoin();
|
||
Destroy(collision.gameObject);
|
||
}
|
||
|
||
if (collision.CompareTag("Finish"))
|
||
{
|
||
GameManager.Instance.NextLevel();
|
||
}
|
||
}
|
||
|
||
//private void OnDestroy() => Debug.Log("Player destroyed");
|
||
|
||
|
||
/// <summary>
|
||
/// Метод для просчитывания скорости прыжка с учетом высоты прыжка
|
||
/// </summary>
|
||
/// <param name="height">Высота прыжка (высшая точка по y)</param>
|
||
/// <returns>float jumpSpeed - высчитанная скорость прыжка</returns>
|
||
float CalcJumpSpeed(float height)
|
||
{
|
||
float g = Physics2D.gravity.y * rb.gravityScale;
|
||
|
||
return Mathf.Sqrt(-2f * g * height);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Метод для проверки соприкосновения с землей
|
||
/// </summary>
|
||
/// <returns>bool isGrounded - на земле ли игрок</returns>
|
||
bool Grounded()
|
||
{
|
||
RaycastHit2D hit = Physics2D.CircleCast(new Vector2(col.bounds.center.x, col.bounds.min.y), 0.01f, Vector2.down, 0.1f);
|
||
bool isGrounded = hit.collider != null;
|
||
|
||
animator.SetBool("Jump", !isGrounded);
|
||
|
||
return isGrounded;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Метод смерти персонажа.
|
||
/// Базовое поведение - сброс уровня в GameManager
|
||
/// </summary>
|
||
public void Die()
|
||
{
|
||
GameManager.Instance.ResetLevel();
|
||
}
|
||
}
|
||
} |