기록 보관소

[Unity/유니티] 기초-뱀서라이크: HUD 제작하기[10] 본문

유니티 프로젝트/뱀서라이크

[Unity/유니티] 기초-뱀서라이크: HUD 제작하기[10]

JongHoon 2023. 7. 11. 19:59

개요

유니티 독학을 위해 아래 링크의 골드메탈님의 영상들을 보고 직접 따라 해보면서 진행 상황을 쓰고 배웠던 점을 요약한다.

https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2 

 

📚유니티 기초 강좌

유니티 게임 개발을 배우고 싶은 분들을 위한 기초 강좌

www.youtube.com


뱀서라이크: HUD 제작하[10]

1. UI 캔버스

UI - Canvas 생성
Screen Space - Overlay로 설정

  • RectTransform : 스크린 전용 Transform 역할 컴포넌트
  • Render Mode
    • Screen-Overlay : 스크린에 그대로 얹는 형태
    • Screen-Camera : 스크린을 카메라에 맞추는 형태
    • World Space : 월드 공간에 배치하는 형태

테스트를 위한 Text(Legacy) 생성 및 수정
Scale With Screen Size로 변경

  • UI Scale Mode
    • Constant Pixel Size : 해상도에 상관없이 픽셀 크기 고정
    • Scale With Screen Size : 해상도에 따라 픽셀 크기 변경됨
    • Constant Physical Size

Reference Resolution을 Main Camera의 Pixel Perfect Camera의 값과 동일하게
변경하면 해상도가 작아지면서 텍스트가 엄청나게 커지게 된다. UI Scale Mode 설정 때문.

  • Refernce Resolution : 해상도.
    • Main Camera의 Pixel Perfect Camera 컴포넌트에도 동일한 설정 값이 있다.

Match와 Reference Pixels Per Unit도 변경
테스트를 위해 텍스트 Font Size는 조금 줄여준다
Full HD에서의 UI 모습
QHD에서의 UI 모습
4K에서도 UI가 크기를 계속 유지하고 있다


2. 스크립트 준비

HUD 스크립트 파일 생성

//HUD Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   //Text

public class HUD : MonoBehaviour {
    public enum InfoType {  Exp, Level, Kill, Time, Health }    //열거형
    public InfoType type;

    Text myText;
    Slider mySlider;

    void Awake() {
        myText = GetComponent<Text>();
        mySlider = GetComponent<Slider>();
    }

    void LateUpdate() {
        switch (type) {
            case InfoType.Exp:

                break;
            case InfoType.Level:

                break;
            case InfoType.Kill:

                break;
            case InfoType.Time:

                break;
            case InfoType.Health:

                break;
        }
    }
}

3. 경험치 게이지

테스트용 Text는 제거하고, Slider 추가
Anchor 변경
Slider 변경

  • Interactable : 플레이어가 임의로 수정할 수 있게 하는 설정. 체크시 슬라이더의 원을 마우스로 끌어서 값 변경이 가능해짐.
  • Transition : 슬라이더 값에 따라 색상 변경
  • Navigation : UI의 Tab 포커싱 순서.

Slider 아래의 Handle Slide Area도 지운다
Background의 Anchor 설정
Fill Area의 Anchor도 동일하게 설정해준다
Fill의 Lef, Right 값을 0으로 설정
Background의 Source Image를 Back0 스프라이트로 변경
Fill의 Source Image도 Front 0 스프라이트로 변경
잘 변경되었다.

//HUD Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   //Text

public class HUD : MonoBehaviour {
    public enum InfoType {  Exp, Level, Kill, Time, Health }    //열거형
    public InfoType type;

    Text myText;
    Slider mySlider;

    void Awake() {
        myText = GetComponent<Text>();
        mySlider = GetComponent<Slider>();
    }

    void LateUpdate() {
        switch (type) {
            case InfoType.Exp:  //슬라이더로 표현할 경험치 = 현재 경험치 / 최대 경험치
                float curExp = GameManager.instance.exp;
                float maxExp = GameManager.instance.nextExp[GameManager.instance.level];
                mySlider.value = curExp / maxExp;

                break;
            case InfoType.Level:

                break;
            case InfoType.Kill:

                break;
            case InfoType.Time:

                break;
            case InfoType.Health:

                break;
        }
    }
}

Slider에 HUD 스크립트 추가
테스트 실행. 경험치 바가 비어있다.
몬스터를 처치하니 경험치가 올라갔다.


4. 레벨 및 킬수 텍스트

Slider 이름은 Exp로 변경해준다
Text(Legacy) 생성
Anchor 설정
Text 설정 변경
Image 추가 및 Source Image 지정
Set Native Size를 눌러 크기를 줄여준다
앵커를 왼쪽 위로 지정하고, X와 Y 값을 수정해 위치를 조정한다
Level 텍스트를 복사해서 Image의 자식 오브젝트로 넣고 앵커와 위치를 조정한다
Text의 Alignment도 수정하고, 이미지와 텍스트 오브젝트의 이름을 수정

//HUD Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   //Text

public class HUD : MonoBehaviour {
    public enum InfoType {  Exp, Level, Kill, Time, Health }    //열거형
    public InfoType type;

    Text myText;
    Slider mySlider;

    void Awake() {
        myText = GetComponent<Text>();
        mySlider = GetComponent<Slider>();
    }

    void LateUpdate() {
        switch (type) {
            case InfoType.Exp:  //슬라이더로 표현할 경험치 = 현재 경험치 / 최대 경험치
                float curExp = GameManager.instance.exp;
                float maxExp = GameManager.instance.nextExp[GameManager.instance.level];
                mySlider.value = curExp / maxExp;
                break;
            case InfoType.Level:
                myText.text = string.Format("Lv.{0:F0}", GameManager.instance.level); //F0, F1, F2... : 소수점 자리를 지정
                break;
            case InfoType.Kill:
                myText.text = string.Format("{0:F0}", GameManager.instance.kill);
                break;
            case InfoType.Time:

                break;
            case InfoType.Health:

                break;
        }
    }
}
  • String.Format : 각 숫자 인자값을 지정된 형태의 문자열로 만들어주는 함수

Level 텍스트에 HUD 삽입 후 Type 변경
Kill Text도 마찬가지로 변경해준다
테스트 실행. 게임 시작 직후.
1마리를 죽이니 킬 카운트가 올라갔다.
레벨 업도 잘 된다.


5. 타이머 텍스트

이전에 만든 Level Text를 복사하여 Timer로 수정. 앵커, Y축 위치 및 기타 텍스트 설정도 변경한다.
HUD 타이머도 수정해준다.

//HUD Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   //Text

public class HUD : MonoBehaviour {
    public enum InfoType {  Exp, Level, Kill, Time, Health }    //열거형
    public InfoType type;

    Text myText;
    Slider mySlider;

    void Awake() {
        myText = GetComponent<Text>();
        mySlider = GetComponent<Slider>();
    }

    void LateUpdate() {
        switch (type) {
            case InfoType.Exp:  //슬라이더로 표현할 경험치 = 현재 경험치 / 최대 경험치
                float curExp = GameManager.instance.exp;
                float maxExp = GameManager.instance.nextExp[GameManager.instance.level];
                mySlider.value = curExp / maxExp;
                break;
            case InfoType.Level:
                myText.text = string.Format("Lv.{0:F0}", GameManager.instance.level); //F0, F1, F2 ... : 소수점 자리를 지정
                break;
            case InfoType.Kill:
                myText.text = string.Format("{0:F0}", GameManager.instance.kill);
                break;
            case InfoType.Time:
                float remainTime = GameManager.instance.maxGameTime - GameManager.instance.gameTime;
                int min = Mathf.FloorToInt(remainTime / 60);
                int sec = Mathf.FloorToInt(remainTime % 60);
                myText.text = string.Format("{0:D2}:{1:D2}", min, sec); //D0, D1, D2 ... : 자리수를 지정
                break;
            case InfoType.Health:

                break;
        }
    }
}

테스트 실행. 게임 실행 직후.
타이머가 잘 작동하고 있다


6. 체력 게이지

Create Empty로 Canvas에 빈 오브젝트를 생성한 후, 크기 조정.
Exp를 복사해서 Health의 자식 오브젝트로 넣고, 오브젝트 이름, 앵커, 크기를 조절해준다. HUD 타입도 변경해준다.
Background 스프라이트 변경
Fill 스프라이트도 변경
플레이어와 체력바가 잘 보이도록 Health Slider 위치 조정

//GameManager Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour {
	public static GameManager instance;
	[Header("# Game Control")]
	public float gameTime;	//게임 시간 변수
	public float maxGameTime = 2 * 10f; //최대 게임 시간 변수(20초).
	[Header("# Player Info")]
	public int health;
	public int maxHealth = 100;
    public int level;
	public int kill;
	public int exp;
	public int[] nextExp = { 3, 5, 10, 100, 150, 210, 280, 360, 450, 600 };
    [Header("# Game Object")]
    public PoolManager pool;
    public Player player;

    void Awake() {
		instance = this;
	}

	void Start() {
		health = maxHealth;
	}

	void Update() {
		gameTime += Time.deltaTime;

		if (gameTime > maxGameTime) {
			gameTime = maxGameTime;
		}
	}

	public void GetExp() {
		exp++;

		if (exp == nextExp[level]) {
			level++;
			exp = 0;

		}
	}
}
//HUD Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   //Text

public class HUD : MonoBehaviour {
    public enum InfoType {  Exp, Level, Kill, Time, Health }    //열거형
    public InfoType type;

    Text myText;
    Slider mySlider;

    void Awake() {
        myText = GetComponent<Text>();
        mySlider = GetComponent<Slider>();
    }

    void LateUpdate() {
        switch (type) {
            case InfoType.Exp:  //슬라이더로 표현할 경험치 = 현재 경험치 / 최대 경험치
                float curExp = GameManager.instance.exp;
                float maxExp = GameManager.instance.nextExp[GameManager.instance.level];
                mySlider.value = curExp / maxExp;
                break;
            case InfoType.Level:
                myText.text = string.Format("Lv.{0:F0}", GameManager.instance.level); //F0, F1, F2 ... : 소수점 자리를 지정
                break;
            case InfoType.Kill:
                myText.text = string.Format("{0:F0}", GameManager.instance.kill);
                break;
            case InfoType.Time:
                float remainTime = GameManager.instance.maxGameTime - GameManager.instance.gameTime;
                int min = Mathf.FloorToInt(remainTime / 60);
                int sec = Mathf.FloorToInt(remainTime % 60);
                myText.text = string.Format("{0:D2}:{1:D2}", min, sec); //D0, D1, D2 ... : 자리수를 지정
                break;
            case InfoType.Health:
                float curHealth = GameManager.instance.health;
                float maxHealth = GameManager.instance.maxHealth;
                mySlider.value = curHealth / maxHealth;
                break;
        }
    }
}

테스트 실행. 체력바는 화면에 고정된 UI라서 플레이어의 이동을 따라가지 못한다.
이를 해결할 Follow 스크립트 생성

//Follow Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Follow : MonoBehaviour {
    RectTransform rect;

    void Awake() {
        rect = GetComponent<RectTransform>();
    }

    void FixedUpdate() {
        rect.position = Camera.main.WorldToScreenPoint(GameManager.instance.player.transform.position); //월드 좌표와 스크린 좌표는 서로 다르므로 WorldToScreenPoint 사용.
    }
}
  • Camera.main.WorldToScreenPoint() : 월드 상의 오브젝트 위치를 스크린 좌표로 변환.

Health 오브젝트에 Follow 스크립트 추가
최종 테스트 실행. 체력 UI가 문제없이 잘 따라온다.