카메라

·2023년 4월 13일
0

Unity

목록 보기
12/22

📌카메라


Culling Mask

Layer 지정한거 카메라에 보이게 안보이게 가능

Target Texture

CCTV기능 컴퓨터에다가 다른쪽에서 찍은 스크린을 보고 싶다고 할 때

쿼터뷰 만들기

카메라를 유니티짱에 넣으면 이제부터 카메라는 유니티짱으로부터의 거리로 x, y, z가 결정된다

문제점

rotation할 때 카메라도 같이 돌아서 어지럽다

해결방법

유니티짱 회전에 상관없이 x, y축 떨어진채로 위치해야한다.
그렇게 하려면 밖으로 꺼내서 코드로 카메라를 조정해야한다.

//쿼터뷰가 아닌 다른것도 추가 될수 있기에 이렇게 추가
[SerializeField] Define.CameraMode _mode = Define.CameraMode.QuaterView;
//플레이어 기준으로 얼마나 떨어져있나
[SerializeField] Vector3 _delta;
//오브젝트 드래그앤 드롭방식으로 받아오기
[SerializeField] GameObject _player;

void Update()
    {
        transform.position = _player.transform.position + _delta;
    }

LookAt

 //카메라가 플레이어를 주시하도록
transform.LookAt(_player.transform);

Update에 넣어주면 각도를 알아서 설정해준다.
현재 transform.LookAt은 Camera이고 _player.transform 방향으로 회전하게 한다.
기존 45도 설정해놨지만 이렇게 하면 알아서 각도를 조절해줌(50도정도나옴)

문제점

하지만 이렇게만 돌리면 위와 같이 움직일때마다 뚝뚝 끊기게 된다
Player이동과 카메라 움직임이 각 update에 구현되어 있는데 누가먼저 실행될지 몰라서 뚝뚝 끊긴다

해결방법(LataUpdate)

플레이어의 이동후에 카메라를 움직이게하면된다
이는 카메라 update부분에 LataUpdate를 붙여줘서 플레이어의 움직임 이후에 카메라가 움직이게 하면 문제가 해결된다. 이는 이벤트 함수의 실행순서를 보면 어떻게 작동하는지 알 수 있다

코드

 void LateUpdate()
    {
        if(_mode == Define.CameraMode.QuaterView)
        {
            transform.position = _player.transform.position + _delta;
            //카메라가 플레이어를 주시하도록
            transform.LookAt(_player.transform);
        }
    }

    //혹시라도 나중에 쿼터뷰를 코드상 세팅하기위해서 만들어둠
    public void SetQuaterView(Vector3 delta)
    {
        _mode = Define.CameraMode.QuaterView;
        _delta = delta;
    }

📌땅 찍으면 그곳으로 이동하게 만들기


전체적인 설명

이전에 raycasting에서 만든 코드를
마우스 입력 -> InputManager.cs
enum 저장용도 -> Define.cs
플레이어 이동 -> PlayerController.cs
이렇게 3군데 나눠서 코딩한다

Define 부분에는 MouseEvent가 Press, Click인지 enum으로 구분하고
Input Manager 부분에는 마우스 클릭이 들어오면 딜리게이트를 사용해서
PlayerController의 raycasting을 활용해서 땅 찍으면 가게 하는 함수를 실행시켜서 목표로 이동시킨다

InputManager부분

 if (Input.anyKey == false)
            return;

if (KeyAction != null)
            KeyAction.Invoke(); //연결된 함수를 실행해라

이렇게 되어 있는 update부분을

 if (Input.anyKey && KeyAction != null)
            KeyAction.Invoke(); //연결된 함수를 실행해라

마우스에 영향 주기 싫기에 이렇게 바꿈
마우스 클릭하고 떼는거 모든게 상태변화라서 상황에 따라서 제일 처음 코드는 키가 씹히는 일도 있다

GetMouseButton

Input.GetMouseButton(0 or 1)

0이 왼쪽이고 1이 오른쪽 2가 중간
마우스버튼을 누르는 동안 실행된다

GetMouseButtonDown

Input.GetMouseButtonDown
마우스 누른순간 이벤트가 실행된다.

InputManager 코드

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))
            {
                //눌렀으면 true;
                MouseAction.Invoke(Define.MouseEvent.Press);
                _pressed = true;
            }
            else
            {
                //누른상태에서 마우스를 떼면 클릭했다고 인정
                if(_pressed)
                {
                    MouseAction.Invoke(Define.MouseEvent.Click);
                }
                _pressed = false;
            }
        }
    }
}

PlayerController부분

마우스 클릭이 들어온다면

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.yellow, 1.0f);

        LayerMask mask = LayerMask.GetMask("Wall");

        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 100.0f, mask))
        {
            _destPos = hit.point;
            _moveToDest = true;
            //Debug.Log($"Raycast Camera @ {hit.collider.name}");
        }
    }

딜리게이트로 연결한 위의 함수가 실행되고 _moveToDest가 True가 되면서
Update문에 있는 캐릭터에서 raycasting으로 찍은 바닥의 좌표까지 이동하는 코드가 실행된다.

void Update()
    {
        if(_moveToDest)
        {
            //방향벡터 구하기(정확히 0이 나오지 않는다)
            Vector3 dir = _destPos - transform.position;
            //아주 작은값을 사용해서 오차
            //도착했다
            if(dir.magnitude < 0.0001f)
            {
                _moveToDest = false;
            }
            //좀더 이동해야한다
            else
            { 
                transform.position += dir.normalized * _speed * Time.deltaTime;
                //player가 가는 방향을 바라보게
                transform.LookAt(_destPos);
            }
        }
    }

이는 유니티짱의 위치와 raycasting으로 찍은 바닥의 위치를 이용하여 방향벡터를 구한다
두 값을 빼면 완전히 0은 될수는 없으므로 아주 작은 값을 이용해서
거의 0이 되기 전까지 유니티짱을 그 방향을 바라보게 이동 시킨다.

문제점

근데 클릭하면 위 화면처럼 그 부분에서 부들부들거린다
그 이유는 목적지보다 움직이는 값이 더 크면 원하는 목적지 보다 더 가서 왔다 갔다 하는 것이다.

문제해결(Clamp)

//2.부들거리는거 해결(실제로 이동할 거리를 구함)
//value, min, max : value가 min보다 작거나 max보다 크면 min, max로 덮어써진다
float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);

transform.position += dir.normalized * moveDist;               

slerp

이제 여기서 회전 부분을 부드럽게 만들어준다

 //3.부드럽게 이동시키기(현재위치, 마지막위치, 적당한값) 
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 10 * Time.deltaTime);

📌카메라가 벽으로 막혔을 경우 카메라 앞으로 이동


플레이어 기준으로 카메라한테 좌표를 쏴서 collision이 있다면 카메라를 이동시킨다.
카메라 관련이므로 CameraController부분에 코드를 구현해준다.

 void LateUpdate()
    {
        if(_mode == Define.CameraMode.QuaterView)
        {
            //4.카메라가 벽으로 막혔을 경우 카메라 앞으로 이동
            RaycastHit hit;
            if (Physics.Raycast(_player.transform.position, _delta, out hit, _delta.magnitude, LayerMask.GetMask("Wall")))
            {
                //(충돌한 좌표 - 플레이어의 좌표).magnitude -> 방향벡터의 크기 구한다음에 살짝 앞으로 오게 0.8곱해줌
                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);
            }
        }
    }

참고자료


Part3: 유니티 엔진
섹션 5.Camera(카메라)

LookAt
GetMouseButton
마우스버튼차이

profile
개인공부저장용(하루의 기록)

0개의 댓글