[UNITY] 하나의 에이전트가 사용 가능한 FSM

Doorbals·2022년 9월 7일
0

UNITY

목록 보기
1/4

하나의 에이전트가 사용 가능한 FSM

  • 상태 패턴은 객체의 상태에 따라 동작을 다르게 처리하는 디자인 패턴
  • 에이전트가 취할 수 있는 동작을 클래스로 구현, 이 때 모든 상태 클래스는 공통된 기반 클래스를 상속받아 자료형을 통일
  • 에이전트의 상태를 바꿔주는 메소드 구현 : ChangeState()
    • 각 상태는 보통 FSM의 유연성을 위해 상태로 진입할 때(Enter)나
    • 상태에서 나갈 때(Exit) 취하는 행동에 대해 정의되기를 원함.

1. State 클래스 (공통 기반 클래스)

public abstract class State // 추상(abstract) 클래스로 구현
{
	// 해당 상태를 시작할 때 1회 호출
	public abstract void Enter(Student entity);
    
    // 해당 상태를 업데이트할 때 매 프레임 호출
    public abstract void Execute(Student entity);
    
    // 해당 상태를 종료할 때 1회 호출
    public abstract void Exit(Student entity);
}

2. Student 클래스 (에이전트 클래스)

public enum StudentStates // 플레이어가 가질 수 있는 상태
{RestAndSleep, StudyHard, TakeAExam, PlayAGame, HitTheBottle}

public class Student
{
	// Student의 속성들
	private int knowledge;				// 지식
    private int stress;					// 스트레스
    private int fatigue;				// 피로
    private int totalScore;				// 총 점수
    private Locations currentLocation;	// 현재 위치
    
    // Student가 가지고 있는 모든 상태, 현재 상태
    private State[] states;
    private State currentState;
    
    public void SetUp(string name)
    {
    	// states에 State 클래스 형식의 객체가 5개 들어가는 배열 대입 
    	states = new State[5]; 
        
        // states의 각 인덱스에 각 클래스 메모리 할당
        states[(int)StudentStates.RestAndSleep] = new StudentOwnedStates.RestAndSleep(); 
        states[(int)StudentState.StudyHard] = new StudentOwendStates.StudyHard();
        states[(int)StudentState.TakeAExam] = new StudentOwendStates.TakeAExam();
        states[(int)StudentState.PlayAGame] = new StudentOwendStates.PlayAGame();
        states[(int)StudentState.HitTheBottle] = new StudentOwendStates.HitTheBottle();
        
         // 현재 상태를 RestAndSleep 상태로 설정
       	ChangeState(StudentStates.RestAndSleep);
        
        knowledge = 0;				
        stress = 0;					
        fatigue = 0;			
        totalScore = 0;				
        currentLocation = Location.SweetHome;	
    }
    
    public void Updated()
    {
    	// currentState가 비어있지 않으면 현재 상태의 Execute 메서드 실행
    	if(currentState != null)
        	currentState.Execute(this); // this는 Student 클래스 객체
    }
    
    public void ChangeState(StudentStates newState)
    {
    	// 새로 바꾸려는 상태가 비어있으면 상태를 바꾸지 않고 return
    	if(states[(int)newState] == null) return;
        
        // 현재 상태를 종료하기 위해 Exit() 메소드 호출
        if(currentState != null)
        	currentState.Exit(this);
            
        // 새로운 상태로 변경하고, 새로 바뀐 상태의 Enter() 메서드 호출
        currentState = states[(int)newState]
        currentState.Enter(this);
    }
}

3. StudentOwnedStates 네임스페이스 (에이전트의 동작 클래스들)

nameSpace StudentOwnedStates
{
	public class RestAndSleep : State // State 클래스를 상속받음.
    {
    	public override void Enter(Student entity) // 시작 행동
        {
        	// 장소를 집으로 설정하고, 집에 오면 스트레스가 0이 된다.
        	entity.currentLocation = Location.SweetHome;
            entity.stress = 0;
        }
        
        public override void Execute(Student entity) // 지속 행동
        {
        	// 피로가 0보다 크면 지속적으로 피로 10씩 회복
        	if(entity.fatigue > 0)
            	entity.fatigue -= 10;
            else
            {
            	// StudyHard 상태로 변경
                entity.ChangeState(StudentStates.StudyHard);
            }
        }
        
        public override void Exit(Student entity) {} // 종료 행동 없음
        
    }
    
    public class StudyHard : State
    {
    	public override void Enter(Student entity) 
        {
        	entity.currentLocation = Location.Library;
        }        
        
        public override void Execute(Student entity)
        {
        	entity.knowledge++;
            entity.stress++;
            entity.fatigue++;
            
            // entity의 knowledge가 3 ~ 10 사이면
            if(entity.knowledge >= 3 && entity.knowledge <= 10)
            {
            	int isExit = Random.Range(0, 2);
                // isExit가 1이거나 knowledge가 10이면
                if(isExit == 1 || entity.knowledge == 10)
                {
                	// 현재 상태를 TakeAExam 상태로 변경
                	entity.ChangeState(StudentStates.TakeAExam);
                }	
            }
            
            if(entity.stress >= 20)
            {
            	// 현재 상태를 PlayAGame 상태로 변경
            	entity.ChangeState(StudentStates.PlayAGame);
            }
            
            if(entity.fatigue >= 50)
            {
            	// 현재 상태를 RestAndSleep 상태로 변경
            	entity.ChangeState(StudentStates.RestAndSleep);
            }
        }
        public override void Exit(Student entity) {} // 종료 행동 없음
    }
    
    // 나머지 클래스들도 구현
    public class TakeAExam : State
    {...}
    
    public class PlayAGame : State
    {...}
    
    public class HitTheBottle : State
    {...}
}

출처 : 고박사의 유니티 노트 - FSM2 (링크)

profile
게임 클라이언트 개발자 지망생의 TIL

0개의 댓글