오늘은 랜덤인카운터, 플레이어 이동 애니메이션을 구현해보겠습니다!!
1. 랜덤인카운터
'포켓몬스터'라는 게임 아시나요?? 이 게임에서 풀숲을 돌아다니다 보면 무작위로 야생 포켓몬을 만날 수 있는데요~
이러한 방식을 이용해서 제 게임에서 적과의 전투를 발생시킬 것입니다!
제가 생각한 인카운터 로직은 플레이어가 이동을 해서 밟고 있는 타일이 달라지면 어떤 메소드를 호출하고, 그 메소드에서 난수를 만들어 기준 이상이면 인카운터가 발생하는 방식입니다.
기존 PlayerController 스크립트에서 변경된 부분을 보겠습니다!
private float criticalPoint = 80;
private Vector3Int previousPosition;
.
.
.
private void Move(Vector2 direction)
{
if (CanMove(direction))
{
transform.position += (Vector3)direction;
Vector3Int afterPosition = groundTilemap.WorldToCell(transform.position);
if(previousPosition != afterPosition)
{
OnSteppedOnNewTile(afterPosition);
previousPosition = afterPosition;
}
}
}
.
.
.
.
private void OnSteppedOnNewTile(Vector3Int newPosition)
{
int P = Random.Range(1,100);
if(P >= criticalPoint)
{
Debug.Log("Encounter!!");
}
}
먼저 인카운터 발생 기준점을 80으로 잡아주었습니다. 또한 다른 타일을 밟았는지 확인하기 위해서 이전위치를 기억하는 변수를 만들었습니다.
(생략되었지만 Start메소드에서는
previousPosition = currentCell;로 현재 위치를 넣어주었습니다.)
Move메소드에서는 afterPosition 메소드에 이동후의 위치를 저장해주었고, 이전 위치와 이후 위치가 다르다면 메소드를 호출해주고 이전위치를 이후위치로 바꿔주었습니다.
OnSteppedOnNewTile메소드에서는 1~99까지의 정수 난수를 받는 변수 P가 임계점 이상이면 "Encounter!!"을 콘솔창에 출력하게 했습니다.
2. 이동 애니메이션
저번에 말했듯이 스프라이트에 캐릭터 모습하나 딸랑 있기 때문에 이동할 때 심심할 수 있습니다. 그래서 이동할 때 캐릭터가 흔들리는 애니메이션을 추가해주었습니다.
흔들리는 효과를 주는데는 3가지의 방법이 있는데
캐릭터만 비추는 카메라 흔들기
애니메이터로 애니메이션 만들기
스크립트에 코루틴 만들기
이렇게 있습니다. 저는 코루틴을 사용했습니다.
스크립트 변경점입니다!!
SpriteRenderer spriteRenderer;
private Coroutine shakeCoroutine;
.
.
.
private void Move(Vector2 direction)
{
if(direction.x != 0)
{
spriteRenderer.flipX = (direction.x == 1);
}
if (CanMove(direction))
{
transform.position += (Vector3)direction;
if (shakeCoroutine != null)
{
StopCoroutine(shakeCoroutine);
}
shakeCoroutine = StartCoroutine(ShakeSpriteRoutine(direction));
Vector3Int afterPosition = groundTilemap.WorldToCell(transform.position);
if(previousPosition != afterPosition)
{
OnSteppedOnNewTile(afterPosition);
previousPosition = afterPosition;
}
}
}
.
.
.
private IEnumerator ShakeSpriteRoutine(Vector2 direction)
{
Transform spriteTransform = spriteRenderer.transform;
float targetRotationZ = 0f;
if(direction.x > 0)
{
targetRotationZ = -5f;
}
else if(direction.x < 0)
{
targetRotationZ = 5f;
}
else
{
targetRotationZ = 5f;
}
spriteTransform.localPosition = new Vector3(0,0.12f,0);
spriteTransform.localRotation = Quaternion.Euler(0,0,targetRotationZ);
yield return new WaitForSeconds(0.06f);
spriteTransform.localPosition = Vector3.zero;
spriteTransform.localRotation = Quaternion.identity;
yield return new WaitForSeconds(0.1f);
}
}
1)플레이어 방향 바꾸기
direction.x != 0: 위아래로만 움직일 때는 이미지를 뒤집을 필요가 없으므로, 오직 좌우 입력(direction.x가 0이 아닐 때)이 있을 때만 내부 로직을 실행합니다.
spriteRenderer.flipX = (direction.x == 1);:
만약 플레이어가 오른쪽 키를 누르면 direction.x 값은 1이 됩니다. 이때 (direction.x == 1)이라는 조건식은 참(true)이 되므로 flipX가 켜집니다.
반대로 왼쪽 키를 누르면 direction.x 값은 -1이 됩니다. 이때 조건식은 거짓(false)이 되므로 flipX가 꺼집니다.
즉, 원본 스프라이트가 왼쪽을 바라보고 있을 때 오른쪽을 누르면 이미지를 뒤집고(true), 왼쪽을 누르면 원래 이미지대로 보여주어(false) 자연스럽게 이동 방향을 쳐다보게 만듭니다.
2)코루틴
캐릭터가 타일맵 위를 한 칸 이동하는 순간, 스프라이트 이미지를 물리적으로 튕겨주어 통통 튀는 듯한 생동감을 주는 핵심 연출입니다. 시간차를 두고 빠르게 일어나는 3단계 과정으로 진행됩니다.
준비 단계: 플레이어 본체의 실제 좌표 이동에 방해를 주지 않기 위해서, 이동은 본체가 조용히 처리하게 두고 오직 겉껍데기 이미지(자식 오브젝트)의 위치와 회전 정보만 따로 가져와서 만집니다.
1단계 (기울기 각도 정하기): 움직이는 방향에 맞춰 몸을 자연스럽게 눕히기 위한 회전값을 미리 정합니다. 오른쪽으로 갈 때는 시계 방향으로 살짝 몸을 눕히고, 왼쪽으로 갈 때는 반시계 방향으로 몸을 기울입니다. 위아래로 이동할 때도 심심하지 않도록 기본적으로 몸이 갸우뚱하도록 설정해 둡니다.
2단계 (공중 부양과 회전): 이동이 시작되는 순간, 캐릭터 이미지를 위쪽 방향으로 가볍게 들어 올림과 동시에 1단계에서 계산해 둔 각도로 몸을 기울입니다. 이 자세를 약 0.06초라는 아주 찰나의 시간 동안 유지하여 유저에게 "점프하는 느낌"을 즉각적으로 전달합니다.
3단계 (착지와 원위치): 짧은 공중 동작이 끝나면, 들어 올렸던 이미지를 다시 정중앙 제자리로 뚝 떨어트리고 갸우뚱했던 몸도 똑바로 세운 뒤 약 0.1초 동안 대기합니다.
이렇게 오늘은 랜덤 인카운터와 이동 애니메이션을 만들어보았습니다!
다음에는 인카운터가 발생해서 전투로 들어가는 부분을 만들어보겠습니다.