유니티/게임 프로젝트

2D RPG - (9) 스켈레톤 만들기 (1)

monstro 2024. 12. 18. 12:18
728x90
반응형

이제 기존의 Enemy 3총사를 사용하여

새로운 적인 Skeleton을 만들어보겠습니다.

 

기존의 코드에서 볼 수 있듯이 

새로운 적의 타입별로 Controller가 필요하므로 이를 수행하겠습니다.

 

1) SkeletonController

public class SkeletonController : EnemyController
{
    // Skeleton States
    public SkeletonStateIdle _idleState { get; private set; }
    public SkeletonStateMove _moveState { get; private set; }
    public SkeletonStateEngage _engageState { get; private set; }
    public SkeletonStateAttack _attackState { get; private set; }


    // Awake에서는 Skeleton이 가질 State들을 설정함
    protected override void Awake()
    {
        base.Awake();

        _idleState = new SkeletonStateIdle(this, _stateMachine, "Idle", this);
        _moveState = new SkeletonStateMove(this, _stateMachine, "Move", this);
        _engageState = new SkeletonStateEngage(this, _stateMachine, "Move", this);
        _attackState = new SkeletonStateAttack(this, _stateMachine, "Attack", this);
    }

    // 초기 Skeleton은 IdleState에서 시작함
    protected override void Start()
    {
        base.Start();
        _stateMachine.Init(_idleState);
    }

    protected override void Update()
    {
        base.Update();
    }

}

 

2) 땅을 딛고 서 있는 SkeletonStateGrounded

public class SkeletonStateGrounded : EnemyState
{
    // Skeleton의 State이므로 SkeletonContoller가 필요함
    // 추가적으로 Player를 탐지해야 하므로 Player를 갖고 있음
    protected SkeletonController _skeletonController;
    protected Transform _player;

    // 생성자에서는 추가적으로 SkeletonController를 설정함
    public SkeletonStateGrounded(EnemyController enemyBaseController, EnemyStateMachine enemyStateMachine, string animatorBoolParamName, SkeletonController enemyController)
        : base(enemyBaseController, enemyStateMachine, animatorBoolParamName)
    {
        this._skeletonController = enemyController;
    }

    // StateGrouned에 들어가면 Player를 탐색하여 변수를 설정
    // 그러나 GameObject.Find는 좋은 연산이 아니므로 후에 수정할 예정
    public override void Enter()
    {
        base.Enter();
        _player = GameObject.Find("Player").transform;
    }

    public override void Exit()
    {
        base.Exit();
    }
    
    // StateGrounded에서는 매 프레임마다
    // 플레이어가 탐지되었는지 또는 플레이어와 Skeleton의 거리가 2보다 작은지를 판단하여
    // State를 EngageState로 변환함
    public override void Update()
    {
        base.Update();

        if (_skeletonController.DoDetectPlayer() || Vector2.Distance(_skeletonController.transform.position, _player.position) < 2)
            _enemyStateMachine.ChangeState(_skeletonController._engageState);
    }
}

 

3) 가만히 서 있는 SkeletonStateIdle

public class SkeletonStateIdle : SkeletonStateGrounded
{
    public SkeletonStateIdle(EnemyController enemyBaseController, EnemyStateMachine enemyStateMachine, string animatorBoolParamName, SkeletonController enemyController) 
        : base(enemyBaseController, enemyStateMachine, animatorBoolParamName, enemyController)
    {

    }

    // IdleState에서는 들어간 순간에 _stateTimer를 _idleTimer 값으로 설정함
    public override void Enter()
    {
        base.Enter();

        _stateTimer = _enemyBaseController._idleTime;
    }

    public override void Exit()
    {
        base.Exit();
    }

    // 매 프레임마다 실행하는 Update에서는 _stateTimer가 0보다 작다면 IdleState를 탈출하고 MoveState로 들어감 
    public override void Update()
    {
        base.Update();
        
        if (_stateTimer < 0)
        {
            _enemyStateMachine.ChangeState(_skeletonController._moveState);
        }
    }
}

 

4) 이동하는 상태의 SkeletonStateMove

public class SkeletonStateMove : SkeletonStateGrounded
{
    public SkeletonStateMove(EnemyController enemyBaseController, EnemyStateMachine enemyStateMachine, string animatorBoolParamName, SkeletonController enemyController) 
        : base(enemyBaseController, enemyStateMachine, animatorBoolParamName, enemyController)
    {

    }

    public override void Enter()
    {
        base.Enter();
    }

    public override void Exit()
    {
        base.Exit();
    }

    // 매 프레임마다 실행되는 MoveState의 Update함수의 경우 다음과 같이 처리함
    public override void Update()
    {
        base.Update();

        // 기본적으로 Skeleton을 움지이게끔 하되,
        _skeletonController.SetVelocity(_enemyBaseController._moveSpeed * _skeletonController._facingDir, _rigidbody2D.velocity.y);

        // 만약 벽을 마주하고 있거나, 땅을 딛고 있지 않다면
        if (_skeletonController.DoDetectIsFacingWall() || !_skeletonController.DoDetectIsGrounded())
        {
            // 방향을 전환하고 IdleState로 변환함
            _skeletonController.Flip();
            _enemyStateMachine.ChangeState(_skeletonController._idleState);
        }
    }
}

 

 

728x90
반응형