호빵의 IT 개발소

[MMO RPG] RayCast를 이용한 마우스 이동 및 벽에서 카메라 줌인 본문

C#, 유니티/MMO RPG 만들기 기초(클라이언트)

[MMO RPG] RayCast를 이용한 마우스 이동 및 벽에서 카메라 줌인

호빵Stack 2022. 1. 25. 00:19

지금 까지 사용했던 문법을 응용하여 쉽게 구현할 수 있습니다.

 

InputManager.cs

-InputManager를 통해 마우스가 눌렸을 때 이벤트를 받습니다.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InputManager
{
    public Action<Define.MouseEvent> MouseAction = null;

    bool _Pressed = false; //마우스 누른 상태는 기본값 false

    public void OnUpdate()
    {
        if (MouseAction != null) //press와 click 
        {
            if (Input.GetMouseButton(0)) //마우스 좌클릭
            {
                MouseAction.Invoke(Define.MouseEvent.Press);
                _Pressed = true;
            }
            else 
            {
                if (_Pressed)
                    MouseAction.Invoke(Define.MouseEvent.Click);
                _Pressed = false;

            }
        }
    }
}

 

Define.cs

-마우스 이벤트를 추가하여 관리합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Define
{
    public enum CameraMode //카메라 모드
    {
        QuarterView,
    }

    public enum MouseEvent //마우스 이벤트
    {
        Press, //누르고있는 상태
        Click, //한번 짧게 누른 상태
    }
}

 

CameraController.cs

-카메라 이벤트에서는 플레이어가 벽(Layer : "Wall")에 가려졌을 때 확대하여 플레이어가 보일 수 있도록 코드를 작성하였습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    [SerializeField]
    Define.CameraMode _mode = Define.CameraMode.QuarterView; //Define에서 쿼터뷰 정의를 가져옴

    [SerializeField]
    Vector3 _delta = new Vector3(0.0f, 7.0f, -6.0f); //_delta 방향 벡터

    [SerializeField]
    GameObject _player; //카메라가 따라갈 플레이어를 유니티에서 드로그드롭으로 설정

    void LateUpdate() //플레이어의 움직임 다음으로 update문이 실행되도록 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; //벽에서 0.8f 띄운 거리
                transform.position = _player.transform.position + _delta.normalized * dist;
            }
            else
            {
                transform.position = _player.transform.position + _delta; //플레이어의 좌표를 가져와서 우리가 설정한 _delta라는 방향 벡터를 더해준다
                transform.LookAt(_player.transform); //카메라가 무조건 플레이어의 좌표를 주시한다.
            }
        }
    }

}

 

PlayerController.cs

-여기서는 Raycast를 이용하여 "Wall" layer인 경우 마우스클릭하였을 때 그 방향으로 이동하도록 설정하였습니다.

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.MouseAction -= OnMouseClicked; //다른 곳에서 구독신청 되어있으면 끊어주고
        Managers.Input.MouseAction += OnMouseClicked; //InputManager에게 마우스가 눌리면 이 함수를 실행해주세요
    }

    void Update()
    {
        if (_moveToDest)
        {
            Vector3 dir = _destPos - transform.position; //destPos에서 Player위치를 빼주면 방향벡터가 나온다.
            if (dir.magnitude < 0.0001f) //목적지에 도착했다면 (방향벡터에 오차범위 때문에 아주 작은 값을 적어줍니다.)
            {
                _moveToDest = false; //움직임 멈춤
            }
            else //도착하지 않았으니 조금 더 움직여야 한다.
            {
                float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude); //_speed * Time.deltaTime는 min(0)과 max(dir.magnitude) 사이의 값을 보장해야 한다.(캐릭터 떨림 현상)

                transform.position = transform.position + dir.normalized * moveDist; //dir이 지금 방향벡터이지만 크기가 1로 바꾼 상태가 아니므로 normalized를 통해 크기가 1짜리인 하지만 방향은 똑같은 방향벡터가 나옴 그래서 moveDist 만큼을 방향벡터로 이동

                transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 10 * Time.deltaTime); //slerp를 통해 플레이어 좌표, 내가 바라보는 방향, 적당한 회전 수치 순서로 부드럽게 회전 구현
                transform.LookAt(_destPos); //플레이어가 _destPos 방향을 보면서 이동

            }
        }
    }

    void OnMouseClicked(Define.MouseEvent evt)
    {
        if (evt != Define.MouseEvent.Click)
            return;

        //RayCasting
        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;
        }
    }
}

 

벽 뒤에 플레이어가 있을 때

 

벽이 없는 곳으로 이동했을 때

 

※벽과 바닥은 Layer가 꼭 "Wall"로 되어있어야 합니다!

 

 

---------------------------------------------------------------------------------------------------------------------------

참고 : [인프런] Rookiss님의 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티  엔진

'C#, 유니티 > MMO RPG 만들기 기초(클라이언트)' 카테고리의 다른 글

[MMO RPG] 간단한 점수 UI 만들기  (0) 2022.01.27
[MMO RPG] Animator  (0) 2022.01.27
[MMO RPG] Camera 설정(Quater View)  (0) 2022.01.24
[MMO RPG] Raycasting  (0) 2022.01.23
[MMO RPG] Trigger  (0) 2022.01.21
Comments