내일배움캠프 59일차 7/09 TIL + Grid로 이동 기능 대체
작업 내용
그리드로 기존의 이동 기능들 대체
오늘은 기존의 이동 기능들을 그리드 기반으로 대체했다. 기존 코드를 최대한 수정하지 않는 방향으로 진행했는데, 이는 개발 진행 속도를 높이고 충돌 가능성을 줄이는 장점이 있다. 하지만 코드가 지저분해지고 비효율적일 수 있어 다음 주에는 팀원들과 함께 코드 리팩토링을 진행할 계획이다.
기존에는 NavMesh Agent가 목적지 설정 후 자동으로 이동했지만, 이제는 직접 이동을 제어해야 하므로 Update
함수에서 이동을 처리해야 한다. 기존의 Move
관련 함수는 direction
과 speed
를 계산하여 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()
함수는 이제 direction
과 speed
만 받아서 이동하며, 클래스의 필드인 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
함수에서 이동을 직접 제어할 수 있고, direction
과 speed
는 미리 계산해 계속 전달할 수 있다. 가장 기본적인 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를 제거하고 그리드만으로 동작하게 되었다.
마무리
단체 이동 시 서로 겹치지 않게 정렬하려면 유닛들의 위치를 그리드에 저장하고, 다른 유닛이 해당 자리로 이동하려 할 때 검사를 통해 가능한 위치로 이동해야 한다. 단순히 직선 이동이 아닌, 이동 중에 홀드, 공격 종료, 이동 방향 변경 등으로 위치가 변형될 수 있으므로 유닛들이 그리드 위에서 항상 매 프레임 움직이게 해야 한다. 이를 위해 A* 알고리즘을 이용한 길찾기 기능을 추가할 계획이다.