무민은귀여워

[유니티] 시야를 가리는 물체 판별 및 카메라 이동 본문

IT/Unity

[유니티] 시야를 가리는 물체 판별 및 카메라 이동

moomini 2022. 5. 19. 14:55
반응형

좌클릭 시 플레이어를 이동시키고, 카메라와 플레이어 사이 시야를 가리는 물체가 있으면 카메라를 물체 앞으로 옮겨준다.

1. Define

2. CameraController

3. InputManager

4. PlayerController

5. 실행

1. Define

public class Define
{
    public enum MouseEvent
    {
        Press,
        Click,
    }

    public enum CameraMode
    {
        QuarterView,
    }
}

2. CameraController

_delta : 카메라의 기준 위치

_player : 플레이어. 툴 내에서 사용할 캐릭터 오브젝트를 지정

LateUpdate() 

플레이어 이동 시 깜빡거리는 현상은, 플레이어 키입력에 따른 update()와 카메라 update() 중 어느것을 먼저 실행할지 정해지지 않아서이다.
LateUpdate()로 카메라의 업데이트를 나중에 실행하도록해서 깜빡임을 없앤다.

Raycast

레이캐스트로 플레이어로부터 카메라까지의 경로중 막히는 물체가 있는지 검사한다. 막히는 물체가 있다면, 시야를 가리는 물체이므로 카메라를 그 물체 앞으로 이동시킨다.

 

* transform.LookAt(_player.transform) : 카메라가 플레이어를 바라보도록 회전

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 = null;

    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);
            }
        }
    }

    public void SetQuarterView(Vector3 delta)
    {
        _mode = Define.CameraMode.QuarterView;
        _delta = delta;
    }
}

3. InputManager

InputManager에 마우스 Action 추가

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

4. PlayerController

_moveToDest : 클릭 시 이동가능여부

_destPos : 이동위치

 

MouseAction에 OnMouseClicked()을 등록한다

클릭 이동일 경우, Update()에서 플레이어 위치를 이동시킨다

OnMouseClicked()

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

이동

잘못된 예

Update() 내에서 이동거리를 아래처럼 _speed * Time.deltaTime 를 직접사용하면, 이동거리가 dir.magnitude보다 큰 경우가 생길 수 있어 캐릭터가 왔다갔다 거리는 문제가 생긴다.

// 수정필요 예시 - 이동완료 직전 캐릭터 왔다갔다
Vector3 dir = _destPos - transform.position;
if (dir.magnitude < 0.0001f)
{
    _moveToDest = false;
}
else
{
    transform.position += dir.normalized * _speed * Time.deltaTime;
    transform.LookAt(_destPos);
}

수정된 예

Mathf.Clamp()를 이용해서 이동거리를 dir.magnitude 범위 내로 조정한다.

*  Mathf.Clamp(value, min, max) : value가 최소값보다 작으면 최소값을, 최대값보다 크면 최대값을 반환

플레이어 방향은 Quaternion.Slerp() 와 Quaternion.LookRotation()으로 자연스럽게 바뀌게 한다

        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);

                transform.position += dir.normalized * moveDist;

                transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 20 * Time.deltaTime);
            }
        }

 

5. 실행

재생 전 시야를 막는 물체가 있는 경우

재생 후 시야를 막는 물체 앞으로 카메라를 이동시킨 경우

반응형

'IT > Unity' 카테고리의 다른 글

[유니티] 오브젝트 풀링  (0) 2022.05.24
[유니티] 충돌 Collision / Trigger / Raycast / LayerMask  (0) 2022.05.17
[유니티] ResourceManager  (0) 2022.05.17
[유니티] InputManager  (0) 2022.05.17
[유니티] 회전 rotation  (0) 2022.05.16
Comments