포스트

섹션5. Camera

위 글은 인프런에 있는 Rookiss님의 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진 강의를 듣고 남긴 필기입니다.

쿼터뷰 형태의 인게임 카메라를 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class CameraController : MonoBehaviour
{
    [SerializeField]
    public Define.CameraMode _mode = Define.CameraMode.QuarterView;
    [SerializeField]
    public Vector3 _delta = new Vector3(0,6.0f,-5.0f);
    [SerializeField]
    private GameObject _player = null;
    
    // Update에서하면 화면에 덜덜거리는 현상이 발생_왜?
    // => 이동을 담당하는 Managers.Update()안의 _input.OnUpdate();보다 먼저할지 뒤에할지 미정이기 때문에, 순서가 매번 일정하지 않아서.
    private void LateUpdate()      
    {
        if (_mode == Define.CameraMode.QuarterView)
        {
            if (Physics.Raycast(_player.transform.position, _delta, out var 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.position);
            }
        }
    }

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

InputManager도 수정. 마우스 클릭을 통한 이동을 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class InputManager
{
    public Action KeyAction = null;
    public Action<Define.MouseEvent> MouseAction = null;

    private bool _pressed = false;
    
    public void OnUpdate()
    {
        // 어떠한 입력이라도 있고, KeyAction이 비어있지 않다면,
        if (Input.anyKey && KeyAction != null)
            KeyAction.Invoke();

        if (MouseAction != null)
        {
            if (Input.GetMouseButton(0))                            // 마우스가 눌린다면,
            {
                MouseAction.Invoke(Define.MouseEvent.Press);       // (1)일단 Press에 해당하는 이벤트를 Invoke
                _pressed = true;                                   // (2)눌렸다가,
            }
            else
            {
                if(_pressed) MouseAction.Invoke(Define.MouseEvent.Click);   // 떨어질 때, Click 이벤트를 Invoke
                _pressed = false;
            }
        }
    }
}

덩달아 PlayerController도 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float _speed = 10.0f;

    private bool _moveToDest = false;
    private Vector3 _destPos;
    
    void Start()
    {
        // InputManager에서 Event 함수를 실행하도록 맡긴다.
        Managers.Input.KeyAction -= OnKeyboard;
        Managers.Input.KeyAction += OnKeyboard;
        Managers.Input.MouseAction -= OnMouseClicked;
        Managers.Input.MouseAction += OnMouseClicked;
    }

    private void Update()
    {
        if (_moveToDest)
        {
            Vector3 dir = _destPos - transform.position;
            if (dir.magnitude < 0.0001f) { _moveToDest = false; }   // 거리는 float, 이렇게 매우 적은 오차에 든다면 도착한것으로 간주.
            else    // 그렇지 않다면, 아직 도착하지 않은것이니 이동하도록.
            {
                float moveDest = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);
                transform.position += dir.normalized * moveDest;
                transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 10 * Time.deltaTime);    // 도착지를 바라보도록.
            }
        }
        
    }

    private float _slerp = 0.2f;
    void OnKeyboard()
    {
        if (Input.GetKey(KeyCode.W))
        {
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.forward), _slerp);
            transform.position += Vector3.forward * (Time.deltaTime * _speed);
        }

        if (Input.GetKey(KeyCode.A))
        {
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.left), _slerp);
            transform.position += Vector3.left * (Time.deltaTime * _speed);
        }

        if (Input.GetKey(KeyCode.S))
        {
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.back), _slerp);
            transform.position += Vector3.back * (Time.deltaTime * _speed);
        }

        if (Input.GetKey(KeyCode.D))
        {
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), _slerp);
            transform.position += Vector3.right * (Time.deltaTime * _speed);
        }

        _moveToDest = false;
    }

    void OnMouseClicked(Define.MouseEvent mouseEvent)
    {
        if (mouseEvent != Define.MouseEvent.Click) return;      // Define.MouseEvent.Click이 아닌 MouseEvent라면 무시하도록.
        
        #region Moving By Raycasting

        var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        Debug.DrawRay(Camera.main.transform.position, ray.direction * 100.0f, Color.red,1.0f);
        
        if (Physics.Raycast(ray, out var hit, 100.0f, LayerMask.GetMask("Wall")))
        {
            //Debug.Log($"Raycast Camera @ {hit.collider.gameObject.tag}");
            _destPos = hit.point;
            _moveToDest = true;
        }

        #endregion
    }
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.