TOOLS GAMES DEVLOG DOCS ABOUT CONTACT
HOME > DOCS > Unity / C# > 2D 탑다운(Top-Down) 8방향 이동, 대시(Dash) 및 넉백 물리 제어
Unity / C# Beginner Unity 2022.3+ / Unity 6

2D 탑다운(Top-Down) 8방향 이동, 대시(Dash) 및 넉백 물리 제어

대각선 속도 왜곡을 방지하는 벡터 정규화, 무적 시간 동반 대시(Dash), 외부 피격 넉백(Knockback) 벡터를 부드럽게 감쇠·합산하는 탑다운 액션 모빌리티 구현.

#Unity #2D #TopDown #Movement #Dash #Knockback
핵심 개념탑다운 2D 게임에서 x와 y 입력을 단순히 더하면 대각선 이동 속도가 약 1.414배($\sqrt{2}$) 빨라집니다. 입력 벡터를 정규화(normalized)하고, 플레이어의 의도된 이동과 적 공격에 의한 넉백(Knockback) 속도를 분리하여 지수 감쇠(Decay)시키는 구조를 갖춰야 자연스러운 피격 리액션이 완성됩니다.

1. 탑다운 플레이어 컨트롤러 (대시 및 넉백 시스템)

TopDownCharacterController.cs
using System.Collections;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class TopDownCharacterController : MonoBehaviour
{
    [Header("기본 이동")]
    [SerializeField] private float walkSpeed = 6f;
    [SerializeField] private float acceleration = 40f;

    [Header("대시 설정")]
    [SerializeField] private float dashSpeed = 18f;
    [SerializeField] private float dashDuration = 0.18f;
    [SerializeField] private float dashCooldown = 0.8f;

    [Header("넉백 감쇠")]
    [SerializeField] private float knockbackDecay = 12f;

    private Rigidbody2D _rb;
    private Vector2 _moveInput;
    private Vector2 _currentMoveVelocity;
    private Vector2 _knockbackVelocity;

    private bool _isDashing;
    private float _dashCooldownTimer;

    public bool IsInvulnerable => _isDashing;

    private void Awake()
    {
        _rb = GetComponent<Rigidbody2D>();
        _rb.gravityScale = 0f; // 탑다운 중력 제거
    }

    private void Update()
    {
        if (_dashCooldownTimer > 0f)
            _dashCooldownTimer -= Time.deltaTime;

        if (_isDashing) return;

        // 8방향 입력 수집 및 정규화
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        _moveInput = new Vector2(h, v).normalized;

        // 대시 입력
        if (Input.GetKeyDown(KeyCode.Space) && _dashCooldownTimer <= 0f && _moveInput != Vector2.zero)
        {
            StartCoroutine(DashRoutine(_moveInput));
        }
    }

    private void FixedUpdate()
    {
        // 넉백 지수 감쇠
        _knockbackVelocity = Vector2.MoveTowards(_knockbackVelocity, Vector2.zero, knockbackDecay * Time.fixedDeltaTime);

        if (_isDashing) return;

        // 부드러운 가속 보간
        Vector2 targetVelocity = _moveInput * walkSpeed;
        _currentMoveVelocity = Vector2.MoveTowards(_currentMoveVelocity, targetVelocity, acceleration * Time.fixedDeltaTime);

        // 이동 속도와 외부 넉백 합산 적용
        _rb.linearVelocity = _currentMoveVelocity + _knockbackVelocity;
    }

    private IEnumerator DashRoutine(Vector2 direction)
    {
        _isDashing = true;
        _dashCooldownTimer = dashCooldown;

        // 대시 순간 기존 넉백 초기화 및 고속 돌진
        _knockbackVelocity = Vector2.zero;
        _rb.linearVelocity = direction * dashSpeed;

        yield return new WaitForSeconds(dashDuration);

        _currentMoveVelocity = direction * walkSpeed;
        _isDashing = false;
    }

    /// <summary>
    /// 적 타격 시 호출되는 외부 넉백 함수
    /// </summary>
    public void ApplyKnockback(Vector2 direction, float force)
    {
        if (_isDashing) return; // 대시 무적 중 넉백 무시
        _knockbackVelocity = direction.normalized * force;
    }
}

2. 피격 판정 및 넉백 전달 예제

EnemyHitbox.cs
using UnityEngine;

public class EnemyHitbox : MonoBehaviour
{
    [SerializeField] private float damage = 10f;
    [SerializeField] private float knockbackForce = 15f;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.TryGetComponent<TopDownCharacterController>(out var player))
        {
            if (player.IsInvulnerable) return;

            Vector2 pushDir = (collision.transform.position - transform.position).normalized;
            player.ApplyKnockback(pushDir, knockbackForce);
        }
    }
}

3. 성능 및 설계 체크리스트

  • Vector2.normalized 대각선 보정: 대각선 입력((1, 1))의 크기는 약 1.414이므로, .normalized 처리를 하지 않으면 대각선 이동 시 적이나 총알을 따돌리기 쉬워지는 밸런스 붕괴가 일어납니다.
  • 충돌 콜라이더 회전 고정: 2D 캐릭터 이동 시 장애물 모서리와 비스듬히 충돌할 때 캐릭터가 회전하지 않도록 Rigidbody2D.constraints = RigidbodyConstraints2D.FreezeRotation을 반드시 활성화합니다.