마우스를 움직일 때 커서의 좌표에 따라 플레이어의 시선이 변경됩니다.
스크린 크기를 x 와 y
마우스 좌표를 n 과 m 이라고 가정
마우스 좌표를 스크린 크기로 나눈 뒤 0과 1을 기준으로 정규화를 시키면 (n÷x, m÷y) 입니다.
n/x 와 m/y 의 값에 0.5 씩 감소시켜 화면 중점을 0으로 만듭니다.
Input 클래스에서 마우스 좌표를, Screen 클래스에서 화면의 너비와 높이 받아왔습니다.
그러나 Camera 클래스를 사용하면 더 간단하게 만들 수 있는 사실을 알게되어 코드를 수정하였습니다.
Screen To Viewport Point는 화면 크기를 미리 0~1로 정규화 시켜 값을 얻는 함수입니다.
따라서 직접 정규화를 할 필요가 없었습니다.
플레이어가 -y를 향해 달리고 있어도,
마우스 포인터의 위치가 플레이어의 위치보다 +y라면
플레이어의 시선은 마우스 포인터의 위치인 +y 좌표를 바라보고 이동은 -y로 하게 됩니다.
그림으로 나타내면 아래와 같습니다.
이 로직을 코드로 나타내는 과정입니다.
캐릭터의 좌표와 마우스 커서의 좌표를 빗변으로 두고 그 사이의 각도를 구하면
사진처럼 어느 구간에 위치하는지 판별할 수 있다고 생각했습니다.
Vector2.Angle과 Quaternion.FromToRotation을 사용해 각도를 구해봤지만 실제로 원하는 결과는 나오지 않아 다른 방식으로 접근하게 되었고,
긴 고민 끝에 직선의 방정식 그래프와 절대값을 사용하여 원하는 결과를 얻을 수 있었습니다.
1. y=x 와 y=-x 그래프
2. y=|x| 와 y=-|x| 그래프
3. x=|y| 와 x=-|y| 그래프
4. 왼쪽 사각형의 빨간 부분은 y>|x| 로 구하며 파란 부분은 y<-|x| 가 됩니다.
5. 오른쪽 사각형은 반대로 빨간 부분이 x>|y| 이며 파란 부분은 x<-|y| 입니다.
간단한 이동 함수를 만들고 게임의 스프라이트를 추출하여 게임과 동일한 환경으로 비교해보았습니다. 커서와 캐릭터를 움직이며 테스트해보니 원작과 다른 부분을 찾았는데요,
만약 캐릭터가 커서보다 아래에 있거나, 커서가 플레이어보다 위에 있을 경우 캐릭터가 위를 바라보지 않는 것이었습니다.
이 문제는 두가지 해결법이 있는데,
첫 번째는 카메라를 캐릭터의 자식 오브젝트로 넣어 정적 좌표로 구하는 방법과
두 번째는 월드 좌표에서 캐릭터의 위치를 뺀 값을 전달하는 것입니다.
수정 후 기존과는 달라진 부분이 생겼습니다.
사진과 같이 좌측과 우측면의 처리 면적이 더 넓어진 것입니다.
하지만 게임을 플레이해보니 만든 로직과 실제로 사용하는 로직이 유사하게 작동하여 시선처리는 완성했습니다.
직접 시도하기 전에는 무척 간단해보였지만 화면을 분할하는 과정이 생각한 것과는 많이 달라서 좋은 경험이었습니다.
이동 함수를 추가하여 마무리를 합니다.
코어키퍼는 이동 애니메이션과 시선처리 애니메이션을 각각 다른 axies로 접근해야 합니다.
따라서 파라미터를 단순 이동 노드로 그치지 않고 시선처리 노드도 만들어주어야 합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Animate : MonoBehaviour
{
string animState = "AnimState";
string lookState = "LookState";
Animator anim;
enum AnimState //이동 애니메이션 상태
{
up = 1,
down = 2,
side = 3,
stop = 4
}
enum LookState //캐릭터 시선 상태
{
up = 1,
down = 2,
side = 3
}
void Awake()
{
anim = GetComponent<Animator>();
}
void Update()
{
UpdateState();
}
private void UpdateState() //애니메이션, 시선 상태 조건
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
if (MousePoint.instance.isUp == true && (h != 0 || v != 0))
anim.SetInteger(animState, (int)AnimState.up);
else if (MousePoint.instance.isDown == true && (h != 0 || v != 0))
anim.SetInteger(animState, (int)AnimState.down);
else if (MousePoint.instance.isSide == true && (h != 0 || v != 0))
anim.SetInteger(animState, (int)AnimState.side);
else
anim.SetInteger(animState, (int)AnimState.stop);
if (MousePoint.instance.isUp == true)
anim.SetInteger(lookState, (int)LookState.up);
if (MousePoint.instance.isDown == true)
anim.SetInteger(lookState, (int)LookState.down);
if (MousePoint.instance.isSide == true)
anim.SetInteger(lookState, (int)LookState.side);
}
}
간단하지만 코어키퍼 게임에서 만들어보고 싶었던 상하체 분리(?) 이동 기능을 구현해 보았습니다.
시행착오가 있었지만 결과물은 원작과 유사하게 작동하는 것 같아 다행입니다.