Unity 게임 만들기 프로젝트 - Camera, Click 이동 구현
- -
1. Camera
보통 RPG 게임에서 Player Object 가 이동하면 Camera 는 그에 맞춰 계속 이동해야 한다.
먼저 enum 을 작성해서 가독성을 높일 수 있도록 준비한다.
# Defins.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Define
{
public enum MouseEvent
{
Press,
Click,
}
public enum CameraMode
{
QuarterView,
}
}
CameraMode 와 Click 이동을 구현하기 위한 MouseEvent 를 작성한다. 이후에 CameraController 를 작성해준다.
# CameraController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
[SerializeField]
Define.CameraMode _mode = Define.CameraMode.QuarterView;
[SerializeField]
Vector3 _delta = new Vector3(0.0f, 6.0f, -5.0f);
[SerializeField]
GameObject _player;
void Start()
{
}
void LateUpdate()
{
if (_mode == Define.CameraMode.QuarterView)
{
RaycastHit hit;
if(Physics.Raycast(_player.transform.position, _delta, out hit, _delta.magnitude, LayerMask.GetMask("Wall")))
{
float dist = (hit.point - _player.transform.position).magnitude * 0.8f;
transform.position = _player.transform.position + _delta.normalized * dist;
}
else
{
transform.position = _player.transform.position + _delta;
transform.LookAt(_player.transform); // _player 를 주시하도록 설정 (각도 대신)
// 유저의 위치가 먼저 변할지 카메라가 먼저 변할지 랜덤이라서 유저 먼저 이동하도록 지정 -> LateUpdate
}
}
}
public void SetQuaterView(Vector3 delta)
{
_mode = Define.CameraMode.QuarterView;
_delta = delta;
}
}
해당 스크립트는 SerializeField 로 _mode, delta, GameObject 를 받는다. 모드를 설정하고 카메라가 위치할 각도, 그리고 주시할 게임 오브젝트를 지정해준다.
LateUpdate 를 사용하는 이유는 Unity 엔진에서 Player Object가 이동한 후 카메라도 함께 이동하는데, 어떤게 먼저 이동하도록 처리할지는 항상 랜덤이기 때문에 Player Object 가 먼저 이동한 후 Camera 가 이동하여 끊김을 방지하고자 LastUpdate 를 사용한다.
RaycastHit hit;
if(Physics.Raycast(_player.transform.position, _delta, out hit, _delta.magnitude, LayerMask.GetMask("Wall")))
{
float dist = (hit.point - _player.transform.position).magnitude * 0.8f;
transform.position = _player.transform.position + _delta.normalized * dist;
}
만약 Camera 와 Player Object 사이에 Wall Layer 로 지정한 Object 가 있다면 Raycast 를 통해 만난 Wall Object 지점의 Point 를 계산한 후 해당 Point 와 Player Object의 magnitude 를 계산한다.
이후 해당 Wall Layer 보다 카메라가 앞쪽에 위치해야 Player Object 를 볼 수 있기 때문에 0.8 정도의 값을 곱해서 거리를 지정한다.
그리고 카메라의 위치를 Player Object 의 포지션 * 앞에서 구한 거리만큼 연산하여 벽의 앞 쪽으로 카메라를 이동 시킨다.
transform.position = _player.transform.position + _delta;
transform.LookAt(_player.transform); // _player 를 주시하도록 설정 (각도 대신)
그 외에 카메라와 Player Object 의 사이에 Wall Layer 가 없다면 Player Object 의 위치에 _delta 각도만큼 이동하고, 해당 카메라는 Player Object를 주시하도록 LookAt 을 사용해준다.
2. 클릭 이동 구현
# InputManager.cs
public class InputManager
{
public Action KeyAction = null;
public Action<Define.MouseEvent> MouseAction = null;
bool _pressed = false;
public void OnUpdate()
{
if (Input.anyKey && KeyAction != null)
KeyAction.Invoke();
if (MouseAction != null)
{
if (Input.GetMouseButton(0))
{
MouseAction.Invoke(Define.MouseEvent.Press);
_pressed = true;
}
else
{
if (_pressed)
MouseAction.Invoke(Define.MouseEvent.Click);
_pressed = false;
}
}
}
}
기존 InputManager 에서 마우스 클릭 이벤트를 받을 수 있도록 수정한다.
MouseAction 을 통해 MouseEvent 를 받을 수 있돌고 선언해주고, 좌클릭 이벤트가 발생했다면 _pressed 를 True, Press 발생,
이후 마우스를 띤다면 Click 이벤트를 발생시키고 _pressed 를 false 로 선언해준다.
InputManager 를 통해 마우스 이벤트를 감지하고 처리할 수 있게 되었다면 PlayerController 에서 클릭 이벤트를 통해 동작을 구현할 수 있게 되었다.
# PlayerController.cs
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;
}
}
먼저 OnMouseClicked 함수를 작성한다. MouseEvent 라는 값을 매개변수로 받고, evt가 만약 Click 이 아니라면 종료,
Click 이 되었다면 Ray 를 통해 클릭 된 지점의 좌표를 계산한다.
그리고 클릭된 좌표에 존재하는 Wall Layer 와 만나는 지점의 좌표를 계산한다. 이를 _destPos 라는 목적지 좌표로 계산하고 _moveToDest 값을 통해 이동 중이라는 flag 값을 지정해주면 된다.
void Start()
{
Managers.Input.KeyAction -= OnKeyboard;
Managers.Input.KeyAction += OnKeyboard;
Managers.Input.MouseAction -= OnMouseClicked;
Managers.Input.MouseAction += OnMouseClicked;
}
그리고 Start 에서 동일하게 MouseAction 을 구독해주면 된다.
void Update()
{
if (_moveToDest)
{
Vector3 dir = _destPos - transform.position;
if (dir.magnitude < 0.00001f)
{
_moveToDest = false;
}
else
{
float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);
transform.position += dir.normalized * moveDist;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 20 * Time.deltaTime);
transform.LookAt(_destPos);
}
}
}
그리고 Update 문에서 _moveToDest 값을 통해 이동 중인지 체크하여 이동 중인 상태라면 목적지의 좌표와 PlayerObject 의 좌표 값의 차이를 구하여 벡터 값을 구한다.
벡터 값이 아주 미세한 값으로 작아지면 이동을 중지하고 _moveToDest 를 false 로 하여 이동을 종료 한다.
아직 값이 큰 차이를 지닌다면 도착하지 못한 것이므로 이동을 유지한다.
_speed * Time.deltaTime 의 값으로 이동할 텐데, 실제 도착할 거리보다 크다면 목적지를 지나칠 수 있기 때문에 Clamp 를 통해 목적지 까지의 거리를 넘지 않도록 지정해준다.
이동 거리와 방향을 곱하여 Player Object Postion 을 계속해서 더해주면서 이동하게 된다.
마찬가지로 Player Object 가 이동하면서 회전할텐데, Slerp , LookAt 을 통해 부드러운 회전을 하도록 하면 이동 구현이 완료 된다.
'⇥ 2D Game > Unity' 카테고리의 다른 글
Unity 게임 만들기 프로젝트 - SceneManager (0) | 2024.06.10 |
---|---|
Unity 게임 만들기 프로젝트 - UI Manager, 자동화 (2) | 2024.06.10 |
Unity 게임 만들기 프로젝트 - RayCasting (0) | 2024.06.07 |
Unity 게임 만들기 프로젝트 - 충돌 (Collision) (0) | 2024.06.06 |
Unity 게임 만들기 프로젝트 - Resource Manager (0) | 2024.06.05 |
소중한 공감 감사합니다