AggressiveAnimal.cs
PassiveAnimal.cs
NonattackAnimal.cs
위의 상태 변경을 담당하는 클래스 3개는 Update문 내에서 if 문을 사용하여 상태 변경 조건들을 체크하여 상태를 변경하는 방식으로 작성했다.
Update문 내에는 현재 상태를 체크하여 그 상태에 해당하는 업데이트 메서드를 호출하는 switch문만 남기고 상태 변경 로직은 각 상태의 업데이트 메서드를 만들어 그 안에서 변경 조건을 판단해 상태를 변경하는 방식으로 코드를 개선해보았다.
예시) PassiveAnimal.cs
if (playerDistance <= data.detectDistance)
{
if (!isAttacked && IsPlayerInFieldOfView())
{
isAlerted = true;
ChangeState(CreatureState.Alerted);
}
else if (isAttacked)
{
isAlerted = true;
isChasing = true;
if (currentHealth < data.health * 0.1f)
{
isEscaping = true;
isChasing = false;
ChangeState(CreatureState.Escape);
}
else if (playerDistance < data.attackDistance && IsPlayerInFieldOfView())
{
ChangeState(CreatureState.Attack);
}
else
{
NavMeshPath path = new NavMeshPath();
if (agent.CalculatePath(PlayerManager.Instance.Player.transform.position, path))
{
ChangeState(CreatureState.Chase);
}
}
}
}
else
{
isAlerted = false;
isChasing = false;
isEscaping = false;
isAttacked = false;
if (creatureState == CreatureState.Wander && agent.remainingDistance < 0.5f)
{
ChangeState(CreatureState.Idle);
wanderRate = Random.Range(data.minWanderWaitTime, data.maxWanderWaitTime);
}
else if (Time.time - lastWanderTime > wanderRate)
{
lastWanderTime = Time.time;
ChangeState(CreatureState.Wander);
}
}
protected override void Update()
{
base.Update();
switch (creatureState)
{
case CreatureState.Idle:
case CreatureState.Wander:
IdleWanderUpdate();
break;
case CreatureState.Alerted:
AlertedUpdate();
break;
case CreatureState.Chase:
case CreatureState.Attack:
CombatUpdate();
break;
case CreatureState.Escape:
EscapeUpdate();
break;
case CreatureState.Dead:
return;
}
stateContext.currentState.OnStateUpdate();
}
private void IdleWanderUpdate()
{
if (creatureState == CreatureState.Wander && agent.remainingDistance < 0.5f)
{
ChangeState(CreatureState.Idle);
wanderRate = Random.Range(data.minWanderWaitTime, data.maxWanderWaitTime);
}
else if (Time.time - lastWanderTime > wanderRate)
{
lastWanderTime = Time.time;
ChangeState(CreatureState.Wander);
}
if (playerDistance <= data.detectDistance)
{
if (!isAttacked && IsPlayerInFieldOfView())
{
isAlerted = true;
ChangeState(CreatureState.Alerted);
return;
}
else if (isAttacked)
{
isAlerted = true;
if (currentHealth < data.health * 0.1f)
{
isEscaping = true;
isChasing = false;
ChangeState(CreatureState.Escape);
return;
}
else
{
isChasing = true;
ChangeState(CreatureState.Chase);
}
}
}
}
private void AlertedUpdate()
{
if (currentHealth < data.health * 0.1f)
{
isEscaping = true;
ChangeState(CreatureState.Escape);
return;
}
else
{
if (isAttacked)
{
isChasing = true;
ChangeState(CreatureState.Chase);
return;
}
}
if (playerDistance > data.detectDistance)
{
isAlerted = false;
isChasing = false;
isEscaping = false;
isAttacked = false;
ChangeState(CreatureState.Idle);
}
}
private void CombatUpdate()
{
if (currentHealth < data.health * 0.1f)
{
isEscaping = true;
isChasing = false;
ChangeState(CreatureState.Escape);
return;
}
else
{
if (playerDistance < data.attackDistance && IsPlayerInFieldOfView())
{
ChangeState(CreatureState.Attack);
return;
}
else
{
if (playerDistance <= data.detectDistance)
{
if (CanChase())
{
isChasing = true;
ChangeState(CreatureState.Chase);
return;
}
else
{
isChasing = false;
isAlerted = false;
ChangeState(CreatureState.Wander);
return;
}
}
else
{
isAlerted = false;
isChasing = false;
isEscaping = false;
isAttacked = false;
ChangeState(CreatureState.Idle);
}
}
}
}
private void EscapeUpdate()
{
if (playerDistance > data.detectDistance)
{
isAlerted = false;
isChasing = false;
isEscaping = false;
isAttacked = false;
ChangeState(CreatureState.Idle);
}
}
기존의 코드는 모든 조건을 한 번에 판단해서 처리하려다보니 예상치 못한 상황에 대한 대처가 쉽지 않고 수정도 쉽지 않다.
수정 한 코드는 현재 상태에서 전환 가능한 상태들에 대한 조건만 판단하기 때문에 조건을 설정하는 데 있어 보다 수월하다. 상태가 전환되는 조건들이 많고 각 상태별로 업데이트 메서드를 만들었기 때문에 코드는 더 길어졌지만 수정은 보다 쉽게 할 수 있게 개선된 것 같다.