using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Define
{
public enum MouseEvent
{
Press,
Click,
}
public enum CameraMode
{
QuaterView,
}
}
그리고 이전에 만들어줬던 InputManager.cs를 수정해준다.
using System;
using UnityEngine;
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;
}
}
}
}
using Unity.Mathematics;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UIElements;
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;
}
float _yangle = 0.0f;
void Update()
{
if(_moveToDest)
{
Vector3 dir = _destPos - transform.position;
if(dir.magnitude < 0.0001f)
{
_moveToDest = false;
}
else
{
float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);
// 1번 방법
// if (moveDist > = dir.magnitude)
// moveDist = dir.magnitude
transform.position += dir.normalized * moveDist;
transform.LookAt(_destPos);
}
}
}
void OnKeyboard()
{
if (Input.GetKey(KeyCode.W))
{
// transform.rotation = Quaternion.LookRotation(Vector3.forward); // 월드 기준
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.LookRotation(Vector3.back);
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.back), 0.2f);
transform.position += Vector3.back * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.A))
{
// transform.rotation = Quaternion.LookRotation(Vector3.left);
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.left), 0.2f);
transform.position += Vector3.left * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.D))
{
// transform.rotation = Quaternion.LookRotation(Vector3.right);
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), 0.2f);
transform.position += Vector3.right * Time.deltaTime * _speed;
}
_moveToDest = false;
}
void OnMouseClicked(Define.MouseEvent evt)
{
if (evt != Define.MouseEvent.Click)
return;
Debug.Log("onmouseclicked");
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"))) //768;을 사용해서 2의 8승과 2의 9승을 더한 숫자를 입력해도 된ㅏ.
{
_destPos = hit.point;
_moveToDest = true;
}
}
}
Start()
OnMouseClicked()
Define.MouseEvent 타입의 인수를 evt로 받는다.
evt가 Define.MouseEvent.Click이 아니라면 함수를 종료(return).
마우스 클릭 위치에 따라 Ray를 생성하고 충돌 검사 수행
클릭한 위치에 Wall 레이어가 있다면
Update()
클릭한 위치로 이동해야 하는지 확인
목적지가지의 방향 벡트(dir) 계산
Vector3 dir = _destPos - transform.position;
목적지 도착 여부 확인
if (dir.magnitude < 0.0001f)아직 이동해야 한다면 이동 실행
_speed * Time.deltaTime 값을 현재 남은 거리(dir.magnitude)에서 제한
한 번의 Update()에서 너무 많이 이동하지 않도록 조절
float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);
이 과정없이 아래와 같인 코드를 작성한다면 목적지를 지나쳤다가 dir가 새롭게 업데이트 되어 다시 돌아오는 과정이 존재할 수 있기에 플레이어가 도착했는데도 버벅이며 움직이는 것처럼 된다.
그렇기에 위와 같이 moveDist라는 float변수를 만들어 주어 그것만큼 이동하게 하는 것이다.
transform.position = transform.position + dir.normalized * _speed * Time.deltaTime;
계산된 거리만큼 이동
transform.position += dir.normalized * moveDist;
이동 방향으로 케릭터 회전
transform.LookAt(_destPos);
1. 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;
}
}
}
}
2. PlayerController에서 MouseAction을 구독
PlayerController에서 InputManager가 감지한 마우스 이벤트를 받으려면 MouseAction에 함수를 등록해야함
void Start()
{
Managers.input.MouseAction -= OnMouseClicked; // 기존에 등록된 이벤트 제거 (중복 방지)
Managers.input.MouseAction += OnMouseClicked; // 새로운 이벤트 추가
}
3. InputManager에서 마우스 입력 발생 -> PlayerController의 OnMouseClicked()실행
마우스를 클릭하면 다음과 같이 동작
void OnMouseClicked(Define.MouseEvent evt)
{
if (evt != Define.MouseEvent.Click)
return;
Debug.Log("onmouseclicked");
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;
}
}
정리하자면
(1) InputManager가 마우스 클릭 감지
(2) MouseAction을 구독한 PlayerController의 OnMouseClicked() 실행
(3) 클릭한 위치를 Raycast로 찾고 _destPos에 저장
(4) Update()에서 _moveToDest가 true이므로 캐릭터가 클릭한 곳으로 이동
float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);
이 코드에서 사용된 Clamp()함수는
public static int Clamp (int value, int min, int max);
와 같이 생겼으며, 이것이 의미하는 것이 value값이 min보다 작으면 min으로 설정하고 max보다 크면 max로 설정한다는 것이다.