1 minute read

작업 내용

그리드로 기존의 이동 기능들 대체

오늘은 기존의 이동 기능들을 그리드 기반으로 대체했다. 기존 코드를 최대한 수정하지 않는 방향으로 진행했는데, 이는 개발 진행 속도를 높이고 충돌 가능성을 줄이는 장점이 있다. 하지만 코드가 지저분해지고 비효율적일 수 있어 다음 주에는 팀원들과 함께 코드 리팩토링을 진행할 계획이다.

기존에는 NavMesh Agent가 목적지 설정 후 자동으로 이동했지만, 이제는 직접 이동을 제어해야 하므로 Update 함수에서 이동을 처리해야 한다. 기존의 Move 관련 함수는 directionspeed를 계산하여 State Enter 시에 설정하도록 변경했다.

public override void Enter()
{
    userUnit.Action.IsAlert = true;
    userUnit.Action.IsOnTheMove = true;
    userUnit.Action.IsTracking = false;

    targetPos = userUnit.Action.TargetPosition;
    direction = (targetPos - (Vector2)userUnit.transform.position).normalized;
    speed = userUnit.Stat.MovementSpeed.TotalValule;
}

UserUnitAction 클래스의 MoveToTarget() 함수는 이제 directionspeed만 받아서 이동하며, 클래스의 필드인 targetPosition으로 거리를 계산해 도착 여부를 판단한다.

public bool MoveToTarget(Vector2 direction, float speed)
{
    if (Vector2.Distance(targetPosition, transform.position) <= speed * Time.deltaTime) {
        transform.position = targetPosition;
        return false;
    }
    transform.Translate(direction * speed * Time.deltaTime, Space.World);
    return true;
}

이렇게 하면 Update 함수에서 이동을 직접 제어할 수 있고, directionspeed는 미리 계산해 계속 전달할 수 있다. 가장 기본적인 UserUnitMoveState에서는 이를 이용해 Update를 처리한다.

public override void Update()
{
    if (!userUnit.Action.MoveToTarget(direction, speed))
    {
        userUnit.StateMachine.ChangeState(userUnit.IdleState);
    }
}

UserUnitMovingAttackState에서도 적이 있는지 판단하고, 없거나 죽었으면 이동을 진행한다.

public override void Update()
{
    if (userUnit.Action.TargetEnemy == null || userUnit.Action.TargetEnemy.Dead)
    {
        if (!userUnit.Action.MoveToTarget(direction, speed))
        {
            userUnit.StateMachine.ChangeState(userUnit.IdleState);
        }
    }
}

Update 함수로 이동을 직접 통제하니 이동 제어가 훨씬 쉬워졌다. 공격 시에도 Stop을 이용해 agent를 수정할 필요 없이 단순히 이동을 멈추면 된다. 목적지를 수정하지 않으므로 다시 이동할 때도 간단하게 계속 Update하면 된다.

UserUnitTrackingState 클래스도 수정되었다. 이번에는 적의 위치에 따라 direction이 바뀌어야 하므로 매번 새로운 방향과 목적지를 계산한다.

public override void Update()
{
    if (userUnit.Action.TargetEnemy == null || userUnit.Action.TargetEnemy.Dead)
    {
        userUnit.StateMachine.ChangeState(userUnit.IdleState);
    }
    else if (Vector2.Distance(userUnit.Action.TargetEnemy.transform.position, userUnit.transform.position) > userUnit.Stat.AttackRange.TotalValule)
    {
        Vector2 directionToEnemy = (userUnit.Action.TargetEnemy.transform.position - userUnit.transform.position).normalized;
        Vector2 targetPosition = ((Vector2)userUnit.Action.TargetEnemy.transform.position) - directionToEnemy * (userUnit.Stat.AttackRange.TotalValule - 0.1f);

        userUnit.Action.TargetPosition = targetPosition;
        userUnit.Action.MoveToTarget(directionToEnemy, speed);
    }
}

이제 NavMesh를 제거하고 그리드만으로 동작하게 되었다.

스크린샷 2024-07-09 195927

마무리

단체 이동 시 서로 겹치지 않게 정렬하려면 유닛들의 위치를 그리드에 저장하고, 다른 유닛이 해당 자리로 이동하려 할 때 검사를 통해 가능한 위치로 이동해야 한다. 단순히 직선 이동이 아닌, 이동 중에 홀드, 공격 종료, 이동 방향 변경 등으로 위치가 변형될 수 있으므로 유닛들이 그리드 위에서 항상 매 프레임 움직이게 해야 한다. 이를 위해 A* 알고리즘을 이용한 길찾기 기능을 추가할 계획이다.