내일배움캠프 56일차 7/04 TIL + 유닛 공격 구현 + FSM 적용
유닛 공격 구현: 타겟 지정 공격
private void HandleAttackClick(Vector2 worldMousePos)
{
attackPositionMarker.gameObject.SetActive(false);
RaycastHit2D hit = Physics2D.Raycast(worldMousePos, Vector2.zero, float.MaxValue, enemyInt);
if (hit.collider != null)
{
ISelectable selectable = hit.collider.GetComponent<ISelectable>();
if (selectable != null && selectable.SelectType() == SelectableType.Enemy)
{
foreach (var unit in selectedUnits)
{
((UserUnit)unit).SetTarget((Enemy)selectable);
}
}
}
}
UserUnitController에서 공격 준비 상태에서 마우스 좌클릭으로 적을 지정하면 해당 적의 정보를 받아 추적하게 하였다. SetTarget
함수를 호출하면 해당 적 추적을 시작한다.
// UserUnit.cs
public void SetTarget(Enemy target)
{
action.SetTarget(target);
StateMachine.ChangeState(trackingState);
}
// UserUnitAction.cs
public void SetTarget(Enemy enemy)
{
targetEnemy = enemy;
MoveToTarget(targetEnemy.transform.position, userUnit.Stat.AttackRange.TotalValule - 0.1f);
}
SetTarget
을 호출하면 공격 타겟을 설정하고 추적을 시작한다. TrackingState
로 전환되면 적의 위치를 갱신하며 추적한다.
// UserUnitTrackingState.cs
public override void Enter()
{
userUnit.Action.IsAlert = true;
userUnit.Action.IsOnTheMove = false;
userUnit.Action.IsTracking = true;
}
public override void Exit() {}
public override void Update()
{
if (userUnit.Action.TargetEnemy != null && !userUnit.Action.TargetEnemy.Dead)
{
userUnit.Agent.destination = userUnit.Action.TargetEnemy.transform.position;
}
}
FSM 구현
유닛의 기본 동작을 StateMachine으로 나누어 유지보수성과 가시성을 높였다. Idle
, Move
, Hold
, Tracking
, MovingAttack
의 다섯 가지 상태로 나누었다.
- Idle: 기본 상태로 적이 들어오면 추적하며 공격한다.
public override void Enter()
{
userUnit.Action.IsAlert = true;
userUnit.Action.IsOnTheMove = false;
userUnit.Action.IsTracking = true;
}
- Move: 평범한 우클릭으로 이동 시 적용된다.
public override void Enter()
{
userUnit.Action.IsAlert = false;
userUnit.Action.IsOnTheMove = true;
userUnit.Action.IsTracking = false;
userUnit.Action.IsHold = false;
userUnit.Action.MoveToTarget(userUnit.Action.TargetPosition, userUnit.Action.PositionOffset);
}
public override void Exit()
{
userUnit.Action.IsOnTheMove = false;
userUnit.Action.IsAlert = true;
userUnit.Action.IsTracking = true;
}
public override void Update()
{
if (userUnit.Agent.remainingDistance <= (userUnit.Agent.stoppingDistance + 0.1f))
{
userUnit.StateMachine.ChangeState(userUnit.IdleState);
}
}
- Hold: Tracking을 하지 않는 idle 상태.
public override void Enter()
{
userUnit.Action.IsAlert = true;
userUnit.Action.IsOnTheMove = false;
userUnit.Action.IsTracking = false;
userUnit.Action.IsHold = true;
userUnit.Action.Stop();
}
public override void Exit()
{
userUnit.Action.IsHold = false;
}
-
Tracking: 적의 위치를 갱신하며 추적하고 공격한다.
-
MovingAttack: A를 누르고 땅을 찍으면 그 장소로 이동하면서 적을 감지하면 공격한다.
public override void Enter()
{
userUnit.Action.IsAlert = true;
userUnit.Action.IsOnTheMove = true;
userUnit.Action.IsTracking = false;
userUnit.Action.MoveToTarget(userUnit.Action.TargetPosition, userUnit.Action.PositionOffset);
}
public override void Exit()
{
userUnit.Action.IsOnTheMove = false;
userUnit.Action.IsTracking = true;
}
public override void Update()
{
if (userUnit.Agent.remainingDistance <= (userUnit.Agent.stoppingDistance + 0.1f))
{
userUnit.StateMachine.ChangeState(userUnit.IdleState);
}
}
HFSM, Behaviour Tree
FSM을 공부하며 HFSM(계층적 유한 상태 머신)과 BT(Behaviour Tree)도 배웠다. HFSM은 상위 State를 통해 전환을 진행하여 중복 코드를 줄이고 가시성을 높인다. BT는 복잡한 NPC AI 구현에 사용되며, FSM과 BT의 차이와 용도를 이해하게 되었다.
내일은 팀원들이 이번 주차 동안 만든 것을 모두 모아 게임 한 사이클을 테스트해볼 예정이다. 이 테스트에는 병력 생산, 정보 표시 등 기본적인 기능도 포함될 것이다.