컴퓨터/정보처리기사 SW 공학

| 니앙팽이 - 객체지향(OOP) | 4-8 | 행동패턴 - State pattern

객체지향

📕 4. 객체지향 디자인 패턴

유니티에서 사용하면 좋을 디자인 패턴만 명시한다.


📄 5. 행동 패턴

클래스와 객체간 서로 상호작용하는 방법이나 책임 분배 방법을 정의 하나의 객체로 수행할 수 없는작업을 여러 객체로 분해하면서 결합도를 낮출수 있음


3). State Pattern


ⓐ 특징

  • "상태"를 객체화한 패턴이다. 이 상태라는것도 참조가능하다.
  • 상태에 따라 객체의 행위 내용을 변경해주는 패턴
    • 메인 캐릭터가 가질만한 상태를 생각해보자. jump, walk, run 등등..
      이러한 상태를 손쉽게 switcing 하고 싶을떄. 제안하는 패턴이다.
  • 오토마타의 state machine와 관련이 깊다.

ⓑ 왜 쓰는가?

  • if 조건절이 많을때 사용하면 유용하다.
  • 객체의 상태에 따라 동일한 동작을 다르게 처리해야할떄,
    1. Menu System : 메뉴 UI
    2. 미니게임
    3. 인트로 영상
    4. Animatior : 실제 유니티의 animatior에 구현이 되어있다.
    5. 몬스터의 AI 만들기
    

ⓒ 구성요소

  • State :
    • 상태 전이를 시키는 상태전이 함수(handle) 를 추상화하여 ConcreateState가 파생되도록
  • ConcreateState
    • State를 상속받고, 상태전이 함수(handle) 를 구체화 한다.
    • 그리고 이 객체 자체가 상태 가된다.
  • Context || Player
    • Player는 State 객체를 포함하고 있다.
    • player.state = new ConcreateState()를 통해
    • 상태 (ConcreateState) 로 전이한다.

ⓓ 예시

📂신호등 State (C#)📂
namespace DesignPattern
{
    enum Estate{ GREEN, YELLOW, RED}
    //enum I{}
    /* 다음은 안좋은 예시
    public class TrraficLight {
        private State mState;
        public TrraficLight(State _s){ this.state = _s; }
        public State state {get; set;} 
        public void Speak(State _s){
            if(_s == GREEN){Console.WriteLine("green light");}
            else if(_s == RED){Console.WriteLine("red light");}
        }

        public void Wait(State _s){
            Console.WriteLine("Wait.. the light changed");
            if(_s == GREEN){Console.WriteLine("green light");}
            else if(_s == RED){Console.WriteLine("red light");}
        }
    }
    */

    public class TrraficLight {
        private State mState;
        public State state {get; set;} 
        
        public TrraficLight(State _s){
            this.state = _s;
        }
        public void Speak(){
            this.state.Speak();
        }
        public void Wait(){
            this.state.Wait();
        }
    }

    public interface State {
        public void Speak();
        public void Wait(TrraficLight _trraficLight);
    }

    public class GreenLight : State {
        public void Speak(){Console.WriteLine("green light");}
        public void Wait(TrraficLight _trraficLight){
            _trraficLight.state = RedLight;//바뀌어질 상태
            Console.WriteLine("wait.. the light changed");
        }
    }
    public class RedLight : State {
        public void Speak(){Console.WriteLine("red light");}
        public void Wait(TrraficLight _trraficLight){
            _trraficLight.state = GreenLight;//바뀌어질 상태
            Console.WriteLine("wait.. the light changed");
        }
    }
    //서로 상태 클래스는 서로 각각 상태클래스를 알고있다.
}
📂서있기, 앉기, 걷기, 달리기 (C#)📂
using System;
using System.Collections;
using System.Collections.Generic;
namespace DesignPattern
{
    public class Player
    {
        public Player() { speed = 0;  state = new StateStandUp(this); }
        public int speed { get; set; }
        public State state { get; set; }

        public void Talk(String _msg)
        {
            Console.WriteLine($"플레이어의 말 : {_msg}");
        }
    }

    //플레이어에 대한 State 객체 작성
    public abstract class State
    {
        protected Player player;
        public State(Player _player)
        {
            this.player = _player;
        }

        public abstract void StandUp();
        public abstract void SitDown();
        public abstract void Walk();
        public abstract void Run();
        public abstract String GetDescription(); //현재 상태 반환
    }


    public class StateStandUp : State
    {
        public StateStandUp(Player _player) : base(_player) { }

        public override void StandUp() { player.Talk("언제 음직일꺼야?"); }
        public override void SitDown() { 
            player.Talk("앉으니깐 좋다.");
            player.state = new StateSitDown(player);
        }
        public override void Walk()
        {
            player.speed = 5;
            player.state = new StateWalk(player);
            player.Talk("걷기 시작.");
        }
        public override void Run()
        {
            player.speed = 20;
            player.state = new StateRun(player);
            player.Talk("갑자기?? 존나뛰어.");
        }
        public override String GetDescription()
        {
            return "지금은 서있는 상태";
        }
    }
    public class StateSitDown : State
    {
        public StateSitDown(Player _player) : base(_player) { }
        public override void StandUp()
        {
            player.state = new StateStandUp(player);
            player.Talk("일어나자.");
        }
        public override void SitDown()
        {
            player.Talk("쥐나겠다.");
        }
        public override void Walk()
        {
            player.speed = 1;
            player.state = new StateStandUp(player);
            player.Talk("어떻게 앉은상태에서 걷냐? 일단 서있자");
        }
        public override void Run()
        {
            player.speed = 1;
            player.state = new StateStandUp(player);
            player.Talk("어떻게 앉은상태에서 뛰냐 일단 서있자");
        }
        public override String GetDescription()
        {
            return "앉아있음";
        }

    }
    public class StateWalk : State
    {
        public StateWalk(Player _player) : base(_player) { }
        public override void StandUp()
        {
            player.speed = 0;
            player.state = new StateStandUp(player);
            player.Talk("멈춰..");
        }
        public override void SitDown()
        {
            player.speed = 0;
            player.state = new StateSitDown(player);
            player.Talk("걷다가 앉으면 넘어질 수 있다.");
        }
        public override void Walk()
        {
            player.Talk("난 걷는걸 좋아하지");
        }
        public override void Run()
        {
            player.speed = 20;
            player.state = new StateRun(player);
            player.Talk("슬슬 뛴다.");
        }
        public override String GetDescription()
        {
            return "걷는 중";
        }
    }
    public class StateRun : State
    {
        public StateRun(Player _player) : base(_player) { }
        public override void StandUp()
        {
            player.speed = 0;
            player.state = new StateStandUp(player);
            player.Talk("뛰다가 갑자기 멈춰?? 연골 나가요.");
        }
        public override void SitDown()
        {
            player.speed = 0;
            player.state = new StateSitDown(player);
            player.Talk("뛰다가 어떻게 앉노.. 미쳤나");
        }
        public override void Walk()
        {
            player.speed = 5;
            player.state = new StateWalk(player);
            player.Talk("속도 줄인다~");
        }
        public override void Run()
        {
            player.speed += 2;
            player.state = new StateRun(player);
            player.Talk("더 빨리 뛰라고? ㅇㅋ");
        }
        public override String GetDescription()
        {
            return "달리는 중";
        }
    }
    static void StateMain()
    {
        Console.WriteLine("[1] : 서있기 | [2] : 앉기 | [3] : 걷기 | [4] : 뛰기 | [5] : 상태 출력 | [0] : 종료");
        Player player = new Player();
        bool stop = false;
        while (!stop)
        {
            var Input = Console.ReadLine();

            try
            {
                var validValue = Input;
            }
            catch
            {
                Console.WriteLine("You did not enter a valid format.");
                Console.ReadLine();
            }

            switch (Input)
            {
                case "1":
                    player.state.StandUp();
                    break;
                case "2":
                    player.state.SitDown();
                    break;
                case "3":
                    player.state.Walk();
                    break;
                case "4":
                    player.state.Run();
                    break;
                case "5":
                    Console.WriteLine(player.state.GetDescription());
                    break;
                case "0":
                    Console.WriteLine("프로그램 종료");
                    stop = true;
                    return;
                default:
                    break;
            }
        }
    }
}
📂자전거 타기 (C#)📂
namespace DesignPattern{
    public enum Direction {
        Left = -1, Right = 1        
    }   
    public interface IBikeState {
        void Handle(BikeController bikeController);
    }

    public class BikeStateContext {
        public IBikeState currentState {get; set;}
        private readonly BikeController mBikeController;

        public BikeStateContext(BikeController _bikeController){
            this.mBikeController = _bikeController;
        }

        public void Transition(IBikeState _state){
            currentState = _state;
            currentState.Handle(mBikeController);
        }
    }
    /*BikeController.cs*/
    public class BikeController : MonoBehaviour {
        [field : SerializeField] public float maxSpeed {get; private set;} = 2.0f;
        [field : SerializeField] public float turnDistance {get; private set;} = 2.0f;
        public float currentSpeed {get;set;}
        public Direction currentTurnDirection {get; private set;}

        private IBikeState mStartState;
        private IBikeState mStopState;
        private IBikeState mTurntState;

        private BikeStateContext mBikeStateContext;
        private void Awake() {
            mBikeStateContext = new BikeStateContext(this);
            mStartState = gameobject.AddComponent<BikerStateState>();
            mStopState = gameobject.AddComponent<BikerStopState>();
            mTurntState = gameobject.AddComponent<BikerTurnState>();
            mBikeStateContext.Transition(mStopState);
        }
        
        public void StartBike()
        {
            mBikeStateContext.Transition(mStartState);
        }

        public void StopBike()
        {
            mBikeStateContext.Transition(mStopState);
        }

        public void Turn(Direction direction)
        {
            urrentTurnDirection = direction;
            mBikeStateContext.Transition(mTurntState);
        }

    }
    /*ClientState.cs*/
    public class ClientState : MonoBehaviour {
        private BikeController mBikeController;
        private void Awake(){
            MonoBehaviour = GetComponent<BikeController>();
        }

        private void OnGUI(){
            if (GUILayout.Button("Start Bike"))
            {
                mBikeController.StartBike();
            }

            if (GUILayout.Button("Turn Left"))
            {
                mBikeController.Turn(Direction.Left);
            }

            if (GUILayout.Button("Turn Right"))
            {
                mBikeController.Turn(Direction.Right);
            }

            if (GUILayout.Button("Stop Bike"))
            {
                mBikeController.StopBike();
            }
        }
    }
}

참고

  1. 상태 패턴
  2. 상태 패턴
  3. 상태 패턴
  4. 상태 패턴
  5. FSM
  6. FSM
  7. FSM