게임에서 점프 기능은 매우 중요합니다. 탐험의 영역을 넓히거나, 적의 공격을 피하는 등. 수 많은 확장이 가능합니다. 유니티에서는 오브젝트의 y축 값을 올려주며 위아래로 이동시키며 점프를 구현할 수 있지만, 자연스러운 애니메이션도 중요합니다.
✅ 낮은 점프력에서의 애니메이션
✅ 높은 점프력(6x) 에서의 애니메이션
유니티의 애니메이션은 프레임으로 구성되어있어, 시간을 조정하는 Speed를 제외하면 런타임 내에서는 구성하기 어렵다.
그렇기에 점프 높이에 따라 점프 종료 직전까지 자연스러운 애니메이션을 구현하기위해 중간 단계에 점프루프(JumpLoop)를 넣어 바닥에 닿기 직전까지 루프가 돌며 공중에서 허우적대는(특정한 애니메이션)을 재생하도록 하였다.
❇️ 애니메이션 컨트롤러
Jumping Up 애니메이션이 실행되고, Jumping Forward로 넘어가며, 바닥에 닿기 전까지 Jumping Forward <> Jumping Backward가 서로 교차되며 재생된다.
사용한 애니메이션은 Mixamo에서 Jumping Down, Up, Jumping을 사용하였다. 아이디어는 언리얼 엔진의 기본 애니메이션인 마네킹 점프를 빗대어 구성한것이다.
Jumping 애니메이션은 Jumping Loop가 아닌 시작 <> 끝의 모든 애니메이션이기에, Loop로 사용하기위해
Speed가 -0.2 / +0.2인것을 볼 수 있다.
//PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum PlayerStates { GLOBAL = 0, IDLE, WALK, RUN, JUMP_UP, JUMP_DOWN, JUMP_LOOP };
public class PlayerController : BaseGameEntity
{
...
[Header(":: Player Movement Properties")]
[SerializeField] private float mSpeed = 3.5f; //캐릭터 움직임 스피드
[SerializeField] private float mJumpForce = 5.0f; //캐릭터 점프 힘
[SerializeField] private float mGravity = 9.8f; //캐릭터에게 작용하는 중력
...
/// <summary>
/// 입력을 통해 플레이어를 이동시킨다.
/// </summary>
/// <param name="multiply">이동속도의 배수만큼 움직일 수 있도록 한다.</param>
public void Move(float multiply = 1.0f)
{
//위, 아래 움직임 입력
mMoveInput = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
//현재 캐릭터가 땅에 있는가
if (CharController.isGrounded)
{
//벡터를 로컬 좌표계 기준에서 월드 좌표계 기준으로 변환한다
mMoveDir = mCameraArm.transform.TransformDirection(mMoveInput);
//y축 방향 제거
mMoveDir.y = 0;
//스피드 증가
mMoveDir *= mSpeed;
//캐릭터 방향 돌리기
if (mMoveInput.magnitude > 0)
{
ViewRot();
}
}
//캐릭터에 중력 적용
mMoveDir.y -= mGravity * Time.deltaTime;
//캐릭터 움직임
CharController.Move(mMoveDir * multiply * Time.deltaTime);
}
...
/// <summary>
/// 점프 키를 눌러 점프를 하도록 한다.
/// </summary>
/// <returns>점프를 한 경우에 true가 반환된다.</returns>
public bool Jump()
{
if(CharController.isGrounded && Input.GetButton("Jump"))
{
mMoveDir.y = mJumpForce;
CharController.Move(mMoveDir * Time.deltaTime);
return true;
}
return false;
}
...
/// <summary>
/// 점프 애니메이션이 종료되는 시점에 애님컨트롤러에서 호출
/// </summary>
public void FinishJump()
{
MessageDispatcher.Instance.DispatchMessage(0, EntityName, EntityName, "FinishJump");
}
}
|
cs |
public bool Jump()
- 플레이어가 점프를 하면, mMoveDir의 y값이 mJumpForce가 되며, 위로 떠오르게 된다.
- 점프를 실행하는 함수는 PlayerController.cs가 아닌 State를 담는 클래스에 별도로 지정되어있다.
//PlayerOwnedStates.cs
using UnityEngine;
namespace PlayerOwnedStates
{
public class IDLE : State<PlayerController>
{
public override void Enter(PlayerController entity)
{
entity.Animator.SetInteger("_AnimState", 0);
}
public override void Execute(PlayerController entity)
{
//움직임
entity.Move();
//점프입력시 JUMP_UP 스테이트로 변경
if(entity.Jump()) { entity.ChangeState(PlayerStates.JUMP_UP); }
//현재 입력이 0보다 큰경우, WALK 스테이트로 변경
else if(entity.MoveInput > 0f) { entity.ChangeState(PlayerStates.WALK); }
//떨어짐 값이 0.5보다 작은경우 JUMP_LOOP 스테이트로 변경
else if(entity.CurrentFallSec > 0.5f) { entity.ChangeState(PlayerStates.JUMP_LOOP); }
}
public override void Exit(PlayerController entity)
{
}
public override bool OnMessage(PlayerController entity, Telegram telegram)
{
return false;
}
}
public class JUMP_UP : State<PlayerController>
{
public override void Enter(PlayerController entity)
{
entity.Animator.SetTrigger("_StartJump");
}
public override void Execute(PlayerController entity)
{
//점프 힘에 의한 잔류 움직임
entity.Move();
//땅에 닿은경우, JUMP_DOWN 스테이트로 변경
if(entity.CharController.isGrounded) { entity.ChangeState(PlayerStates.JUMP_DOWN); }
}
public override void Exit(PlayerController entity)
{
}
public override bool OnMessage(PlayerController entity, Telegram telegram)
{
return false;
}
}
public class JUMP_DOWN : State<PlayerController>
{
public override void Enter(PlayerController entity)
{
entity.Animator.SetTrigger("_FinishJump");
}
public override void Execute(PlayerController entity)
{
}
public override void Exit(PlayerController entity)
{
}
public override bool OnMessage(PlayerController entity, Telegram telegram)
{
//외부로부터 (애니메이션 이벤트) FinishJump가 온 경우, IDLE 스테이트로 변경
switch(telegram.message)
{
case "FinishJump":
{
entity.ChangeState(PlayerStates.IDLE);
break;
}
}
return false;
}
}
}
|
cs |
게임 내 오브젝트들의 스테이트를 효율적으로 관리하기 위해 FSM 디자인 패턴으로 구현한 스테이트 머신이다.
작성일 기준으로 IDLE 또는 WALK, RUN 상태에서만 Jump를 할 수 있으며 Jump를 실행하면 JUMP_UP 스테이트로 전환된다.
public class JUMP_UP::Enter()
- entity.Animator.SetTrigger("_StartJump"); 함수가 JUMP_UP스테이트가 될 때 단 한번 실행되며, 애니메이션 트리거를 활성화하여 점프 시작 애니메이션을 재생하도록 한다.
public class JUMP_UP::Execute()
- JUMP_UP의 상태에서 Update처럼 매 프레임마다 호출되며, isGrounded가 활성화 되면 JUMP_DOWN 스테이트로 전환시킨다.
public class JUMP_DOWN::Enter()
- JUMP_DOWN상태가 되면 단 한번 실행된다. _FinishJump 애니메이션 트리거를 활성화한다.
public class JUMP_DOWN::OnMessage()
- PlayerController.cs에서 public void FinishJump()로부터 호출되며 애니메이션이 종료되면 IDLE 상태로 되돌려 다시 움직일 수 있도록 한다.
'unity game modules' 카테고리의 다른 글
[유니티] 지정한 위치에 직선 형태로 여러 발을 소환하여 공격하기 (0) | 2022.09.26 |
---|---|
[유니티] 낙하 감지(Fall Detection) 및 낙하 연출 (0) | 2022.09.01 |
[유니티] TTS(Text-To-Speech) 목소리 구현 (0) | 2022.08.05 |
[유니티] 게임 옵션 저장 (0) | 2022.08.03 |
[유니티] .obj 파일을 인터넷으로 다운로드하여 사용 (0) | 2022.07.31 |