지난 게시글에서는 자모로봇 플레이어의 카메라 조작 방식을 변경하는 부분을 다뤄보았었는데요.
https://itadventure.tistory.com/429
플레이어가 화면의 오른쪽에 위치하도록 스크립트를 구성해보았습니다.
요새 3인칭 FPS 게임들은 많이들 이런 방식을 선호하지요.
물론 크레이는 다른 컨텐츠가 목표이긴 하지만요 :)
이번 스토리는 이러한 부분 외에도 몇가지 부가적 기능에 대한 스크립트 설명편인데요.
자 그럼 하나씩 살펴보실까요?
우선 Main Camera 에 들어있던 카메라 스크립트를 아래 내용으로 바꿔 치기했는데요.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamController : MonoBehaviour {
public GameObject player; // 바라볼 플레이어 오브젝트입니다.
public float xmove = 2; // X축 누적 이동량
public float ymove = 25; // Y축 누적 이동량
public float distance = 1;
private Vector3 velocity = Vector3.zero;
private int toggleView = 3; // 1=1인칭, 3=3인칭
private float wheelspeed = 10.0f;
private Vector3 Player_Height;
private Vector3 Player_Side;
void Start() {
Player_Height = new Vector3(0, 1.5f, 0f);
Player_Side = new Vector3(-0.8f, 0f, 0f);
}
// Update is called once per frame
void Update() {
// 마우스 우클릭 중에만 카메라 무빙 적용
if (Input.GetMouseButton(1)) {
xmove += Input.GetAxis("Mouse X");
// 마우스의 좌우 이동량을 xmove 에 누적합니다.
ymove -= Input.GetAxis("Mouse Y");
// 마우스의 상하 이동량을 ymove 에 누적합니다.
}
transform.rotation = Quaternion.Euler(ymove, xmove, 0); // 이동량에 따라 카메라의 바라보는 방향을 조정합니다.
if (Input.GetMouseButtonDown(2)) toggleView = 4 - toggleView;
if (toggleView == 3) {
distance -= Input.GetAxis("Mouse ScrollWheel") * wheelspeed;
if (distance < 1f) distance = 1f;
if (distance > 100.0f) distance = 100.0f;
}
if (toggleView == 1) {
Vector3 Eye = player.transform.position + Player_Height;
Vector3 reverseDistance = new Vector3(0.0f, 0.0f, 0.5f);
// 카메라가 바라보는 앞방향은 Z 축입니다. 이동량에 따른 Z 축방향의 벡터를 구합니다.
transform.position = Eye + transform.rotation * reverseDistance;
// 플레이어의 위치에서 카메라가 바라보는 방향에 벡터값을 적용한 상대 좌표를 차감합니다.
}
else if (toggleView == 3) {
Vector3 Eye = player.transform.position
+ transform.rotation * Player_Side + Player_Height;
Vector3 reverseDistance = new Vector3(0.0f, 0.0f, distance);
// 카메라가 바라보는 앞방향은 Z 축입니다. 이동량에 따른 Z 축방향의 벡터를 구합니다.
transform.position = Eye - transform.rotation * reverseDistance;
}
}
}
제일 먼저 이 스크립트가 들어있는 오브젝트가 씬에서 실행될 때는
Start() 라는 메소드 내의 기능이 한번 실행됩니다.
void Start() {
Player_Height = new Vector3(0, 1.5f, 0f);
Player_Side = new Vector3(-0.8f, 0f, 0f);
}
클래스 내 아래와 같은 속성이 먼저 정의가 되어 있기 때문에,
이 메소드에서는 이 변수에 값이 한번 대입하고 그 값은 오브젝트 소멸까지 계속 유지되지요.,
private Vector3 Player_Height;
private Vector3 Player_Side;
각각의 값에는 어떤 의미가 있을까요?
이 값은 나중에 Update() 에서 지속적으로 사용될 예정인데요.
3인칭 모드에서 카메라가 바라보는 대상의 3차원 좌표값을 계산할 용도입니다.
Update() 에서 여러번 수행되는 것이 아니라 처음에 단 한번만 실행되기 때문에 속도향상에 도움이 됩니다.
키높이와 왼쪽의 이동 좌표를 벡터 변수 1개가 아닌 2개로 정의한 것은 이유가 있는데요.
이 부분은 뒤에서 다루도록 하겠습니다.
이 후 플레이가 진행되면서 Update() 메소드의 내용이 반복하여 실행됩니다.
void Update() {
:
}
유사 내용을 전에 잠깐 다루었으나 변경된 부분도 있어 상세 설명 들어갑니다 :)
제일 먼저 처리되는 부분은 마우스의 우클릭 드래그입니다. 해당 코드는 아래와 같은데요.
// 마우스 우클릭 중에만 카메라 무빙 적용
if (Input.GetMouseButton(1)) {
// 마우스의 좌우 이동량을 xmove 에 누적합니다.
xmove += Input.GetAxis("Mouse X");
// 마우스의 상하 이동량을 ymove 에 누적합니다.
ymove -= Input.GetAxis("Mouse Y");
}
// 이동량에 따라 카메라의 바라보는 방향을 조정합니다.
transform.rotation = Quaternion.Euler(ymove, xmove, 0);
Input.GetMouseButton(1) 는 마우스의 우측 버튼이 눌렸는지 눌려있지 않은지를 판단하고,
눌려진 경우 true, 눌려지지 않은 경우 false 의 값을 갖습니다.
그래서 눌려진 경우 if 조건문에서 참으로 판단, 그 안쪽 부분이 실행됩니다.
if (Input.GetMouseButton(1)) {
:
}
if 안쪽에서는 마우스의 이동량을 받아다 처리하는데요.
Input.GetAxis("Mouse X") 가 마우스를 좌우로 움직인 상대 이동량이며,.
Input.GetAxis("Mouse Y") 가 마우스를 상하로 움직인 상대 이동량입니다.
이 상대 이동량은 정확히는 한 프레임간의 이동량을 의미합니다.
30프레임이 진행되었다면 Update() 메소드에 30번의 각기 다른 값이 들어옵니다.
사용자가 마우스를 그동안 빠르거나 늦게 움직였을수도 있기 때문에 매번 다른 값이 들어옵니다.
마우스를 빨리 이동한 시간에는 많은 이동량이 입력되고,
마우스를 느리게 이동한 시간에는 느린 이동량이 입력되겠지요.
이 값을 xmove 와 ymove 에 계속 더해주는데요.
마우스를 오른쪽으로 움직이면 이동량이 더하기 수치로 xmove 에 누적되나,
마우스를 왼쪽으로 움직이면 이동량이 마이너스 수치가 들어와 실제적으로 이동량의 수치를 뺍니다.
참고로 ymove 는 3D와 2D가 방향이 정반대이기 때문에 더해주는 것이 아니라 기본적으로 누적해서 빼줍니다.
// 마우스의 좌우 이동량을 xmove 에 누적합니다.
xmove += Input.GetAxis("Mouse X");
// 마우스의 상하 이동량을 ymove 에 누적합니다.
ymove -= Input.GetAxis("Mouse Y");
xmove, ymove 는 클래스 내에 속성변수로 정의되어 있으며 초기값은 아래와 같습니다.
public float xmove = 2; // X축 누적 이동량
public float ymove = 25; // Y축 누적 이동량
이렇게 누적해주는 이유는 무엇일까요?
그것은 속도 때문이지요. 매번 현재 카메라가 바라보는 방향을 읽어다가 더해준다고 한다면
바라보는 방향을 읽는 동작 조차도 속도에 영향을 미치기 떄문입니다.
그래서 이렇게 누적된 이동량이 반영된 속성변수를 정의하고 이 값을 최종적으로 카메라에 적용하는 것이지요.
카메라에 방향을 적용하는 코드는 아래와 같습니다.
transform.rotation = Quaternion.Euler(ymove, xmove, 0);
이 스크립트는 카메라에 들어 있습니다.
그러니까 여기서 transform 은 카메라의 transform 컴포넌트이고,
이 트랜스폼을 이용하여 카메라의 위치나 방향을 조정할 수 있습니다.
transform.rotation 은 바로 카메라의 바라보는 회전값인데요.
어떤 정확한 위치가 아니라 동서남북과 같은 방향을 의미합니다.
여기에 Quaternion.Euler( ymove, xmove, 0) 이라는 값을 대입해주는 데요.
Quaternion 은 쿼터니온이라는 복잡한 수학적 개념을 쉽게 사용할 수 있게 해주는
유니티의 내장 객체입니다.
이 중 Quaternion.Euler()은 오일러 각도값을 쿼터니온값으로 변환해주는 메소드인데요.
오일러 각도란 오브젝트를 단순히 X축, Y축, Z축으로 얼마 회전할지를 결정하는 회전값입니다.
이 값을 쿼터니온이라는 유니티의 회전값으로 변환해 주는 것입니다.
얼핏 보기에는 여기에 대입되는 값이 (xmove, ymove, 0) 이런 순서가 되어야 할것 같지만 그렇지 않습니다.
그 이유는 마우스를 좌우로 움직일 경우 카메라의 Y축이 회전해야 하기 때문이고,
마우스를 상하로 움직일 경우 카메라의 X축이 회전해야 하기 때문입니다
3번째 좌표는 항상 0이 지정되어 있는데 이 값을 바꾸면 어떻게 될까요?
크레이가 실험결과를 제공해드릴테니 직접 해보실 필요는 없습니다 :)
45도를 적용한 경우
transform.rotation = Quaternion.Euler(ymove, xmove, 45);
-90도를 지정한 경우
transform.rotation = Quaternion.Euler(ymove, xmove, -90);
위와 같이 카메라의 앵글이 좌우로 회전합니다.
뭐 이런 카메라 조정은 좌우로 흔들리는 배같은 컨텐츠나 한바퀴 빙글 도는 비행기를 만드는 경우 시선처리에 유용하게 사용할 수 있을것 같습니다 :)
이제 그 다음 스크립트는 마우스 중앙 버튼을 클릭할 때마다 3인칭 모드와 1인칭 모드를 전환하는 기능입니다.
if (Input.GetMouseButtonDown(2))
toggleView = 4 - toggleView;
마우스 우클릭과 비슷하게 Input.GetMouseButtonDown(2) 는 마우스 가운데 버튼이 눌려졌는가를 판단하는 메소드인데요. 마우스 중앙 버튼을 클릭하면 어떤 일이 일어났었나요?
바로 1인칭 보기 모드와 3인칭 보기 모드가 왔다 갔다 전환되었습니다.
앞에서는 Input.GetMouseButton 이 사용되었었는데요.
여기서는 Input.GetMouseButtonDown 이 사용되었군요. 차이점이 무엇일까요?
그것은 바로 버튼을 누른 것을 한번만 체크한다는 것입니다.
즉 처음에 버튼을 누른 한번만 잠깐 true 값이 되었다가 버튼을 누르고 있는 상태로 떼지 않고 않으면 이 후에는 false 가 되는 것이지요.
다시 true 가 되게 하려면 버튼을 떼었다가 다시 눌러야 합니다.
왜 다르게 했을까요? 그것만 버튼을 누르는 동안의 반복 동작을 제한하기 위해서입니다.
만일 가운데 버튼을 누르고 있으면 1인칭 3인칭 모드가 계속 토글되면 화면이 정신없어질 우려가 있습니다.
그러니까 버튼을 누르면 1초동안 누르건 3초동안 누르건간에 1, 3인칭간 전환은 1번만 작동되게 하려는 것이지요.
이 때 스크립트에서는 toggleView 라는 속성변수의 값을 이때 바꿔주는데요.
toggleView 의 초기값은 아래와 같이 정의되어 있습니다.
3인칭 보기 모드라는 것이지요
// 1=1인칭, 3=3인칭
private int toggleView = 3;
마우스 가운데 버튼을 클릭하면 아래 코드가 실행이 되는데요.
toggleView = 4 - toggleView;
toggleView 의 값이 3일 때는, 4- toggleView 를 계산하고 그 값을 다시 toggleView 에 집어 넣습니다.
toggleView ← 4 - toggleView ( 3 )
4 - 3 = 1 이기 때문에 toggleView 에는 1 값이 대입됩니다.
1인칭 보기 모드라는 의미입니다.
toggleView 값이 1일 때 이 코드가 실행되면 어떻게 될까요?
toggleView ← 4 - toggleView ( 1 )
4 - 1 = 3이기 때문에 toggleView 에는 3 값이 대입됩니다.
3인칭 보기 모드라는 의미입니다
원래 대로라면 스크립트를 아래와 같이 작성하면 되나 단순 명료한걸 좋아하는 크레이의 성격이니
혼동되시면 아래 스크립트로 바꿔서 사용하셔도 됩니다 :)
if (Input.GetMouseButtonDown(2)) {
if (toggleView == 1) toggleView = 3;
else if (toggleView == 3) toggleView = 1;
}
그 다음으로는 3인칭 모드에서만 작동하는 마우스 휠 기능입니다.
마우스 휠 버튼을 위 아래로 스크롤하면 카메라가 플레이어로부터
가까워졌다 멀어졌다 하는 기능이 있었지요?
방금전에 마우스 중앙 버튼 클릭으로 토글되었던 toggleView 속성변수에는 현재 몇인칭 보기 모드로 작동할지 값이 들어있습니다.
그래서 이 변수값이 3일 때만 위 기능이 작동되어야 하기 때문에,
아래 if문의 조건이 참일 경우에만 안쪽 부분이 실행됩니다.
if (toggleView == 3) {
:
}
IF문 안쪽에서는 아래와 같은 코드가 실행이 되는데요.
distance -= Input.GetAxis("Mouse ScrollWheel") * wheelspeed;
if (distance < 1f) distance = 1f;
if (distance > 100.0f) distance = 100.0f;
distance 라는 값은 카메라가 플레이어로부터 떨어진 거리를 나타내는 속성변수이며,
아래와 같이 클래스 내에 정의되어 있습니다. 이 값으로 카메라로부터 얼만큼 떨어져 있는지를 정의하는 것이지요
public float distance = 1;
Input.GetAxis("Mouse ScrollWheel") 이란 마우스 이동과 유사한데요.
마우스 휠 버튼을 얼만큼 회전시켰는지 그 이동량을 나타냅니다.
역시 프레임마다 상대적인 이동량을 받아옵니다.
이 값을 distance 속성변수에 누적해주는데요.
wheelspeed 값을 곱해서 누적해줍니다.
distance -= Input.GetAxis("Mouse ScrollWheel") * wheelspeed;
wheelspeed 는 휠버튼에 곱해줄 배수를 정의한 속성 변수인데요.
private float wheelspeed = 10.0f;
그냥 마우스의 휠버튼 이동량을 더해주기만 하면 줌인, 줌아웃 속도가 너무 느리기 때문에 별도의 변수를 정의하여 곱해주도록 처리하고 있습니다.
이 값을 조정하면 마우스 휠버튼을 돌릴 때 줌인, 줌아웃 속도도 변경됩니다.
거리를 조정하고 나서, 그 거리가 너무 가깝거나 먼 경우 플레이어가 보이지 않을 우려가 있습니다.
그래서 바로 이어서 아래 코드가 실행이 되는데요.
if (distance < 1f) distance = 1f;
if (distance > 100.0f) distance = 100.0f;
거리가 1미터보다 작으면 1미터로 조정해주고,
100미터보다 크면 100미터로 조정해주는 부분입니다.
이제 다음으로 1인칭 보기 모드일 때 카메라 위치를 조정하는 기능이 실행이 됩니다.
여지껏 실행된 코드는 카메라가 바라보는 방향만 작동시켰지,
실제 카메라가 어디에 위치할지 이동하는 기능은 작동되지 않았습니다.
앞에서 결정된 속성에 따라 실제 카메라의 위치가 결정이 되는데요.
먼저 좀 쉬운 1인칭 모드부터 살펴보겠습니다.
toggleView 값이 1일 때 아래 코드가 실행이 됩니다.
if (toggleView == 1) {
// 카메라가 바라보는 앞방향은 Z 축입니다. 이동량에 따른 Z 축방향의 벡터를 구합니다.
Vector3 Eye = player.transform.position + Player_Height;
// 플레이어의 위치에서 카메라가 바라보는 방향에 벡터값을 적용한 상대 좌표를 차감합니다.
Vector3 reverseDistance = new Vector3(0.0f, 0.0f, 0.5f);
transform.position = Eye + transform.rotation * reverseDistance;
}
먼저 플레이어의 머리 위치를 받아옵니다.
Vector3 Eye = player.transform.position + Player_Height;
player 란 이와 같이 속성 변수가 정의되어 있는데요.
public GameObject player;
기본적으로는 아무 값도 대입되어 있지 않으나, 유니티에서 자모로봇 플레이어를 드래그해두었기 때문에 플레이가 시작되는 순간 Jammo_Player 오브젝트가 이 속성변수에 대입됩니다.
player.transform.position 은 자모로봇 플레이어의 3차원 좌표입니다.
발바닥 사이 정가운데 좌표이지요.
이 좌표에 Player_Height 값 ( 0, 1.5, 0 ) 을 더해주면 플레이어의 머리 위치가 됩니다.
이 값을 Eye 속성 변수에 대입해주는 것이지요.
Eye 라는 이름으로 벡터변수를 정의헸는데 지나고 보니
Head 라는 이름이 더 어울리지 않았나 생각되기도 합니다. 이런!
이제 다음으로는 카메라가 플레이어의 눈 앞으로 이동해야 합니다.
엄밀히 말하자면 자모로봇 플레이어의 눈 위치와는 관계가 없습니다.
자모로봇을 그냥 놔두고 마우스로 우클릭 드래그하면 자모로봇은 가만히 있지만 상대적인 위치가 결정되어야 합니다.
플레이어의 머리 좌표를 중심으로 하고, 카메라 방향에 따라 중심에서 약간 떨어진 지점을 그 위치로 결정해야 하는데요.
만일 플레이어 중심좌표를 카메라 위치로 한다면 카메라가 자모로봇의 몸 속에 위치하게 됩니다.
그래서 이와 같이 자모로봇의 몸 속까지 비출 수가 있기 때문이지요.
카메라가 어느 방향을 바라보든 간에 자모로봇이 카메라의 시야를 가려서는 안됩니다.
그렇기 때문에 1인칭 시점은 자모로봇의 중심점(머리)을 기준으로 카메라가 바라보는 방향만큼
거리를 떨어뜨려 배치하는 것이 좋은데요.
아래 코드가 바로 그것을 계산하는 스크립트입니다.
// 카메라가 바라보는 앞방향은 Z 축입니다. 이동량에 따른 Z 축방향의 벡터를 구합니다.
Vector3 reverseDistance = new Vector3(0.0f, 0.0f, 0.5f);
// 플레이어의 위치에서 카메라가 바라보는 방향에 벡터값을 적용한 상대 좌표를 차감합니다.
transform.position = Eye + transform.rotation * reverseDistance;
먼저 reverseDistance 변수를 정의하면서 벡터값 ( 0, 0, 0.5 ) 값을 대입해주고 난 다음에
카메라의 회전 방향 ( transform.rotation ) 에 reverseDistance 값을 곱한 값을
플레이어의 머리 중심위치 Eye 에 더해 줍니다.
여기서 transform.rotation 은 카메라의 회전방향입니다,
여기에 reverseDistance ( 0, 0, 0.5)값을 곱해주면 어떻게 될까요?
카메라가 바라보는 방향으로 0.5미터 상대거리를 구해줍니다.
이 값을 중심좌표 Eye에 더해 줌으로써 플레이어 캐릭터가 시야를 가리지 않는
플레이어 중심으로부터 0.5m 떨어진 정면 위치에 카메라를 배치하는 것입니다.
이 것이 1인칭 카메라의 원리입니다.
3인칭 카메라도 원리는 비슷한데요. 스크립트는 아래와 같습니다.
else if (toggleView == 3) {
Vector3 Eye = player.transform.position + transform.rotation * Player_Side + Player_Height;
// 카메라가 바라보는 앞방향은 Z 축입니다. 이동량에 따른 Z 축방향의 벡터를 구합니다.
Vector3 reverseDistance = new Vector3(0.0f, 0.0f, distance);
transform.position = Eye - transform.rotation * reverseDistance;
}
else if 는 앞에서의 if(toggle ==1) 이라는 조건이 성립되지 않을 때 한번 더 비교하는 비교문인데요.
toggleView 값이 3일 때 안쪽에 있는 스크립트가 실행됩니다.
먼저 카메라가 바라볼 대상 위치를 결정합니다.
Vector3 Eye = player.transform.position
+ transform.rotation * Player_Side
+ Player_Height;
플레이어 위치(player.transform.position)에,
카메라가 바라보는 방향(transform.rotation) 기준으로 Player_Side 값 ( -0.8, 0, 0 ) 을 곱해준 값을 더해주고
플레이어의 키높이(Player_height) 를 더해줍니다.
덧셈의 순서는 바꿔도 되니 우선
플레이어 위치에 높이값을 더한 좌표를 먼저 산출해봅시다.
플레이어 위치 + 키높이, 여기까지는 쉽습니다.
그 다음 복잡한 공식은 transform.rotation * Player_Side ( -0.8, 0, 0 ) 입니다. 이 값을 더해주려는 것인데요.
앞서 1인칭 시점에서는 아래 공식이 카메라가 바라보는 방향으로 0.5미터 앞으로 나아가는 상대방향이라고 했었지요?
이 떄는 Z값이 0.5라는 값이 입력되었습니다.
transform.rotation * ( 0, 0, 0.5 )
그렇다면 아래 공식은 무엇을 의미하는 것일까요?
transform.rotation * ( -0.8, 0, 0 )
그 것은 카메라가 바라보는 방향의 왼쪽으로 0.8미터의 상대거리를 구하라는 것입니다.
그리고 그 값을 바라볼 표적으로 삼으라는 것인데요.
이 값이 더해지지 않았다면 카메라는 이 지점을 바라보겠지만,
(-0.8, 0, 0 ) 값을 곱해주는 순간 바라볼 목표 지점이 카메라 기준 왼쪽으로 평행이동하는 것입니다.
그래서 캐릭터의 왼쪽을 바라보는 사이드 카메라가 되는 것이지요.
키높이 변수 Player_height 와 카메라 왼쪽 방향으로 수평이동할 Player_Side 변수를 따로 정의한 것은 바로 이러한 처리를 위한 거입니다.
카메라가 바라볼 지점은 결정되었지만 NPC 카메라처럼 LookAt 메소드를 사용해서는 안됩니다.
이는 NPC 카메라처럼 먼저 대상을 정하고 카메라 방향이 바뀌는 방식과 달리,
이미 목표점과 방향은 결정되었고 그 기준으로 카메라 위치를 조정해야 하기 때문인데요.
이제 남은 부분은 1인칭 시점과 유사합니다.
스크립트는 아래와 같습니다.
// 카메라가 바라보는 앞방향은 Z 축입니다. 이동량에 따른 Z 축방향의 벡터를 구합니다.
Vector3 reverseDistance = new Vector3(0.0f, 0.0f, distance);
transform.position = Eye - transform.rotation * reverseDistance;
먼저 reserseDistance 값을 구하는 부분이 상이합니다.
distance 속성변수는 카메라와의 거리를 나타내는 값입니다. 마우스 휠버튼을 돌리면 그 값이 변하지요.
아래 내용을 distance 값에 의해 Z축으로 이동할 상대 거리를 계산하기 위해 정의해준 값입니다.
Vector3 reverseDistance = new Vector3(0.0f, 0.0f, distance);
하지만 이 값을 1인칭 시점처럼 플레이어 위치에 더해준다면,
카메라는 플레이어의 눈 앞 저 멀리에 위치하게 될 겁니다.
그러니까 이 경우는 반대로 Eye(플레이어 머리) 위치에서 이 값을 빼줍니다.
transform.position = Eye - transform.rotation * reverseDistance;
그래야 카메라가 플레이어의 뒤에서 플레이어를 바라보게 됩니다.
여기까지 실행되고 나면 비로서 Update() 메소드의 작동이 끝납니다.
이렇게 하여 3인칭 시점 사이드 뷰 방식까지 모두 다루었습니다.
양이 좀 되긴 한데 2회로 나누기는 좀 모호해서 이번 게시글에 모든 설명을 담게 되었습니다.
필요하신 분에게 도움이 되셨을지 모르겠군요.
오늘도 여기까지 읽어주셔서 감사합니다~
내용이 유익하셨다면 하트 뿅뿅 눌러주시고 구독해주시면 감사하겠습니다 :)
'유니티3D' 카테고리의 다른 글
유니티3D의 UI Toolkit (1) - 기본세팅과 '배경 창' 꾸미기 (0) | 2023.09.20 |
---|---|
윈도우 + PHP 8 + unity ( 4편, 유니티와 웹소켓하라! ) (0) | 2023.09.16 |
유니티3D - 3인칭 무빙 카메라 - 사이드 뷰 #1 (2) | 2021.07.03 |
유니티3D - 대화창에서 NPC 를 바라보는 카메라 #2 (2) | 2021.06.27 |
유니티3D - 대화창에서 NPC 를 바라보는 카메라 #1 (8) | 2021.06.24 |