Chapter 5-2. 카메라 : 클릭한 곳으로 플레이어와 카메라가 따라가게 하기
카테고리: Unity Lesson 2
태그: Unity Game Engine
인프런에 있는 Rookiss님의 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click
Chapter 5. 카메라
🚖 롤이나 스타처럼 클릭한 곳으로 플레이어와 카메라가 따라가게 하기
바닥에 마우스를 클릭하면 클릭한 곳으로 플레이어가 자동으로, 스스로 이동하게 한다. 롤이나 스타크래프트처럼.
📜Define.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Define
{
public enum MouseEvent
{
Press,
Click,
}
public enum CameraMode
{
QuaterView,
}
}
- 📜Define 클래스에 MouseEvent 라는 enum 을 정의하였다.
Press
👉 마우스를 클릭하는 동안 사용할Click
👉 마우스를 클릭에서 손 뗐을 때 사용할
📜InputManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class InputManager
{
public Action KeyAction = null;
public Action<Define.MouseEvent> MouseAction = null;
bool _pressed = false;
public void OnUpdate()
{
/* Keyboard 입력 */
if (Input.anyKey && KeyAction != null)
KeyAction.Invoke();
/* Mouse 입력 */
if (MouseAction != null)
{
if (Input.GetMouseButton(0)) // 누르는 중
{
MouseAction.Invoke(Define.MouseEvent.Press); // 마우스를 누르고 있을 땐 Define.MouseEvent.Press 을 인수로 넘겨 액션에 등록된 함수들 실행
_pressed = true; // 누르는 중이었음을 표시하는
}
else // 누르는 중 아님
{
if (_pressed) // 누르는 중이 아닌데 눌렀었다면 뗀 것.
MouseAction.Invoke(Define.MouseEvent.Click); // 마우스를 누르다 뗏을 땐 Define.MouseEvent.Click 을 인수로 넘겨 액션에 등록된 함수들 실행
_pressed = false; // 초기화
}
}
}
}
- Define.MouseEvent 타입의 인수만 받는 함수들을 등록할 수 있는
MouseAction
액션 추가. - 마우스 입력 처리
- 마우스를 누르고 있을 땐 액션에 등록된 함수들에게 Define.MouseEvent.Press 을 인수로 넘겨 실행
- 이번 포스트에선 아직 사용 X
- 마우스를 누르다 뗏을 땐 액션에 등록된 함수들에게 Define.MouseEvent.Click 을 인수로 넘겨 실행
- 클릭한 곳으로 자동 이동하게 할 것이라서 이번 포스트에선 이 것만 사용할 것
- 마우스를 누르고 있을 땐 액션에 등록된 함수들에게 Define.MouseEvent.Press 을 인수로 넘겨 실행
- cf) 만약 마우스 드래그하는 상태를 캐치해내고 싶다면 0.2초 이상 누르고 있을 때는 드래그 상태로 정의한다던가 등등 이런 조건을 줄 수 있을 것이다.
📜PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField]
float _speed = 10.0f;
bool _moveToDest = false; // 마우스 클릭한 곳으로 롤처럼 자동 이동 되야 하는 상황인지
Vector3 _destPos; // 목적지 위치
void Start()
{
Managers.Input.KeyAction -= OnKeyboard;
Managers.Input.KeyAction += OnKeyboard;
Managers.Input.MouseAction -= OnMouseClicked;
Managers.Input.MouseAction += OnMouseClicked;
}
void Update()
{
if (_moveToDest)
{
Vector3 dir = _destPos - transform.position;
if (dir.magnitude < 0.0001f) // 도착함. 더 이상 이동 X
{
_moveToDest = false;
}
else // 아직 도착 안함
{
float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);
transform.position = transform.position + dir.normalized * moveDist;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 10 * Time.deltaTime);
transform.LookAt(_destPos);
}
}
}
void OnKeyboard()
{
if (Input.GetKey(KeyCode.W))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.forward), 0.2f);
transform.position += Vector3.forward * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.S))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.back), 0.2f);
transform.position += Vector3.back * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.D))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), 0.2f);
transform.position += Vector3.right * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.A))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.left), 0.2f);
transform.position += Vector3.left * Time.deltaTime * _speed;
}
_moveToDest = false; // 키보드 입력은 플레이어 의지로 움직이는 것이니 자동 이동 멈춤
}
void OnMouseClicked(Define.MouseEvent evt)
{
if (evt != Define.MouseEvent.Click)
return;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(Camera.main.transform.position, ray.direction * 100.0f, Color.red, 1.0f);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100.0f, LayerMask.GetMask("Wall")))
{
_destPos = hit.point;
_moveToDest = true;
}
}
}
_moveToDest
👉 마우스 클릭 이벤트로 인하여 플레이어가 그 곳으로 이동 되야 하는 상황인지 True or False._destPos
👉 목적지 위치. 바닥에 마우스 클릭한 곳 월드 좌표가 목적지가 될 것이다.- Start()
- 📜Manager.cs 싱글톤에 접근하여 📜InputManager.cs 의
MouseAction
에 📜PlayerController.cs의 OnMouseClicked 함수를 등록한다.
- 📜Manager.cs 싱글톤에 접근하여 📜InputManager.cs 의
- OnMouseClicked
Define.MouseEvent
타입의 인수를evt
에 받는다.- 일단 실험삼아
evt
가 Define.MouseEvent.Click 상태일 때만 처리될 수 있도록 Define.MouseEvent.Click 가 아니면 return 시켰다.- 마우스 클릭하고 뗐을 때
- 카메라 위치로부터, 그 클릭한 화면상의 위치의 월드 좌표로 향하는
Ray
를 생성한 후 - “Wall” Layer 를 가진 대상들에게만 이
Ray
를 100 길이로 쏜다. 그리고 충돌 정보를hit
에 저장.- “Wall” Layer 를 가진 대상에 충돌했다면
_destPos
을 충돌한 위치로 업데이트. 즉, 클릭한 곳으로 업데이트._moveToDest
True로 업데이트.
- “Wall” Layer 를 가진 대상에 충돌했다면
- 카메라 위치로부터, 그 클릭한 화면상의 위치의 월드 좌표로 향하는
- 마우스 클릭하고 뗐을 때
- OnKeyboard()
- 마우스 입력으로 인해 자동으로 이동 중이다가 키보드 입력이 들어오면 키보드 입력 처리를 다 하고난 후,
_destPos
로 더 이상 이동하지 않게_moveToDest
를 False로 업데이트.- 롤 하다보면 마우스 클릭한 곳으로 이동 하다가도 플레이어가 WASD 이동키 입력하면 더 이상 클릭한 곳으로 이동하지 않듯이 그 작업을 해준 것.
- 키보드 입력 처리를 다 하고난 후엔 자동으로
_moveToDest
를 False로 업데이트.
- 마우스 입력으로 인해 자동으로 이동 중이다가 키보드 입력이 들어오면 키보드 입력 처리를 다 하고난 후,
- Update()
- 매 프레임 실행
- 이게 다 실행되고 나서 📜CameraController.cs의 LateUpdate() 에서 플레이어를 따라가도록 카메라 위치가 업데이트 된다.
- 클릭한 곳으로 플레이어가 자동으로 이동 되야 하는 상황이라면 if (_moveToDest)
dir
업데이트 👉 플레이어로부터 목적지까지의 방향- 플레이어가 그 곳으로 도착해갈 수록 점점
dir
벡터의 크기가 작아짐
- 플레이어가 그 곳으로 도착해갈 수록 점점
- 도착했다고 판단하고 더 이상 이동 X
_moveToDest
를 False로 업데이트. if (dir.magnitude < 0.0001f) - 아직 도착하지 않았으니 플레이어의 위치와 회전값을 클릭한 위치로 이동해야 함
- 위치 업뎃
- 한 프레임 마다
dir
방향으로_speed * Time.deltaTime
크기만큼 이동하되,_speed * Time.deltaTime
가dir.magnitude
를 넘지 않게 한다. 즉, 이동할만큼의 스칼라 크기가 목적지 크기를 넘지 않게.- 이 과정 없이 그냥 아래와 같이 해주면 목적지를 지나쳐버릴 수가 있다. 목적지 지나쳤다가
dir
새롭게 업뎃 한것에 의해 다시 목적지 위치로 돌아오고 이런 과정이 있을 수 있기 때문에 플레이어가 버벅대며 움직이는 것처럼 된다.transform.position = transform.position + dir.normalized * _speed * Time.deltaTime;
- 이 과정 없이 그냥 아래와 같이 해주면 목적지를 지나쳐버릴 수가 있다. 목적지 지나쳤다가
- 한 프레임 마다
- 회전 값 업뎃
dir
방향을 바라보도록 먼저 부드럽게 회전을 해준다.destPos
위치를 바라보도록 회전한다.transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 10 * Time.deltaTime); transform.LookAt(_destPos);
- 위치 업뎃
- 매 프레임 실행
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기