유니티/게임 프로젝트

2D RPG - (5) StateMachine의 구성과 완성 (마지막)

monstro 2024. 12. 9. 01:28
728x90
반응형

1) 벽에 달라붙어 미끄러지는 상태의 PlayerStateWallSlide

public class PlayerStateWallSlide : PlayerState
{
    public PlayerStateWallSlide(PlayerController inController, PlayerStateMachine inStateMachine, string inParamName) 
        : base(inController, inStateMachine, inParamName)
    {

    }

    // State에 돌입
    public override void Enter()
    {
        base.Enter();

        
    }

    // State에서 탈출
    public override void Exit()
    {
        base.Exit();
    }

    // State에서 매 프레임마다 진행
    public override void Update()
    {
        base.Update();

        if (_isJumping)
        {
            _stateMachine.ChangeState(_controller._wallJumpState);
            return;
        }

        if (_xInput != 0 && _controller._facingDir != _xInput)
            _stateMachine.ChangeState(_controller._idleState);

        if (_yInput < 0.0f)
            _rigidbody2D.velocity = new Vector2(0, _rigidbody2D.velocity.y);
        else
            _rigidbody2D.velocity = new Vector2(0, _rigidbody2D.velocity.y * 0.7f);

        if (_controller.DoDetectIsGrounded())
            _stateMachine.ChangeState(_controller._idleState);
    }
}

 

벽에 달라붙는 상태의 PlayerStateWallSlide는 Update에서 다음의 로직을 수행합니다.

만약, 점프입력이 들어왔다

Player의 CurrentStatePlayerStateWallJump로 변경하고 아래의 로직을 일체 실행하지 않고 종료합니다.

x축 입력이 들어오고 플레이어의 방향이 x축 입력과 동일하지 않다

벽에서 떨어져 Player의 CurrentStatePlayerStateIdle로 변경합니다.

그리고 벽에 달라붙어 있는 동안

y축입력이 음수로 들어온다벽에서 빠른 속도로 내려오게 됩니다.

반대로 양수로 들어온다벽에서 천천히 내려오게 됩니다.

마지막으로 땅에 발이 닿게 되

Player의 CurrentStatePlayerStateIdle로 변경합니다.

 

2) 벽에 달라붙어 점프하는 상태의 PlayerStateWallJump

public class PlayerStateWallJump : PlayerState
{
    public PlayerStateWallJump(PlayerController inController, PlayerStateMachine inStateMachine, string inParamName) 
        : base(inController, inStateMachine, inParamName)
    {

    }

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

        _stateTimer = 0.1f;
        _controller.SetVelocity(5 * -_controller._facingDir, _controller._jumpForce);

    }

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

        if (_stateTimer < 0)
            _stateMachine.ChangeState(_controller._inAirState);

        if (_controller.DoDetectIsGrounded())
            _stateMachine.ChangeState(_controller._idleState);
    }
}

 

State에 돌입하게되는 Enter의 경우 다음의 로직을 수행합니다.

State들의 타이머인 _stateTimer를 0.1로 초기화하고 Player에게 가속도를 부여합니다. 

이때 x축으로의 가속도를 위 로직과 같이 구성하여 직선이 아닌 곡선의 형태로 떨어지게 만듭니다.

 

Update 함수에서는 다음의 로직을 수행합니다.

_stateTimer가 0.0초보다 작다Player의 CurrentState를 공중에 떠 있는 PlayerStateInAir로 변경합니다.

그리고 발이 땅에 닿게 된다Player의 CurrentStatePlayerStateIdle로 교체합니다.

 

3) 공격 상태인 PlayerStatePrimaryAttack

public class PlayerStatePrimaryAttack : PlayerState
{
    private int _comboCounter;

    private float _lastTimeAttacked;
    private float _comboWindow = 2;

    public PlayerStatePrimaryAttack(PlayerController inController, PlayerStateMachine inStateMachine, string inParamName) 
        : base(inController, inStateMachine, inParamName)
    {

    }

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

        if (_comboCounter > 2 || Time.time >= _lastTimeAttacked + _comboWindow)
            _comboCounter = 0;

        _controller._animtor.SetInteger("ComboCounter", _comboCounter);

        float _attackDir = _controller._facingDir;
        if (_xInput != 0)
            _attackDir = _xInput;

        _controller.SetVelocity(_controller._attackMovement[_comboCounter].x * _attackDir, _controller._attackMovement[_comboCounter].y);

        _stateTimer = 0.1f;
    }

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

        _controller.StartCoroutine("DoSomething", 0.1f);

        _comboCounter++;
        _lastTimeAttacked = Time.time;
    }

    public override void Update()
    {
        base.Update();

        if (_stateTimer < 0)
            _controller.SetZeroVelocity();

        if (_triggerCalled)
            _stateMachine.ChangeState(_controller._idleState);
    }
}

 

공격 상태의 경우 설계 구조가 꽤 복잡합니다.

 

1) 멤버 변수

_comboCounter콤보 공격을 수행하기 위해 현재 콤보를 기록하는 변수입니다.

_lastTimeAttacked마지막 공격을 수행한 시간을 기록하는 변수입니다.

마지막 변수인 _comboWindow콤보공격의 구성을 배열로 치환할 경우 마지막 인덱스를 의미합니다.

 

2) 멤버 함수

State에 돌입하는 Enter 함수에서는 다음의 로직을 수행합니다.

_comboCounter가 2보다 크거  _lastTimeAttacked과 _comboWindow를 더한 값보다 현재 시간이 더 크

_comboCounter0으로 초기화합니다.

 

이후에 Animtor에서 사용할 ComboCounter값을 설정합니다.

 

그리고 공격할 방향을 설정하기 위해 위와 같은 로직을 수행합니다.

이때 공격을 수행하면서 Player를 조금씩 이동시키면서 플레이의 긴장감을 높여줍니다.

최종적으로 _stateTimer를 0.1로 초기화합니다.

 

Exit 함수는 다음과 같이 로직을 수행합니다.

PlayerController의 DoSomething 코루틴을 수행하여

_doingSomethingtrue로 설정하고  0.1초 후다시 false로 설정합니다.

그리고 _comboCounter를 1증가시키고, _lastTimeAttacked를 현재 시간으로 기록합니다.

 

Update 함수는 아래와 같은 로직을 수행합니다.

_stateTimer가 0보다 작다면 공격을 수행하지 않으므로 CurrentState를 PlayerStateIdle로 교체합니다.

그리고 _triggerCalled가 true라면 AnimationEvent를 수행하고 CurrentState를 PlayerStateIdle로 교체합니다.

 

728x90
반응형