내일배움캠프/TIL

[내배캠][Unity6기][TIL] 3. 고양이 밥주기(5~9)

binary는 호남선 2024. 9. 7. 11:26

Preview

- Canvas의 Render Mode
- OnTriggerEnter()
- isKinematic
- OnTriggerEnter와 Update 시점 차이
- 버그 패치
- 코드 재사용
- HP Bar 만들기
- 확률에 따라 실행하는 로직 만들기
- Prefab의 활용


Canvas의 Render Mode

- UI 요소들을 랜더링하는 방법을 선택할 수 있음

Screen Space - Overlay :  UI 요소가 화면에서 씬의 위에 렌더링, 플레이 시 화면에 고정되어 표시됨

World Space : 캔버스는 씬에 있는 다른 오브젝트처럼 동작, UI가 월드에 배치된 오브젝트처럼 보임

참고 : https://docs.unity3d.com/kr/530/Manual/UICanvas.html

예시 화면)

브롤스타즈 플레이 화면 (출처 : https://play.google.com/store/apps/details?id=com.supercell.squad&hl=en_US)

Screen Space - Overlay로 표현된 UI, 화면이 움직여도 고정

World Space로 표현된 UI, 월드의 일부처럼 그룹화된 오브젝트 이동에 따라 함께 이동

OnTriggerEnter()

- 오브젝트가 서로 겹치는 이벤트 발생할 때 호출되는 함수

- 물리적 충돌이 필요하지 않고 닿았거나 특정 영역에 들어왔는지 확인하는 경우에 주로 사용

- Trigger matrix (0열이 자신, 0행이 상대), static은 Rigidbody없이 collider만 있는 상태

참고 : https://docs.unity3d.com/Manual/collider-types-interaction.html

https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html

isKinematic

- RigidBody 컴포넌트의 프로퍼티 중 하나

- 게임오브젝트가 물리 엔진을 영향을 받는지 여부

- isKinematic이 true면 힘, 충돌, 중력 등의 물리적 영향을 받고, false면 물리 법칙을 무시

참고 :  https://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html

OnTriggerEnter와 Update 시점 차이

[ 예시 게임(고양이 밥주기)에서 배부른 상태일 때 시점 차이 분석 ]

- OnTriggerEnter : 배부른 상태면 이미지를 배고픈 냥이에서 배부른 냥이로 변경

- Update : 배부른 상태면 옆으로 이동

=> if 조건 분기 내용(배부른 상태 체크)은 같으나 이미지 전환과 이동이 다른 함수에서 이뤄지므로 약간의 시간차 발생

* 중단점 걸고 디버거로 확인해보면 OnTriggerEnter가 먼저 실행됨

버그 패치

- 아래 코드에서 배부른 냥이가 되었음에도 바로 이미지 변하지 않는 버그

- 배부른 냥이가 화면에서 사라지기 전에 밥을 먹으면 계속 점수 올라가는 버그

- 냥이 에너지가 4f인 상태에서 충돌이 발생하면

public GameObject hungryCat;
public GameObject fullCat;

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.CompareTag("Food"))
    {
        // 에너지가 최대보다 작으면(배부른 상태가 아니면)
        // 현재 에너지 4이므로 if 내용 실행
        if (energy < full)
        {      
            energy += 1.0f;	// 1증가하여 energy 5로 배부른 상태
            Destroy(collision.gameObject);
            front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
        }
        // energy가 5인 상태이지만 아래 내용 실행 x
        // energy 5이고 다시 트리거 발생해야지만 아래 내용 실행
        else
        {
            hungryCat.SetActive(false);
            fullCat.SetActive(true);
            Destroy(this.gameObject, 3.0f);
            // 냥이가 배부른 상태로 밥을 먹으면 계속 점수 증가하는 버그 발생
            GameManager.Instance.AddScore();
        }
    }
}

- if else가 아닌 중첩 if로 조건을 한번 더 체크

bool isFull = false;	// 배부른 상태 체크

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.CompareTag("Food"))
    {
        // 에너지 4이므로 if 내용 실행
        if (energy < full)
        {
            energy += 1.0f;	// 1 증가하여 energy 5
            front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
            Destroy(collision.gameObject);
            // 에너지 더하는 연산 후 배부른 상태가 되었는지 한번 더 확인
            // 현재 energy 5이므로 if 내용 실행
            if (energy == 5.0f)
            {
                // 첫번째 검사에서 아직 배부른 상태가 아니므로 if 내용 실행
                // 두번째 검사부터는 배부른 상태이므로 실행 x
                // 계속해서 점수가 더해지는 버그 방지
                if (!isFull)
                {
                    isFull = true; // 배부른 상태로 변경
                    // 이미지 전환과 오브젝트 파괴로직 실행
                    hungryCat.SetActive(false);
                    fullCat.SetActive(true);
                    Destroy(this.gameObject, 3.0f);
                    // 냥이를 만족시켰으므로 점수를 더하는 함수 호출
                    // 1마리 냥이당 1번만 점수 증가
                    GameManager.Instance.AddScore();
                }
            }
        }
    }
}

코드 재사용

- StartScene의 StartBtn과 MainScene의 RetryBtn은 둘다 MainScene을 로드함

- 이름은 다르나 근본적으로 기능이 동일하므로 작성한 코드를 재사용해도 OK

HP Bar 만들기

1. Hp 바의 대상이되는 게임오브젝트 자식으로 크기와 위치가 같은 Image 2개 생성

2. Canvas의 Render 모드 World Space로 변경

3. Front(게이지가 차면 늘어남), Back(hp바의 배경)으로 이름 변경

4. Back 색상은 흰색, Front는 눈에 띄는 색으로 변경

5. Front의 Rect Transform 컴포넌트에서 PivotX를 0으로 설정 (0을 기준으로 크기가 늘어남)

6. 스크립트에서 scale 변경하도록 작성

public Transform front;	// 게이지 UI 가져오기

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.CompareTag("Food"))
    {
        energy += 1.0f;
        // x를 (현재 체력 / 최대 체력)으로 설정
        front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
    }
}

번외) 레벨 score 바

public Transform levelFront;
int lvThreshold; // 레벨 임계점

public void AddScore()
{
    score++;
    level = score / lvThreshold;
    // (현재 점수 - 현재 레벨 * 레벨 임계점) / (float)레벨 임계점
    // scaleX값은 float이므로 형변환 필요
    levelFront.localScale = new Vector3((score - level * lvThreshold) / (float)lvThreshold, 1.0f, 1.0f);
}

확률에 따라 실행하는 로직 만들기

- Random을 통해 정해진 백분율에 따라 랜덤하게 실행하는 로직 만들 수 있음

ex) 77%의 확률로 실행되는 로직

float p = Random.Range(0, 10);
if (p < 7.7)
{
	// 77% 확률로 실행할 로직
}

Prefab의 활용

[ 원래 프리팹과 조금 다른 프리팹 만들기 ]

1) 원본 프리팹을 Prefab 폴더에서 Hierarchy 창으로 Drag & Drop

2) Hierarchy에서 원본 프리팹 우클릭->Prefab->Unpack

3) 바꿀 부분 적용하고 이름 변경

4) 변경한 오브젝트 Prefab 폴더에 Drag & Drop하여 새로운 프리팹으로 만들기