본문 바로가기
Development/멋쟁이사자처럼 게임개발 부트캠프

[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 30일차 - State 패턴 횡스크롤 2D (4)

by jjeondeuk1008 2025. 4. 24.
반응형

[ 목차 ]


     

    오늘의 포스팅은 State 패턴 횡스크롤 2D 실습 4번째이다!

     

    이번 포스팅은 우선적으로 기존에 2D에 유용하게 쓰이는 작업을 할 것이다.

    이미 지난 곳에서 쓴 기능들이긴 하지만, 복습의 개념으로 다시 해보면서 확실하게 익혀두자!

     

    스크립트 부분은 상속을 관련해 상태 머신을 관리하니

    스크립트 양이 많아질 예정이다. . !

     

     

    State 패턴 횡스크롤 2D (1)

     

    [멋쟁이사자처럼 Unity 게임 부트캠프 4기] 27일차 (2) - State 패턴 횡스크롤 2D

    [ 목차 ] 오늘의 포스팅은 상속을 활용한 횡스크롤 2D를 간단하게 실습한 이후에 게임디자인 패턴에서 배운 state 패턴 응용을 하는 것이다. 1. 스테이트머신 패턴 응용 이제 스테이트머신 패턴을

    gang-design.com

     

     

    State 패턴 횡스크롤 2D (2)

     

    [멋쟁이사자처럼 Unity 게임 부트캠프 4기] 28일차 - State 패턴 횡스크롤 2D (2)

    [ 목차 ] 오늘의 포스팅은 지난 실습에 이어서 하는 내용이다.추가적으로는 State 패턴을 활용한다는 점인데, 게임디자인패턴 중 State를 실습을 통해 어떻게 쓰이는지 하나씩 알아보도록 한다! Stat

    gang-design.com

     

     

    State 패턴 횡스크롤 2D (3)

     

    [멋쟁이사자처럼 Unity 게임 부트캠프 4기] 29일차 - State 패턴 횡스크롤 2D (3)

    [ 목차 ] 오늘은 저번 포스팅에 이어서 State 패턴 횡스크롤 2D를 한다. 이번 포스팅은 벽 상태와 플레이어 공격에 대해서 배워보도록 한다! State 패턴 횡스크롤 2D (1) [멋쟁이사자처럼 Unity 게임 부

    gang-design.com

     


     

    1. Tilemap

     

     

    우선 지형 에셋을 가져와 Tilemap으로 표현할 것이다.

    Window - 2D - Tile Palette

     

     

     

     

    Tile Palette를 선택하면 이러한 창이 생기는 것을 알 수 있다.

     

     

     

     

    에셋 이미지를 적당한 크기의 정사각형으로 잘라준다.

     

     

     

     

    열린 Tile Palette로 가서

    Create New Tile Plaette를 눌러 원하는 이름을 지정해 Create로 생성한다.

     

     

     

     

    그리고 에셋 이미지를 드래그 앤 드롭으로 가져온다.

    그러면 아래 이미지처럼 보일 것이다.

     

     

     

     

    2D Object - Tilemap - Rectangular

     

     

     

     

    이렇게 Grid의 부모로 자식 개체 하나가 생성된다.

    이 자식개체를 누르고 타일맵을 생성하면 된다.

     

     

     

     

    이렇게 한 칸씩 이어 붙여서 원하는 모양대로 지형을 만들 수 있다.

    만일 네모칸이 빈 공간이 생긴다면 이미지의 Pixels Per Unit을 조절해 주면 된다.

     

     

     

     

     

    자식 개체인 Background에서 Tilemap Collider 2DComposite Collider 2D를 생성한다.

    Composite Collider 2D를 생성하면 자동으로 Rigidbody2D가 생긴다.

     

     

     

     

    Tilemap Collier 2D에서 Composite Operation - Merge로 변경한다.

     

     

     

     

    Rigidbody2DBodyTypeStatic으로 변경한다.

     

     

     

     

    Background 개체는 Layer - Ground로 변경한다.

     

     

     


    2. Cinemachine

     

     

    이제 캐릭터가 배경 밖으로 넘어가지 않게 카메라 설정을 할 것이다

    window - Package Manager - Unity Registry - Cinemachine Install

     

     

     

     

    Hierarchy - Cinemachine - Targeted Cameras - 2D Camera 생성

     

     

     

     

    생성된 카메라 Inspector안에 Tracking TargetPlayer 오브젝트를 넣어준다.

    이것은 어떤 것을 타겟팅할 것인지에 대한 부분이다.

     

     

     

     

    카메라는 타겟팅의 정중장으로 위치되는데 이것을 조절하고 싶다면

    카메라 Inspctor의 Screen Position을 조절하면 위치가 변동된다. 개인적으로 맞추어주면 된다.

     

     

     

     

    하나의 팁! (배경이 없을 때)

     

    카메라의 배경 색을 바꾸고 싶다면

    Main Camera - Background 색을 변경하면 된다.

     

     


    3. 패럴럭스 구현

     

     

    이제 무한배경을 구현할 것이다.

    일단 배경 이미지를 배치해 준다. 카메라의 크기보다 클 정도로 잡아준다.

     

     

     

     

    배경 이미지에 Sorting Layer 지정할 새로운 레이어를 생성한다.

    Add Sorting Layer를 클릭한다.

     

     

     

     

    Layer를 작성한다.

     

     

     

     

    PlayerSorting Layer - Player로 바꾼다.

     

     

     

     

    이제 바닥 오브젝트도 Sorting Layer - Ground로 변경한다.

     

     

     

     

    배경 이미지도 Sorting Layer - Background로 지정한다.

     

     

     

     

     

    배경을 양 옆으로 더 생성해 배치한다.

     

     

     

     

    이제 무한맵 구성을 위한 ParallaxBackground 스크립트를 생성한다.

    전체적으로 얘기하면 배경 오브젝트에 붙여서, 카메라가 움직일 때 배경이 천천히 따라오게 하는 스크립트이다.

     

    Start() 함수에서 카메라 오브젝트를 찾고, 배경의 가로길이와 현재 배경 x 위치를 저장한다.

    Update() 함수에서 카메라 이동량을 보정한 값과 실제로 배경이 움직여야 할 거리 변수를 선언하고,

    만일 배경이 많이 이동하게 된다면, 그만큼 타일을 옆으로 옮겨 반복 배경처럼 보이게 한다.

    using UnityEngine;
    
    public class ParallaxBackground : MonoBehaviour
    {
        private GameObject cam;
    
        [SerializeField] private float parallaxEffect;
    
        private float xPosition;
        private float length;
    
        void Start()
        {
            cam = Camera.main.gameObject;
    
            length = GetComponent<SpriteRenderer>().bounds.size.x;
            xPosition = transform.position.x;
    
        }
    
        void Update()
        {
            float distanceMoved = cam.transform.position.x * (1 - parallaxEffect);
            float distanceToMove = cam.transform.position.x * parallaxEffect;
    
            transform.position = new Vector3(xPosition + distanceToMove, transform.position.y);
    
            if (distanceMoved > xPosition + length)
                xPosition = xPosition + length;
            else if (distanceMoved < xPosition - length)
                xPosition = xPosition - length;
    
        }
    }
    
    

     

     

     

     

    Background 첫 번째 자식 오브젝트들에 넣어준다.

    그리고 Parallax Effect 숫자를 각각 다르게 해 준다.

    숫자가 적을수록 천천히 움직이고 높으면 빠르게 움직인다.

     

     

     


    4. Light 구현

     

     

     

    캐릭터에 2D Light를 구현해 보겠다.

     

    BackgroundLayer Color를 바꾸고 싶은 대로 바꾸어준다. (어두울수록 라이트가 잘 보인다.)

    GameObject를 Player 자식으로 생성해 Add Component - Light 2D를 생성한다.

    Intersity를 통해 밝기를 키울 수 있다.

    Outer : 빛의 크기

     

     

     

     

    z축 회전을 통해 플레이어 기준으로 오른쪽으로 퍼지는 빛을 배치한다.

     

     

     

     


    5. 적 생성하기

     

     

     

     

    적과 관련된 Enemy, EnemyState, EnemyStateMachine 스크립트를 생성한다.

     

     

     

     

    이제 저 스크립트의 기초 설정을 한다.

    EnemyStateMachine 스크립트

     

    플레이어와 비슷한 Enemy의 상태 머신을 설정한다.

    using UnityEngine;
    
    public class EnemyStateMachine
    {
        public EnemyState currentState { get; private set; }
    
        public void Initialize(EnemyState _startState)
        {
            currentState = _startState;
            currentState.Enter();
        }
    
        public void ChangeState(EnemyState _newState)
        {
            currentState.Exit();
            currentState = _newState;
            currentState.Enter();
        }
    }
    
    

     

     

     

     

    EnemyState 스크립트

    공통 기능을 담고 있는 부모 클래스의 적 상태의 기본 클래스 스크립트이다.

    using UnityEngine;
    
    public class EnemyState
    {
        protected EnemyStateMachine stateMachine;
        protected Enemy enemyBase;
    
        protected bool triggerCalled;
        private string animBoolName;
        protected float stateTimer;
    
        public EnemyState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName)
        {
            this.enemyBase = _enemyBase;
            this.stateMachine = _stateMachine;
            this.animBoolName = _animBoolName;
        }
    
        public virtual void Enter()
        {
            triggerCalled = false;
            enemy.anim.SetBool(animBoolName, true);
        }
    
        public virtual void Update()
        {
            stateTimer -= Time.deltaTime;
        }
    
        public virtual void Exit()
        {
            enemy.anim.SetBool(animBoolName, false);
        }
    }
    
    

     

     

     

     

    Enemy 스크립트

    적 오브젝트의 전체 상태 관리의 핵심 클래스이다.

    using UnityEngine;
    
    public class Enemy : MonoBehaviour
    {
        public Rigidbody2D rb { get; private set; }
        public Animator anim { get; private set; }
    
        public EnemyStateMachine stateMachine { get; private set; }
    
        private void Awake()
        {
            stateMachine = new EnemyStateMachine();
        }
    
        private void Update()
        {
            stateMachine.currentState.Update();
        }
    
    }
    

     

     

     

     

    Entity 생성

    Player의 스크립트 상속으로 받을 수 있게 생성한다.

    using UnityEngine;
    
    public class Entity : MonoBehaviour
    {
        public virtual void Awake()
        {
    
        }
    
        public virtual void Start()
        {
    
        }
    
        public virtual void Update()
        {
    
        }
    }
    
    

     

     

     

     

    Player 스크립트를 상속으로 Entity 받을 수 있게 변경한다.

    public class Player : Entity
    

     

     

     

     

    Player 스크립트의 Awake(), Start(), Update() 메서드를 오버라이드로 변경한다.

     protected override void Awake()
     {
         // 상태 머신 인스턴스 생성
         stateMachine = new PlayerStateMachine();
    
         // 각 상태 인스턴스 생성 (this: 플레이어 객체, stateMachine: 상태 머신, "Idle"/"Move": 상태 이름)
         idleState = new PlayerIdleState(this, stateMachine, "Idle");
         moveState = new PlayerMoveState(this, stateMachine, "Move");
         jumpState = new PlayerJumpState(this, stateMachine, "Jump");
         airState  = new PlayerAirState(this, stateMachine, "Jump");
         dashState = new PlayerDashState(this, stateMachine, "Dash");
         wallSlide = new PlayerWallSlideState(this, stateMachine, "WallSlide");
         wallJump = new PlayerWallJumpState(this, stateMachine, "Jump");
    
         primaryAttack = new PlayerPrimaryAttackState(this, stateMachine, "Attack");
    
     }
    
     protected override void Start()
     {
         anim = GetComponentInChildren<Animator>();
         rb = GetComponent<Rigidbody2D>();
    
         // 게임 시작 시 초기 상태를 대기 상태(idleState)로 설정
         stateMachine.Initialize(idleState);
     }
    
     protected override void Update()
     {
      
         stateMachine.currentState.Update();
         CheckForDashInput();
     }
    

     

     

     

     

    Entity 스크립트로 돌아가 public이 아닌 protected로 변경한다.

    using UnityEngine;
    
    public class Entity : MonoBehaviour
    {
        protected virtual void Awake()
        {
    
        }
    
        protected virtual void Start()
        {
    
        }
    
        protected virtual void Update()
        {
    
        }
    }
    

     

     

     

     

    이제 플레이어 외에 적에게도 충돌 정보가 필요할 것이다.

    Player 스크립트에 있는 충돌 정보에 관한 변수들을 Entity 스크립트로 옮겨준다.

    private을 protected로 변경한다.

    using UnityEngine;
    
    public class Entity : MonoBehaviour
    {
    
        [Header("충돌 정보")]
        [SerializeField] protected Transform groundCheck;
        [SerializeField] protected float groundCheckDistance;
        [SerializeField] protected Transform wallCheck;
        [SerializeField] protected float wallCheckDistance;
        [SerializeField] protected LayerMask whatIsGround;
    
        protected virtual void Awake()
        {
    
        }
    
        protected virtual void Start()
        {
    
        }
    
        protected virtual void Update()
        {
    
        }
    }
    
    

     

     

     

     

    충돌에 관한 기즈모 및 땅 벽 체크하는 코드Entity로 가져온다.

    private를 protected로 변경하고,

    IsGroundDetected(), IsWallDetected() 메서드는 public virtual로 변경한다.

    using UnityEngine;
    
    public class Entity : MonoBehaviour
    {
    
        [Header("충돌 정보")]
        [SerializeField] protected Transform groundCheck;
        [SerializeField] protected float groundCheckDistance;
        [SerializeField] protected Transform wallCheck;
        [SerializeField] protected float wallCheckDistance;
        [SerializeField] protected LayerMask whatIsGround;
        
        public int facingDir { get; private set; } = 1;
    		protected bool facingRight = true;
    
        protected virtual void Awake()
        {
    
        }
    
        protected virtual void Start()
        {
    
        }
    
        protected virtual void Update()
        {
    
        }
    
        #region 충돌
        public virtual bool IsGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
        public virtual bool IsWallDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);
    
        protected virtual void OnDrawGizmos()
        {
            Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));
            Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));
        }
        #endregion
    }
    
    

     

     

     

     

    플립에 관한 코드도 Player에서 Entity로 옮겨준다.

    public에서 public virtual로 변경한다.

    using UnityEngine;
    
    public class Entity : MonoBehaviour
    {
    
        [Header("충돌 정보")]
        [SerializeField] protected Transform groundCheck;
        [SerializeField] protected float groundCheckDistance;
        [SerializeField] protected Transform wallCheck;
        [SerializeField] protected float wallCheckDistance;
        [SerializeField] protected LayerMask whatIsGround;
    
        public int facingDir { get; private set; } = 1;
        protected bool facingRight = true;
    
        protected virtual void Awake()
        {
    
        }
    
        protected virtual void Start()
        {
    
        }
    
        protected virtual void Update()
        {
    
        }
    
        #region 충돌
        public virtual bool IsGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
        public virtual bool IsWallDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);
    
        protected virtual void OnDrawGizmos()
        {
            Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));
            Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));
        }
        #endregion
    
        #region 플립
        public virtual void Flip()
        {
            facingDir = facingDir * -1;
            facingRight = !facingRight;
            transform.Rotate(0, 180, 0);
        }
    
        public virtual void FlipController(float _x)
        {
            if (_x > 0 && !facingRight)
                Flip();
            else if (_x < 0 && facingRight)
                Flip();
    
        }
    
        #endregion
    }
    
    

     

     

     

     

    속력과 Animator, Rigidbody2D 컴포넌트를 같이 Entity 스크립트로 가져온다.

    #region Components
    public Animator anim { get; private set; }
    public Rigidbody2D rb { get; private set; }
    #endregion
    
    #region 속력
    public void ZeroVelocity() => rb.linearVelocity = new Vector2(0, 0);
    
    public void SetVelocity(float _xVelocity, float _yVelocity)
    {
        rb.linearVelocity = new Vector2(_xVelocity, _yVelocity);
        FlipController(_xVelocity);
    }
    #endregion
    

     

     

     

     

    Player의 Start에 있는 anim, rb 변수의 컴포넌트를 Entity로 옮겨준다.

    아래는 Player 스크립트의 Start() 메서드

    protected override void Start()
    {
        anim = GetComponentInChildren<Animator>();
        rb = GetComponent<Rigidbody2D>();
    
        // 게임 시작 시 초기 상태를 대기 상태(idleState)로 설정
        stateMachine.Initialize(idleState);
    }
    

     

     

     

     

    옮긴 Entity 스크립트의 Start()

     protected virtual void Start()
     {
         anim = GetComponentInChildren<Animator>();
         rb = GetComponent<Rigidbody2D>();
     }
    

     

     

     

     

    Player 스크립트는 base.Start()를 통해 부모의 Start()를 실행하고, 다음을 실행한다는 코드를 추가한다.

    protected override void Start()
    {
        base.Start();
    
        // 게임 시작 시 초기 상태를 대기 상태(idleState)로 설정
        stateMachine.Initialize(idleState);
    }
    

     

     

     

     

    Player 스크립트의 Update() 메서드에서 base.Update()

    protected override void Update()
    {
        base.Update();
        stateMachine.currentState.Update();
        CheckForDashInput();
    }
    

     

     

     

     

    적 오브젝트를 생성하고 Animator를 넣고, Sorting Layer - Enemy로 변경한다.

     

     

     

     

    Enemy 스크립트도 Entity를 상속받을 수 있게 변경한다.

    using UnityEngine;
    
    public class Enemy : Entity
    {
    
        public EnemyStateMachine stateMachine { get; private set; }
    
        protected override void Awake()
        {
            stateMachine = new EnemyStateMachine();
        }
    
        protected override void Update()
        {
            stateMachine.currentState.Update();
        }
    
    }
    

     

     

     

     


    6. 적 애니메이션

     

     

     

    적 애니메이션을 추가한다.

    idle, move 두 가지를 추가해 본다.

    적의 자식인 Animator에게 할당해 준다.

     

     

     

     

    AnimatorAnimation Clip을 각각 생성해 준다.

    ParametersBoolIdle, Move를 생성한다.

    각각 Entry → (Animation Clip) → Exit 으로 이어지는 트랜지션을 만들어준다.

     

     

     

     

    move → Exit로 가는 트랜지션의 Inspector를 변경해 준다.

     

    Has Exit Time 체크해제

    Transition Duration 0

    Move false

    idle → Exit 트랜지션도 위와 동일하지만, Move false가 아닌 idle false이다.

     

     

     

     

    이제 해당 상태에 대한 스크립트를 작성한다.

    SKeletonIdleState 스크립트이다.

     

    상속을 EnemyState로 변경한 뒤에 생성자 생성, 재정의 생성을 통해 아래의 스크립트처럼 만든다.

    여기서 하나만 추가한다.

     

    string _animBoolName 뒤에 Enemy_Skeleton _enemy를 추가한다.

    using UnityEngine;
    
    public class SkeletonIdleState : EnemyState
    {
        Enemy_Skeleton enemy;
    
        public SkeletonIdleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Skeleton _enemy) 
            : base(_enemy, _stateMachine, _animBoolName)
        {
            enemy = _enemy;
        }
    
        public override void Enter()
        {
            base.Enter();
        }
    
        public override void Exit()
        {
            base.Exit();
        }
    
        public override void Update()
        {
            base.Update();
        }
    }
    

     

     

     

     

    SkeletonMoveState도 동일하다.

    using UnityEngine;
    
    public class SkeletonMoveState : EnemyState
    {
        public SkeletonMoveState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Skeleton _enemy)
            : base(_enemyBase, _stateMachine, _animBoolName)
        {
        }
    
        public override void Enter()
        {
            base.Enter();
        }
    
        public override void Exit()
        {
            base.Exit();
        }
    
        public override void Update()
        {
            base.Update();
        }
    }
    
    

     

     

     

     

    Enemy_Skeleton 객체에 Rigidbody2D를 생성해

    Freeze Rotation z축 체크를 하고,

     

    중력을 높이고 싶다면 Gravity Scale을 조절한다.

    Collision Detection: Continuous

    Interpolate: Interpolate

    변경해 준다.

     

     

     

     

    Box Collider 2D도 추가한다.

     

     

     

     

    Enemy 스크립트에서

    이동정보에 관한 변수를 작성하고,

    상태머신 변수 stateMachine를 생성한다.

    using UnityEngine;
    
    public class Enemy : Entity
    {
        [Header("이동 정보")]
        public float moveSpeed;
        public float idleTime;
    
        public EnemyStateMachine stateMachine { get; private set; }
    
        protected override void Awake()
        {
            base.Awake();
    
            stateMachine = new EnemyStateMachine();
        }
    
        protected override void Update()
        {
            base.Update();
    
            stateMachine.currentState.Update();
        }
    
    }
    

     

     

     

     

    Enemy_Skeleton 이름의 스크립트를 생성하고, Enemy를 상속받을 수 있게 변경한다.

    idle와 Move 상태 필드를 선언한다.

    Awake() 메서드에서 상태를 초기화한다.

    Start() 메서드에서 시작 상태를 Idle상태로 시작한다는 코드이다.

    using UnityEngine;
    
    public class Enemy_Skeleton : Enemy
    {
    
        #region States
    
        public SkeletonIdleState idleState { get; private set; }
    
        public SkeletonMoveState moveState { get; private set; }
        #endregion
    
        protected override void Awake()
        {
            base.Awake();
    
            idleState = new SkeletonIdleState(this, stateMachine, "Idle", this);
            moveState = new SkeletonMoveState(this, stateMachine, "Move", this);
    
        }
    
        protected override void Start()
        {
            base.Start();
    
            stateMachine.Initialize(idleState);
        }
    
        protected override void Update()
        {
            base.Update();
        }
    }
    

     

     

     

     

    SkeletonIdleState 스크립트로 간다.

    상태 진입 시에 idleTime만큼 기다렸다가 다음 상태로 넘어가게 한다.

    using UnityEngine;
    
    public class SkeletonIdleState : EnemyState
    {
        private Enemy_Skeleton enemy;
    
        public SkeletonIdleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Skeleton _enemy) 
            : base(_enemy, _stateMachine, _animBoolName)
        {
            enemy = _enemy;
        }
    
        public override void Enter()
        {
            base.Enter();
    
            stateTimer = enemy.idleTime;
        }
    
        public override void Exit()
        {
            base.Exit();
        }
    
        public override void Update()
        {
            base.Update();
    
            if (stateTimer < 0)
                stateMachine.ChangeState(enemy.moveState);
        }
    }
    

     

     

     

     

    SkeletonMoveState 스크립트로 간다.

    Update() 메서드에서 적이 현재 보고 있는 방향으로 이동하는 SetVelocity

     

    if 조건문에서 벽이 앞에 있거나(IsWallDetected), 바닥이 없다면(IsGroundDetected)

    방향을 바꾸고 정지 상태로 전환한다.

    using UnityEngine;
    
    public class SkeletonMoveState : EnemyState
    {
        private Enemy_Skeleton enemy;
        public SkeletonMoveState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Skeleton _enemy) 
            : base(_enemyBase, _stateMachine, _animBoolName)
        {
            this.enemy = _enemy;
        }
    
        public override void Enter()
        {
            base.Enter();
        }
    
        public override void Exit()
        {
            base.Exit();
        }
    
        public override void Update()
        {
            base.Update();
    
            enemy.SetVelocity(enemy.moveSpeed* enemy.facingDir, enemy.rb.linearVelocity.y);
    
            if(enemy.IsWallDetected() || !enemy.IsGroundDetected())
            {
                enemy.Flip();
                stateMachine.ChangeState(enemy.idleState);
            }
        }
    }
    

     

     

     

     


     

    이렇게 상속을 활용해서 번거롭게 코드를 반복해서 쓰는 것이 아니라

    부모 스크립트에서 가져오는 형식으로 진행한다.

     

    확실히 스크립트가 많아지니 헷갈릴 수 있다..

    하지만 하나의 방식을 이름만 교체해 스크립트가 많아져 보이는 것일 수도 있고!

    프로젝트의 상태가 많다면 유용한 부분이니

     

    잘 배워가면서 마지막까지 화이팅하자!

     

     


    목차