[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 9회차 미션 시작합니다.
C#의 Generic 문법을 이용하여 FSM을 구현하는 내용입니다.
상태를 나타내는 State 01~ 구현을 하고, 이 State를 관리하는 State Machine을 구현합니다.
초기화를 진행하면서 State Machine에 각 State 01~ 등록을 진행합니다.
게임 플레이 중에 "Current State", 즉 Transition이 State 간 변경되며 상태가 바뀌는 방식으로 구현합니다.
[Project View] - [Assets] - Scripts 폴더 내에서 "Create - C# Script"를 진행하여 "StateMachine_New"로 작성합니다.
StateMachine_New.cs
public abstract class State<T>
{
protected StateMachine_New<T> stateMachine;
protected T context;
public State()
{
}
internal void SetStateMachineAndContext(StateMachine<T> stateMachine, T context)
{
this.stateMachine = stateMachine;
this.context = context;
OnInitialized();
}
public virtual void OnInitialized()
{
}
public virtual void OnEnter()
{
}
public abstract virtual void Update(float deltaTime);
public virtual void OnExit()
{
}
}
public sealed class StateMachine_New<T>
{
private T context;
private State<T> currentState;
public State<T> CurrentState => currentState;
private State<T> previousState;
public State<T> PreviousState => previousState;
private float elapsedTimeInState = 0.0f; // 상태가 변환 되었을 때 변환된 상태에서 얼마나 시간이 흘렀는가를 위한 변수.
public float ElapsedTimeInState => elapsedTimeInState;
private Dictionary<System.Type, State<T>> states = new Dictionary<System.Type, State<T>>();
public StateMachine(T context, State<T> initialState)
{
this.context = context;
// 초기 상태 설정
AddState(initialState);
currentState = initialState;
currentState.OnEnter(); // 초기 State 실행
}
public void AddState(State<T> state)
{
state.SetMachineAndContext(this, context);
states[state.GetType()] = state;
}
public void Update(float deltaTime)
{
elapsedTimeInState += deltaTime;
currentState.Update(deltaTime);
}
public R ChangeState<R> where R : State<T>
{
var newType = typeof(R);
if (currentState.GetType() == newType)
{
return currentState as R;
}
if (currentState != null)
{
currentState.OnExit();
}
previousState = currentState; // 이전상태 설정
currentState = states[newType]; // 현재상태 설정
currentState.OnEnter();
elapsedTimeInState = 0.0f; // 초기화
return currentState as R;
}
}
초기화시 상태를 등록하는 것은 C#의 Dictionary 문법을 사용하여 구현합니다. Template class를 사용하여 State class를 구현하고 StateMachine class에서는 해당 상태 및 흘러간 시간을 체크할 수 있도록 하고, ChangeState() 함수를 통해 instance의 상태 전환을 하는 방법입니다.
이를 이용하여 캐릭터 AI 구현을 해보도록 하겠습니다.
적 GameObject를 구성한 모습입니다.
시간 관계상 대부분 작업된 상태로 준비를 해놓고 시작합니다. NavMesh, Animation 등 앞 시간에서 배운 내용들이기 때문에 다시 설명을 하지는 않습니다. 공격 State도 3가지를 가지고 있고, 연동된 변수도 미리 준비되어 있습니다.
실제로 멋진 적 캐릭터를 가지고 구현을 해보니 좋긴합니다.. 단지 배운 내용이 한번에 집성체로 나타나니 ㅋㅋ 가..감당하기가 어렵네요.. 머 그래도 괜찮습니다. 저희가 하려는 건 FSM이니까요
+ Idle State - 적을 찾아다니는 상태. 사정거리 내에 적이 있는지 체크. 사정거리 밖에 있고 적에게 이동해야 하는지 체크.
+ Attack State - 적이 사정거리내 있는 상태. 공격 애니메이션 구동.
+ Move State - 적에게 이동하는 상태. 이동 애니메이션 구동.
EnemyController_New.cs
public class EnemyController_New : MonoBehaviour
{
protected StateMachine<EnemyController_New> stateMachine;
private void Start()
{
stateMachine = new StateMachine<EnemyController_New>(this, new IdleState());
stateMachine.AddState(new MoveState());
stateMachine.AddState(new AttackState());
}
private void Update()
{
stateMachine.Update(Time.deltaTime);
}
}
오늘은 완전 코드 레벨이네요. ㅋㅋ 유니티 화면을 거의 보지도 못했어요. ㅎㅎ
강의에서 설명되는 내용들을 코드로 작성하였지만, 제가 보니 설명에는 없지만 실제 코드화된 부분들이 있습니다.
하지만 실제 코드가 교육자료로 첨부되어 있기 때문에 공부하는데에는 지장이 있지는 않습니다. 자료는 첨부하지 않습니다. 그냥 저런 형태로 구현을 하는구나라고만 생각하고 보면 될 것 같습니다.
IdleState.cs
public class IdleState : State<EnemyController_New>
{
private Animator animator;
private CharacterController controller;
protected int hasMove = Animator.StringToHash("Move");
protected int hasMoveSpeed = Animator.StringToHash("MoveSpeed");
public override void OnInitialized()
{
animator = context.GetComponent<Animator>();
controller = context.GetComponent<EnemyController_New>();
}
public override void OnEnter()
{
animator?.SetBool(hasMove, false);
animator?.SetFloat(hasMoveSpeed, 0);
controller?.Move(Vector3.zero);
}
public override void Update(float deltaTime)
{
Transform enemy = context.SearchEnemy();
if (enemy)
{
if (context.IsAvailableAttack)
{
stateMachine.ChangeState<AttackState>();
}
else
{
stateMachine.ChangeState<MoveState>();
}
}
}
public override void OnExit()
{
}
}
코드 레벨로는 파일도 여러개이고, 직접 작성하여 컴파일도 해보아야 하는 부분이라 그림으로 마지막 정리를 해주시네요. ^^
IdleState 상태 기준으로의 설명입니다.
결국 초기화시에 OnInit으로 시작을 하고,
상태가 시작되면 OnEnter로 진입을 하게되고,
현 Idle 상태로 지속 OnUpdate가 호출되며 준비하다가,
적이 사정거리내에 있다면 OnExit를 호출하고,
AttackState로 전환이 이루어지도록 만든 소스코드인 것입니다.
ㅎㅎ 진짜 중요한 내용이 많은 코드인데.. 짧게 할수도 없고 너무 길게 할수도 없기에 어쩔수 없는 것이라고 봐야합니다. 이것과 관련해서는 C# 문법과 프로그래밍 스킬이 어느정도 있어야 이해가 가능하다고 할 수 있겠네요.
아쉬워서 유니티의 Enemy가 설정된 화면 스샷 하나 첨부합니다. ㅎㅎ
패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0
'[컴퓨터] > 웹 | 앱 | 게임 개발' 카테고리의 다른 글
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 11회차 미션 (0) | 2020.10.29 |
---|---|
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 10회차 미션 (0) | 2020.10.28 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 8회차 미션 (0) | 2020.10.26 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 7회차 미션 (0) | 2020.10.25 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 6회차 미션 (0) | 2020.10.24 |