using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; public class Player : MonoBehaviour { private Rigidbody2D _rigidbody; [SerializeField] int currentHp; private int currentJumpCount; private int comboStep; private Vector2 inputVector; private bool isGrounded; private bool isDashing; private bool isAirAttackSlow; private bool pendingAirAttackSlow; private float attackSlowTimer; private float attackCooldownTimer; private List dashHitEnemies = new List(); [SerializeField] private int maxHp = 100; [SerializeField] private int maxJumpCount = 1; [SerializeField] private float moveSpeed = 9f; [SerializeField] private float jumpSpeed = 18.2f; [SerializeField] private float attackSlowDuration = 0.5f; [SerializeField] private float airMoveMultiplier = 0.4f; [SerializeField] private float fallSpeedClamp = -3f; [SerializeField] private float attackCooldown = 0.4f; [SerializeField] private float dashTime = 0.5f; [SerializeField] private float dashDistance = 5f; [SerializeField] private Vector2[] comboAttackSizes = { new Vector2(1.5f, 1f), new Vector2(2f, 1.5f), new Vector2(3f, 2f) }; [SerializeField] private LayerMask enemyLayer; [SerializeField] private LayerMask obstacleLayer; [SerializeField] private LayerMask groundLayer; private void Start() { _rigidbody = GetComponent(); currentHp = maxHp; currentJumpCount = maxJumpCount; } private void Update() { if (attackCooldownTimer > 0f) attackCooldownTimer -= Time.deltaTime; if (isAirAttackSlow) { attackSlowTimer -= Time.deltaTime; if (attackSlowTimer <= 0f) { isAirAttackSlow = false; pendingAirAttackSlow = false; } } } private void FixedUpdate() { if (pendingAirAttackSlow && _rigidbody.linearVelocity.y < 0f) { pendingAirAttackSlow = false; isAirAttackSlow = true; attackSlowTimer = attackSlowDuration; } if (isAirAttackSlow && _rigidbody.linearVelocity.y < fallSpeedClamp) { _rigidbody.linearVelocity = new Vector2( _rigidbody.linearVelocity.x, fallSpeedClamp ); } } public void SetXVector(InputValue value) { inputVector = value.Get(); } public void MoveUpdate() { if (isDashing) return; float multiplier = isAirAttackSlow ? airMoveMultiplier : 1f; _rigidbody.linearVelocity = new Vector2( inputVector.x * moveSpeed * multiplier, _rigidbody.linearVelocity.y ); } public void Jump() { if (currentJumpCount <= 0) return; currentJumpCount--; _rigidbody.linearVelocity = new Vector2(_rigidbody.linearVelocity.x, 0f); _rigidbody.AddForce(Vector2.up * jumpSpeed, ForceMode2D.Impulse); } public void NormalAttack() { if (isDashing) return; if (attackCooldownTimer > 0f) return; attackCooldownTimer = attackCooldown; Vector2 boxSize = comboAttackSizes[comboStep]; float dir = inputVector.x != 0 ? Mathf.Sign(inputVector.x) : Mathf.Sign(transform.localScale.x); Vector2 origin = (Vector2)transform.position + new Vector2(dir * boxSize.x, 0f); Collider2D[] enemies = Physics2D.OverlapBoxAll(origin, boxSize, 0f, enemyLayer); foreach (Collider2D enemy in enemies) { Vector2 toEnemy = (enemy.transform.position - transform.position).normalized; float dist = Vector2.Distance(transform.position, enemy.transform.position); if (!Physics2D.Raycast(transform.position, toEnemy, dist, obstacleLayer)) { enemy.GetComponent()?.TakeDamage(10); } } comboStep = (comboStep + 1) % comboAttackSizes.Length; if (!isGrounded) { if (_rigidbody.linearVelocity.y < 0f) { isAirAttackSlow = true; attackSlowTimer = attackSlowDuration; } else { pendingAirAttackSlow = true; } } } public void DashAttack() { if (isDashing) { return; } StartCoroutine(DashPlayerRoutine()); } private IEnumerator DashPlayerRoutine() { isDashing = true; isAirAttackSlow = false; pendingAirAttackSlow = false; attackSlowTimer = 0f; attackCooldownTimer = 0f; _rigidbody.linearVelocity = Vector2.zero; dashHitEnemies.Clear(); Vector2 direction = inputVector.normalized; if (direction == Vector2.zero) direction = new Vector2(Mathf.Sign(transform.localScale.x), 0f); if (isGrounded && direction.y < 0) direction.y = 0f; Vector2 startPos = _rigidbody.position; Vector2 targetPos = startPos + direction * dashDistance; float timer = 0f; while (timer < dashTime) { timer += Time.deltaTime; float t = timer / dashTime; _rigidbody.MovePosition(Vector2.Lerp(startPos, targetPos, t)); yield return null; } isDashing = false; } public void AttackOnDash(Collider2D collision) { if ((enemyLayer.value & (1 << collision.gameObject.layer)) == 0) return; if (dashHitEnemies.Contains(collision.gameObject)) return; dashHitEnemies.Add(collision.gameObject); collision.GetComponent()?.TakeDamage(10); } public void CheckGround() { isGrounded = Physics2D.Raycast(transform.position, Vector2.down, 1.25f, groundLayer); if (isGrounded) { currentJumpCount = maxJumpCount; isAirAttackSlow = false; pendingAirAttackSlow = false; attackSlowTimer = 0f; } } public void Heal(int value) { if (value <= 0) return; currentHp += value; if (currentHp > maxHp) currentHp = maxHp; } public void TakeDamage(int damage) { currentHp -= damage; if (currentHp <= 0) Destroy(gameObject); } public bool IsDashing() { return isDashing; } private void OnDrawGizmos() { if (comboAttackSizes == null || comboAttackSizes.Length == 0) return; Gizmos.color = Color.red; int stepIndex = (comboStep < comboAttackSizes.Length) ? comboStep : 0; Vector2 boxSize = comboAttackSizes[stepIndex]; float dir = Mathf.Sign(transform.localScale.x); if (Application.isPlaying && inputVector.x != 0) { dir = Mathf.Sign(inputVector.x); } Vector2 origin = (Vector2)transform.position + new Vector2(dir * boxSize.x, 0f); Gizmos.DrawWireCube(origin, boxSize); } }