유니티 내에서 점프 애니메이션은 플레이어가 점프 입력을 하지 않을때도 일어날 수 있습니다. 예시로 앞으로 이동하다가 플레이어가 높은곳에서 떨어지게되는경우, 캐릭터는 높은 곳에서 떨어지기때문에 팔을 허우적 대는 등 현실과 흡사한 반응을 하도록 만들어줘야 더욱 자연스러운 모션 연출이 가능해집니다.
✅ 높은곳에서 떨어질 때, 플레이어는 IDLE 상태이기에 IDLE 애니메이션을 재생한다. 매우 어색한것을 볼 수 있다.
위 이미지에서는 특별한 처리를 하지 않아, 높은곳에서 떨어지면 직전의 애니메이션을 유지한다. 자연스럽지 못한 연출로 어색한 장면으로 볼 수 있다.
✅ 위 문제를 개선하여 높은곳에서 떨어질 때, 낙하를 감지하여 허우적대는 애니메이션을 재생하도록 한다.
🖋️ 낙하 감지 > Jump Loop > IDLE 까지를 간단하게 나타낸 흐름도
isGround가 false일때 falling Seconds를 초 단위로 계속 더해준다. 이 상황에서 isGround가 다시 true가 되는경우 이전까지 더해진 falling Seconds는 초기화된다(0).
반대로 falling Seconds가 일정 값보다 큰경우 JumpLoop를 실행하고, isGround가 true가 될때까지 루프를 반복하다 IDLE로 돌아가며 점프 모션은 종료된다.
//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
{
public CharacterController CharController; //캐릭터 컨트롤러
...
private float mCurrentFallSec = 0.0f; //점프가 아닌 상태에서 isGrounded가 아닌 시점으로부터의 시간
public float CurrentFallSec
{
get
{
return mCurrentFallSec;
}
}
...
/// <summary>
/// 플레이어의 위치가 땅 위가 아니라면(!isGrounded), 카운트를 한다.
/// 적절한 타이밍에 떨어지는 애니메이션으로 전환하기 위해 시간초를 계산한다
/// </summary>
public void CheckFalling()
{
if(CharController.isGrounded)
{
mCurrentFallSec = 0;
}
else
{
mCurrentFallSec += Time.deltaTime;
}
}
...
}
|
cs |
private float mCurrentFallSec
- isGrounded가 false인 시점으로부터 진행되는 초 단위 시간을 담는 변수이다.
- 외부 또는 내부에서 CurrentFallSec에서 획득하여 일정 시간이 지나면 특정 스테이트로 전환하여 후처리를 하도록 한다.
public void CheckFalling()
- CharController.isGrounded가 true이면 mCurrentFallSec을 0으로 되돌려놓는다 (초기화)
- CharController.isGrounded가 false인경우 mCurrentFallSec += Time.deltaTime;로 하여 진행 시간을 더한다.
- CheckFalling() 함수는 Update에서 호출되듯이 매 프레임마다 호출되어 계속 체크를 하도록 한다.
//PlayerOwnedStates.cs
using UnityEngine;
namespace PlayerOwnedStates
{
public class GLOBAL : State<PlayerController>
{
...
public override void Execute(PlayerController entity)
{
//떨어짐 체크
entity.CheckFalling();
}
...
}
public class IDLE : State<PlayerController>
{
...
public override void Execute(PlayerController entity)
{
...
//떨어짐 값이 0.5보다 큰경우 JUMP_LOOP 스테이트로 변경
else if(entity.CurrentFallSec > 0.5f) { entity.ChangeState(PlayerStates.JUMP_LOOP); }
}
...
}
public class WALK : State<PlayerController>
{
...
public override void Execute(PlayerController entity)
{
...
//떨어짐 값이 0.5보다 큰경우 JUMP_LOOP 스테이트로 변경
else if(entity.CurrentFallSec > 0.5f) { entity.ChangeState(PlayerStates.JUMP_LOOP); }
}
...
}
public class RUN : State<PlayerController>
{
...
public override void Execute(PlayerController entity)
{
...
//떨어짐 값이 0.5보다 큰경우 JUMP_LOOP 스테이트로 변경
else if(entity.CurrentFallSec > 0.5f) { entity.ChangeState(PlayerStates.JUMP_LOOP); }
}
...
}
...
public class JUMP_DOWN : State<PlayerController>
{
public override void Enter(PlayerController entity)
{
entity.Animator.SetTrigger("_FinishJump");
}
...
}
public class JUMP_LOOP : State<PlayerController>
{
public override void Enter(PlayerController entity)
{
entity.Animator.SetTrigger("_JumpLoop");
}
public override void Execute(PlayerController entity)
{
entity.Move();
if(entity.CharController.isGrounded) { entity.ChangeState(PlayerStates.JUMP_DOWN); }
}
...
}
}
|
cs |
public class GLOBAL::Execute()
- GLOBAL은 매 프레임마다 스테이트에 상관없이 항상 호출되는 Update를 가진다.
- 이곳에서 CheckFalling() 함수를 매 프레임마다 호출하여 낙하를 감지하도록 한다.
else if(entity.CurrentFallSec > 0.5f) { entity.ChangeState(PlayerStates.JUMP_LOOP); }
- 낙하 감지를 하는 스테이트에서 CurrentFallSec 값이 0.5보다 큰경우 떨어지고 있다고 판단하여 JUMP_LOOP 스테이트로 변경시킨다.
public class JUMP_LOOP::Execute()
- 변경된 후에는 isGrounded가 참이될때까지 기다렸다가, 참이 되는 즉시 JUMP_DOWN 스테이트로 변경시켜 점프를 마무리(바닥에 착지하는 애니메이션) 짓도록 한다.
'unity game modules' 카테고리의 다른 글
[유니티] 파일브라우저(FileExplorer)를 이용하여 로컬 파일 저장/읽기 (SimpleFileBrowser) (0) | 2022.10.22 |
---|---|
[유니티] 지정한 위치에 직선 형태로 여러 발을 소환하여 공격하기 (0) | 2022.09.26 |
[유니티] 자연스러운 점프(JumpLoop) (0) | 2022.08.27 |
[유니티] TTS(Text-To-Speech) 목소리 구현 (0) | 2022.08.05 |
[유니티] 게임 옵션 저장 (0) | 2022.08.03 |