Post

유니티 기본

캡스톤 프로젝트 시작하기 직전에 이미 경험이 있던 팀원 한 분이 알려주신 내용을 정리한 글

Unity Version : 2022.3.16f1 LTS

오브젝트의 생명 주기

이벤트 함수의 실행 순서 - Unity 매뉴얼

크게 보면 Awake → OnEnable → Start → FixedUpdate → Update → LateUpdate → OnApplicationQuit → OnDisable → OnDestroy 순서로 진행됨

Awake는 무조건 실행되고, OnEnable은 해당 오브젝트가 활성화 된 후에 실행됨

Update는 매 틱마다 실행되는 부분

이 부분을 Enum(열거형)으로 설정해서 조절할 수 있다

오브젝트의 컴포넌트 편집

1
2
3
4
5
6
7
8
9
public class MyObject : MonoBehaviour
{
	public Rigidbody rigidbody; // 오브젝트 이동을 위한 컴포넌트
	public float speed = 1.0f; // 사용자 지정 변수
	public Camera cam; // 카메라
	public GameObject my_prefab; // 사용자 지정 오브젝트
	public LayerMask layermask; // 레이어 마스크 (충돌을 인식할 레이어를 선택하는 용도)
...
}

위의 예시처럼 public으로 만들어주면 코드 창이 아닌 Unity Inspector 창에서도 확인하고 변경가능!

키보드 입력으로 오브젝트 움직이기

오브젝트를 움직이게 하기 위해서는 RigidBody 컴포넌트를 붙여줘야 한다.

RigidBody 컴포넌트는 게임 오브젝트가 물리 제어로 동작하게 한다.

질량, 공기 저항 정도를 조절할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void moveSphere()
{
    float now_x = rigidbody.velocity.x;
    float now_y = rigidbody.velocity.y;
    float now_z = rigidbody.velocity.z;

    if (!Input.GetKey(KeyCode.None))
    {
        if (Input.GetKey(KeyCode.W)) { now_z = now_z < max_speed ? now_z + speed : max_speed; } // 전진
        else if (Input.GetKey(KeyCode.S)) { now_z = now_z > -max_speed ? now_z - speed : -max_speed; } // 후진
        if (Input.GetKey(KeyCode.D)) { now_x = now_x < max_speed ? now_x + speed : max_speed; } // 우로 이동
        else if (Input.GetKey(KeyCode.A)) { now_x = now_x > -max_speed ? now_x - speed : -max_speed; } // 좌로 이동
        if (Input.GetKeyDown(KeyCode.Space)) { now_y = now_y < 1f ? now_y + jump_power : 0; } // 점프 구현

        rigidbody.velocity = new Vector3(now_x, now_y, now_z);  // Rigidbody의 속도 벡터를 설정
    }
    else
    {
				// 아무것도 입력하지 않았을 때, Rigidbody가 정지하는 것처럼 보이게 하기
        Vector3 power = new Vector3(-now_x * 1.2f, 0, -now_z * 1.2f); 
        rigidbody.AddForce(power);
    }
}

마우스 조작으로 카메라 움직이기

카메라를 움직이기 위해서는 GetKey가 아니라 GetAxis를 활용해야 한다.

이때 GetAxis 안에 들어가는 내용은 Edit → Project Settings → Input Manager 안에 있는 내용이다.

1
2
3
4
5
6
7
8
void moveCamera()
    {
        float mouse_x = Input.GetAxis("Mouse X");
        float mouse_y = Input.GetAxis("Mouse Y");

        // 회전은 축을 기준으로 회전이기 때문에, x축 기준 회전은 카메라가 위 아래로 움직이는 것 처럼 보인다.
        cam.transform.eulerAngles += new Vector3(-mouse_y, mouse_x, 0) * cam_rotate_speed;
    }

Raycast 구현하기

Raycast : 보이지 않는 선을 사용해서 물체를 선택하는 것

여기서는 마우스 좌클릭을 했을 때 Raycast를 통해 선택되는 물체 위에 미리 정해둔 prefab을 생성하는 예제를 작성했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void myRaycast()
    {
        float my_prefab_y = my_prefab.transform.localScale.y; // prefab의 높이
        if (Input.GetMouseButtonDown(0)) // 좌클릭 했을 때
        {
            var ScreenCenter = new Vector3(Camera.main.pixelWidth / 2, Camera.main.pixelHeight / 2); // 화면의 정중앙
            Ray ray = cam.ScreenPointToRay(ScreenCenter); // 화면의 정중앙에 선 발사
            RaycastHit hit;
            Vector3 spawn_point = Vector3.zero;
            if (Physics.Raycast(ray, out hit, max_instantiate_range, layerMask)) // Hit 했으면 True, Hit 하지 못했으면 False Return 함
            {
                Debug.Log(hit.transform.gameObject);
                spawn_point = new Vector3(hit.point.x, hit.point.y + my_prefab_y, hit.point.z);
            }
            else
            {
                // 이러면 원점 기준으로 생김. 카메라의 위치를 고려하지 않았기 때문
                // Instantiate(my_prefab, cam.transform.forward * max_instantiate_range, Quaternion.identity); 
                spawn_point = cam.transform.position + cam.transform.forward * max_instantiate_range; // 카메라의 위치를 고려 + 최대 범위 설정
            }
            Instantiate(my_prefab, spawn_point, Quaternion.identity); // prefab 소환
        }
    }
}

싱글톤 패턴 구현하기

게임을 전반적으로 관리하는 시스템에 대해서는 싱글톤으로 작성할 필요가 있다.

아래는 GameManager라는 오브젝트를 싱글톤으로 구현한 예제이다.

StartCoroutine을 사용하면 특정 조건에 있을 때 갱신 부분을 진행하지 않고 넘어가도록 할 수 있다.

StartCoroutine 함수 내부에 들어가는 매개변수는 IEnumerator 함수이다.

IEnumerator 함수를 구현할 때, yield return null ⇒ 더이상 갱신하지 않겠다는 의미이다. (⇒ 수정. 한 턴을 넘기겠다는 의미)

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
public class GameManager : MonoBehaviour
{
    private static GameManager _instance = null;

    public static GameManager Instance { get { return _instance; } } // 싱글톤 객체기 때문에 getter만 존재함
    public float fl_num = 0;
    public GameObject obj_prefab = null;
    void Start()
    {
        StartCoroutine(enumrator()); // 직접 지정한 Enumerator를 사용해 갱신되는 주기를 변경함
    }

    private void Awake()
    {
        if (_instance == null) // GameManager 객체가 없으면
        {
            _instance = this; // 만들고
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            if (_instance != null) // GameManager 객체가 있으면
            {
                Destroy(this); // 그 이후에 만들어진 객체는 지워서 싱글톤으로 유지한다
            }
        }
    }
    // Update is called once per frame
    void Update()
    {
        ...
    }

    IEnumerator enumrator()
    {
        while(true)
        {
            GameManager.Instance.fl_num = fl_num + 0.01f;
            yield return new WaitForSeconds(1.0f); // 무한정으로 fl_num이 증가되는 것을 막기 위해, 1초동안 다른 객체에게 순서를 양보한다
        } 
        yield return null;
    }
}
This post is licensed under CC BY 4.0 by the author.