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