Unity 심화 - 플레이어 점프 상태 만들기

Amberjack·2024년 2월 21일
0

Unity

목록 보기
42/44

🪂 플레이어 힘 적용 시키기

플레이어가 공중에서 지면을 향해 떨어지도록 만들어보자.

ForceReceiver.cs

현재 Player는 Rigidbody를 사용하고 있지 않기 때문에 Player가 힘의 영향을 받도록 스크립트 처리를 해주자.

public class ForceReceiver : MonoBehaviour
{
    [SerializeField] private CharacterController controller;
    [SerializeField] private float drag = 0.3f;     // 저항

    private Vector3 dampingVelocity;
    private Vector3 impact;
    private float verticalVelocity;

    public Vector3 Movement => impact + Vector3.up * verticalVelocity;

    private void Update()
    {
        // 현재 플레이어가 땅에 위치해 있으면서 상태가 Ground라면
        if(verticalVelocity < 0f && controller.isGrounded)
        {
            // Physics.gravity.y : 중력 가속도 -9.7을 의미. 따라서 verticalVelocity는 위로 솟아날 경우를 제외하고 항상 음수일 것이다. 
            verticalVelocity = Physics.gravity.y * Time.deltaTime;
        }
        else
        {
            // 현재 플레이어가 공중에 있으면 Physics.gravity.y * Time.deltaTime 만큼 가속.
            verticalVelocity += Physics.gravity.y * Time.deltaTime;
        }

        // impact가 Vector3.zero가 될 때까지 감소된다.
        impact = Vector3.SmoothDamp(impact, Vector3.zero, ref dampingVelocity, drag);
    }

    public void Reset()
    {
        // 플레이어가 땅에 닿으면 impact, verticalVelocity 초기화
        impact = Vector3.zero;
        verticalVelocity = 0f;
    }

    public void AddForce(Vector3 force)
    {
        impact += force;
    }

    public void Jump(float jumpForce)
    {
        verticalVelocity += jumpForce;
    }
}

Player.cs 수정하기

Player에서 ForceReceiver를 활용할 수 있도록 선언해주자.

''' 생략
public ForceReceiver ForceReceiver { get; private set; }

''' 생략
private void Awake()
{
	''' 생략
	ForceReceiver = GetComponent<ForceReceiver>();
}

PlayerBaseState.cs 수정하기

위에서 만든 ForceReceiver를 활용하기 위해 PlayerBaseState를 수정해주자.

private void Move(Vector3 movementDirection)
{
    float movementSpeed = GetMovementSpeed();
    stateMachine.Player.Controller.Move(
        ((movementDirection * movementSpeed) + stateMachine.Player.ForceReceiver.Movement)
        * Time.deltaTime
        );
}

확인해보기

🦘 플레이어 점프 상태 만들기

PlayerAirState, PlayerJumpState, PlayerFallState 스크립트를 만들어 준다.

PlayerAirState.cs

public class PlayerAirState : PlayerBaseState
{
    public PlayerAirState(PlayerStateMachine playerStateMachine) : base(playerStateMachine)
    {
    }

    public override void Enter()
    {
        base.Enter();
        StartAnimation(stateMachine.Player.AnimationData.AirParameterHash);
    }

    public override void Exit()
    {
        base.Exit();
        StopAnimation(stateMachine.Player.AnimationData.AirParameterHash);
    }
}

PlayerJumpState.cs

public class PlayerJumpState : PlayerAirState
{
    public PlayerJumpState(PlayerStateMachine playerStateMachine) : base(playerStateMachine)
    {
    }

    public override void Enter()
    {
        stateMachine.JumpForce = stateMachine.Player.Data.AirData.JumpForce;
        stateMachine.Player.ForceReceiver.Jump(stateMachine.JumpForce);

        base.Enter();
        StartAnimation(stateMachine.Player.AnimationData.JumpParameterHash);
    }

    public override void Exit()
    {
        base.Exit();
        StopAnimation(stateMachine.Player.AnimationData.JumpParameterHash);
    }

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

        // 플레이어가 점프를 한 후, 공중에서 velocity.y가 0보다 작아질 때, 즉 플레이어가 떨어지기 시작할 때
        // FallState로 전환하기
        if (stateMachine.Player.Controller.velocity.y <= 0)
        {
            stateMachine.ChangeState(stateMachine.FallState);
            return;
        }
    }
}

PlayerFallState.cs

public class PlayerFallState : PlayerAirState
{
    public PlayerFallState(PlayerStateMachine playerStateMachine) : base(playerStateMachine)
    {
    }

    public override void Enter()
    {
        base.Enter();
        StartAnimation(stateMachine.Player.AnimationData.FallParameterHash);
    }

    public override void Exit()
    {
        base.Exit();
        StopAnimation(stateMachine.Player.AnimationData.FallParameterHash);
    }

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

		// 플레이어가 땅에 닿으면 Idle 상태로 전환
        if (stateMachine.Player.Controller.isGrounded)
        {
            stateMachine.ChangeState(stateMachine.IdleState);
            return;
        }
    }
}

PlayerStateMachine.cs 수정

위에서 생성한 JumpState, FallState를 추가해주자.

'''생략
public PlayerJumpState JumpState { get; }
public PlayerFallState FallState { get; }

'''생략
public PlayerStateMachine(Player player)
{
	'''생략
	JumpState = new PlayerJumpState(this);
	FallState = new PlayerFallState(this);
}

PlayerBaseState.cs 수정하기

다시 한번 PlayerBaseState를 수정해주자. 이번에는 점프 키를 입력받는 기능을 추가해준다.

'''생략
protected virtual void AddInputActionsCallbacks()
{
    '''생략
    stateMachine.Player.Input.PlayerActions.Jump.started += OnJumpStarted;
}

protected virtual void RemoveInputActionsCallbacks()
{
    '''생략
    stateMachine.Player.Input.PlayerActions.Jump.started -= OnJumpStarted;
}

'''생략
protected virtual void OnJumpStarted(InputAction.CallbackContext context)
{

}

PlayerBaseState에서 점프 키 Input을 받아오기 위해 AddInputActionsCallbacks에 OnJumpStarted라는 이벤트를 구독해준다.
이후, OnJumpStarted는 플레이어가 지상에 있을 때 점프를 하도록 만들어주기 위해 PlayerBaseState를 상속받는 PlayerGroundedState에서 사용하도록 virtual로 만들어준다.

PlayerGroundedState.cs

이제 플레이어가 지상에서 점프를 하도록 만들어보자.

'''생략
protected override void OnJumpStarted(InputAction.CallbackContext context)
{
    stateMachine.ChangeState(stateMachine.JumpState);
}

점프 키가 입력되었을 때 플레이어 상태를 JumpState로 변경해준다.

🎞️ Air 상태의 애니메이션 만들기

플레이어의 점프, 낙하 시 애니메이션을 만들어보자.

Sub-StateMachine Air 만들기

플레이어의 애니메이션 컨트롤러에 들어가서 새로운 Sub-StateMachine을 만들어주고 이름을 Air로 변경한다.

이후 Ground와 Air 간의 Transition을 만들어주는데, Air → Ground로 갈 때, StateMachine → Ground로 연결해주어야 한다.

그 다음에는 Air의 애니메이션들을 추가해준다. Jump와 Fall 애니메이션을 넣어준 뒤, 아래와 같이 Transition을 만들어준다.

확인해보기

점프가 되는 모습!

0개의 댓글