Creature의 상태를 갖고 상태의 전환을 담당하는 StateContext
public class StateContext
{
public IState currentState;
public IdleState idle;
public WanderState wander;
public AlertedState alerted;
public ChaseState chase;
public AttackState attack;
public EscapeState escape;
public DeadState dead;
public StateContext(Creature _creature)
{
idle = new IdleState(_creature);
wander = new WanderState(_creature);
alerted = new AlertedState(_creature);
chase = new ChaseState(_creature);
attack = new AttackState(_creature);
escape = new EscapeState(_creature);
dead = new DeadState(_creature);
}
public void Transition(IState newState)
{
if (currentState == newState) return;
if (currentState != null) currentState.OnStateExit();
currentState = newState;
currentState.OnStateEnter();
}
}
해당 상태에서의 행동을 담당.
WanderState : 랜덤한 곳으로 이동하는 상태, 활동 영역 밖에 있을 경우 처음 스폰된 포지션으로 이동
using UnityEngine;
using UnityEngine.AI;
public interface IState
{
public void OnStateEnter();
public void OnStateUpdate();
public void OnStateExit();
}
public class WanderState : IState
{
private Creature creature;
public WanderState(Creature _creature)
{
creature = _creature;
}
public void OnStateEnter()
{
creature.agent.isStopped = false;
if (IsOutArea())
{
creature.animator.SetBool("Walk", false);
creature.animator.SetBool("Run", true);
creature.agent.speed = creature.data.runSpeed;
creature.agent.SetDestination(creature.spawnPoint.position);
}
else
{
creature.animator.SetBool("Walk", true);
creature.animator.SetBool("Run", false);
creature.agent.speed = creature.data.walkSpeed;
creature.agent.SetDestination(GetWanderLocation());
}
//creature.animator.speed = creature.agent.speed / creature.data.walkSpeed;
}
public void OnStateUpdate()
{
}
public void OnStateExit()
{
creature.animator.SetBool("Walk", false);
creature.animator.SetBool("Run", false);
}
private bool IsOutArea()
{
return Vector3.Distance(creature.transform.position, creature.spawnPoint.position) > creature.data.activityAreaRadius;
}
private Vector3 GetWanderLocation()
{
NavMeshHit hit;
NavMesh.SamplePosition(creature.transform.position + (Random.onUnitSphere * Random.Range(creature.data.minWanderDistance, creature.data.maxWanderDistance)),
out hit, creature.data.maxWanderDistance, NavMesh.AllAreas);
int i = 0;
while (Vector3.Distance(creature.transform.position, hit.position) < creature.data.detectDistance)
{
NavMesh.SamplePosition(creature.transform.position + (Random.onUnitSphere * Random.Range(creature.data.minWanderDistance, creature.data.maxWanderDistance)),
out hit, creature.data.maxWanderDistance, NavMesh.AllAreas);
i++;
if (i == 30) break;
}
return hit.position;
}
}
Aggressive, Passive, Nonattack 각각 변경되는 상태가 다르다.
Passive의 경우 아래와 같은 상태를 가진다.
public class PassiveAnimal : Creature
{
protected override void Awake()
{
base.Awake();
}
protected override void Start()
{
stateContext.Transition(stateContext.wander);
}
protected override void Update()
{
base.Update();
if (creatureState == CreatureState.Dead) return;
if (playerDistance <= data.detectDistance)
{
if (!data.isAttacked && IsPlayerInFieldOfView())
{
isAlerted = true;
ChangeState(CreatureState.Alerted);
}
else if (data.isAttacked)
{
isChasing = true;
if (data.health < 5)
{
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;
data.isAttacked = false;
if (creatureState == CreatureState.Wander && agent.remainingDistance < 0.1f)
{
ChangeState(CreatureState.Idle);
wanderRate = Random.Range(data.minWanderWaitTime, data.maxWanderWaitTime);
}
else if (Time.time - lastWanderTime > wanderRate)
{
lastWanderTime = Time.time;
ChangeState(CreatureState.Wander);
}
}
stateContext.currentState.OnStateUpdate();
}
private void ChangeState(CreatureState _creatureState)
{
creatureState = _creatureState;
switch (creatureState)
{
case CreatureState.Idle:
stateContext.Transition(stateContext.idle);
break;
case CreatureState.Wander:
stateContext.Transition(stateContext.wander);
break;
case CreatureState.Alerted:
stateContext.Transition(stateContext.alerted);
break;
case CreatureState.Chase:
stateContext.Transition(stateContext.chase);
break;
case CreatureState.Attack:
stateContext.Transition(stateContext.attack);
break;
case CreatureState.Escape:
stateContext.Transition(stateContext.escape);
break;
}
}
}
---
public class Creature : MonoBehaviour, IDamagable
{
public CreatureData data;
protected int currentHealth;
protected StateContext stateContext;
protected float playerDistance;
public Transform spawnPoint;
public CreatureState creatureState;
public NavMeshAgent agent;
public Animator animator;
protected SkinnedMeshRenderer[] meshRenderers;
protected float wanderRate;
protected float lastWanderTime;
public bool isAlerted;
public bool isChasing;
public bool isEscaping;
protected virtual void Awake()
{
spawnPoint = CreatureSpawnManager.Instance.pool.Pools[data.id].spawnPoint;
agent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>();
meshRenderers = GetComponentsInChildren<SkinnedMeshRenderer>();
stateContext = new StateContext(this);
isAlerted = false;
isChasing = false;
isEscaping = false;
currentHealth = data.health;
}
(생략)
}