Unity Chapter 4-6. 소코반 게임 만들기 : 게임 매니저, 승리 UI, 최종 빌드

Date:     Updated:

카테고리:

태그:

인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!


Chapter 4. 소코반 게임 만들기 : 게임 매니저, 승리 UI, 최종 빌드

게임 매니저와 승리 조건

  • 게임 매니저는 게임 로직을 관리하는 스크립트다.
    • 게임 매니저도 스크립트이기 때문에 나중에 실행 전 빈 오브젝트에 붙여줄 것이다.
  • “GameManager”라는 이름의 C# 스크립트를 생성한다. 유니티에서는 스크립트 이름을 게임매니저라고 지으면 톱니 바퀴 모양으로 바꿔준다.
    • “GameManager”가 구현할 일
      • ItemBox 3개가 EndPoint와 모두 한번씩 충돌했으면 승리
    • 알아야 할 것
      1. GameManager는 ItemBox 오브젝트 3개를 알고 있어야 한다.
        • ItemBox 오브젝트를 담는 3 크기의 배열
      2. ItemBox는 자신이 EndPoint와 충돌했었는지 알아야 한다.
        • bool타입의 flag를 두기. 충돌하면 true로.
        • 이 flag들을 GameManager에서 참고해서 3개의 모든 flag가 true면 게임을 승리처리하고 종료해야함.

📜 ItemBox.cs

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

public class ItemBox : MonoBehaviour
{
    public bool isOveraped = false; // ItemBox가 ItemBox와 붙어 있다면 true. 반드시 public으로 해야 게임매니저에서 사용 가능.

    private Renderer myRenderer;

    public Color touchColor; 
    private Color originalColor; 

    void Start()
    {
        myRenderer = GetComponent<Renderer>();
        originalColor = myRenderer.material.color; 
    }

    void Update()
    {

    }

    void OnTriggerEnter(Collider other)  // Enter는 충돌을 한 순간
    {
        if (other.tag == "EndPoint")
        {
            isOveraped = true; 
            myRenderer.material.color = touchColor;
        }
    }

    void OnTriggerExit(Collider other)  // Exit는 떼어지는 순간
    {
        if (other.tag == "EndPoint")
        {
            isOveraped = false;
            myRenderer.material.color = originalColor; // 원래 색깔로 돌리기 
        }
    }

    void OnTriggerStay(Collider other)  // Stay는 붙어있는 동안
    {
        if (other.tag == "EndPoint")
        {
            isOveraped = true;
            myRenderer.material.color = touchColor;
        }
    }
}

  • public bool isOveraped = false;
    • ItemBox가 EndPoint와 겹쳐지면 true로 바뀐다.
      • OnTriggerEnter, OnTriggerStay인 경우.
      • 3개의 모든 ItemBox의 isOveraped가 다 true면 게임 승리.
    • 초기값은 false
    • GameManager 스크립트에서 3개의 ItemBox의 isOveraped 값들이 모두 true인지 체크해야 하므로 public으로 지정해주어야 한다.
      • GameManager 스크립트에서, 즉 외부에서 사용할 수 있어야 하니까.

📜 GameManager.cs

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

public class GameManager : MonoBehaviour
{
    public ItemBox[] itemBoxes;
    public bool isGameOver;

    void Start()
    {
        isGameOver = false;
    }

    void Update()
    {
        if(isGameOver == true) // ItemBox 오브젝트의 각각의 `isOveraped` 값이 모두 True이면
        {
            return; // Update함수를 종료시킨다. 더 이상 아래 코드를 매 프레임마다 실행시키지 않음.
        }

        int count = 0;
        for(int i = 0; i < 3 ; i++)
        {
            if(itemBoxes[i].isOveraped == true) // update니까 매 프레임마다 for문 돌려서 isOveraped 가 true가 됐는지 계속 센다. 
            {
                count++;
            }
        }

        if(count >= 3)  // count 가 3 이상이면 게임 승리.
        {
            Debug.Log("게임 승리!");
            isGameOver = true;  // 업뎃
        }
    }
}
  • Hierarchy - Create Empty 하여 빈 오브젝트를 생성 후 이름을 “GameManager”라고 해주자. 그런 다음 이 게임 매니저 스크립트를 이 오브젝트에 붙여준다.
  • public ItemBox[] itemBoxes;
    • ItemBox 오브젝트를 넣을 수 있는 배열이다.
    • 크기는 아직 지정 X. 크기는 유니티에서 지정할 것.
      • public이므로 유니티에서 슬롯이 열려서 size를 입력하면 그 수만큼 배열의 크기가 결정되고 원소를 설정할 수 있는 슬롯이 나온다.
        • GameManager 스크립트는 GameManager 오브젝트에 붙이므로 GameManager 오브젝트의 Inspector창에서 슬롯이 열려 원소를 설정할 수 있다.
    • 유니티에서 size를 3으로 입력하면 3개의 슬롯이 열리는데 ItemBox 오브젝트 3개를 각각의 슬롯에 원소로 드래그 앤 드롭 해주자.
  • public bool isGameOver;
    • ItemBox 오브젝트의 각각의 isOveraped 값이 모두 True이면 isGameOver값을 True로 바꾸고 update 함수를 종료할 것이다.
    • public이기 때문에 ItemBox를 다 EndPoint에 옮기고나면 isGameOver슬롯이 체크가 되있는(True) 것을 유니티에서 확인할 수 있다
  • return
    • Update함수를 종료한다.
      • Update함수가 실행이 아예 안되는게 아님! 착각 노노. Update함수는 매 순간 실행되나 return을 앞에서 만나니 바로 Update함수가 종료되어 버리는 것일 뿐.
    • 더 이상 아래 코드를 매 프레임마다 실행시키지 않음.

image


승리 UI 만들기

UI 추가하기

Hierarchy에서 우클 한 후 UI - Text 해주면 텍스트 UI 오브젝트를 추가할 수 있다. 이름을 “Win UI”로 해주자.

image

UI를 생성하면 CanvasEventSystem이라는 것이 함께 생긴다.

Canvas와 EventSystem

image

  • Canvas
    • UI를 위한 2D 좌표계.
      • 이미지, 텍스트, 버튼 등등 모든 UI들은 이 Canvas위에 나타난다.
      • UI는 게임 월드랑은 상관없는 본인만의 좌표계를 쓴다.
        • 게임 월드의 크기, 위치와는 전혀 상관없음. 게임 월드와 독립적임.
    • 위 사진을 보면 알겠지만 Canvas는 엄~청 크다. 빨간 동그라미는 현재까지 만든 소코반 게임 맵…
      • 커다란 이유는 게임 월드와 사용하는 좌표계가 다른데 게임 월드에 적용하니 축척이 되서 저렇게 커진 것.
    • 게임 월드의 좌표계와는 전혀 상관이 없고 플레이어가 보게 될 화면에 대응한다.
      • 캔버스의 크기와 비율은 즉 Game창 비율에 비례한다.
  • EventSystem
    • 자동으로 실행되어 우리가 만질 필요 X
    • 유저의 키보드, 마우스, 터치 등등 이러한 유저들의 입력을 받아 UI에게 전달해준다.

텍스트 UI를 편집해보자 : Rect Transform

  1. 텍스트 편집을 쉽게 하기 위해 Scene창의 바로 아래에 있는 2D를 눌러 2D 뷰로 바꿔주자.
    • 이 상태에서 Hierarchy에서 Text UI를 더블 클릭하여 Text를 중점적으로 씬 카메라를 옮겨주자.
  2. Text UI를 캔버스 정중앙 위치에 배치해주자. 처음엔 Text가 엉뚱한 위치에 생기는 경우도 많아서..
    • Inspector창의 Rect Transform 컴포넌트.
    • 일반 Transform 컴포넌트와 비슷하되 사각형 영역에 '배치'하기 위한 Transform
      • Pos X,Y,Z, Width, Height,Anchors, Pivot으로 UI의 배치를 세밀하게 디자인 할 수 있지만 귀찮으므로 Anchor Presets를 이용하자. (좌측에 있는 양궁 과녘같은 그림)
    • Anchor Presets
      • 유니티에서 제공하는 자주 쓰이는 UI 배치.
      • 선택해서 사용하기만 하면 된다.
      • 선택시 Alt 키를 누른 상태에서 선택해야 반영된다!
  3. Text 컴포넌트에서 텍스트 내용을 편집해주자.
    • “You Win!”으로 바꿔주자.
    • 폰트 사이즈를 키워준다.
    • 폰트 사이즈를 너무 키워 내용이 짤릴 경우 텍스트 상자를 키워주는 방법도 있지만 아래에서 해결할 수도 있다.
      • Paragraph
        • Horizontal Overflow, Vertical Overflow 에서
        • 각각 수평 혹은 수직으로 글씨 수가 텍스트 UI 크기를 넘어가서 잘릴 때 Overflow라고 선택해주면 글씨가 안잘린다. 둘다 Overflow로 바꿔 준다.
          • 가운데 정렬 해준다.
          • 텍스트 컬러를 선택한다.
          • 폰트 종류도 바꿔준다.
    • 갖고 있는 폰트 파일을 유니티 📁/Assets에 넣어준 후 폰트 파일을 드래그 해서 Font에 넣어주면 된다. - 텍스트에 shadow 그림자 효과를 주자
    • 텍스트 UI에 Shadow 컴포넌트를 검색하여 추가해주면 그림자가 생긴다.

UI가 게임 승리시에만 나타나도록 구현하기

UI의 Inspector창에 UI 이름이 있는 곳 바로 왼쪽에 체크 박스가 있다. 이 체크 박스를 해제하면 UI가 보이지 않게 된다. 게임이 승리할 때만 이 체크 박스를 체크 하여 UI가 보이게 하자. 이 같은 처리를 역시 GameManager에서 해주면 된다.

📜 GameManager.cs

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

public class GameManager : MonoBehaviour
{
    public GameObject winUI;

    public ItemBox[] itemBoxes;
    public bool isGameOver;

    void Start()
    {
        isGameOver = false;
    }

    void Update()
    {
        if(isGameOver == true) 
        {
            return; 
        }

        int count = 0;
        for(int i = 0; i < 3 ; i++)
        {
            if(itemBoxes[i].isOveraped == true) 
            {
                count++;
            }
        }

        if(count >= 3) 
        {
            Debug.Log("게임 승리!");
            isGameOver = true;  
            winUI.SetActive(true); // winUI 변수가 참조하고 있는 오브젝트를 보이게끔 켜준다.
        }
    }
}

우선 WinUI 오브젝트의 체크박스를 해제하여 꺼준다.

-public GameObject winUI

  • public이므로 어떤 GameObject를 넣을 수 있는 슬롯이 유니티에서 열린다
  • 승리 텍스트 UI를 여기 드래그 앤 드롭 해주면 winUI변수가 이를 참조할 수 있게 된다.
  • winUI.SetActive(true);
    • winUI 변수가 참조하고 있는 오브젝트를 보이게끔 켜준다.
    • 게임에서 승리하면 (if(count >= 3)) 슬롯에서 winUI에 WinUI UI 오브젝트를 넣어주었으니 꺼놨던 WinUI가 켜져 텍스트 UI가 보이게 될 것이다.

image


스페이스바 누르면 게임을 재시작하게끔 해보자.

  1. Ctrl + S를 눌러 씬을 저장해준다.
  2. 메뉴 - File - Build Settings 에 들어간다.
  3. Scenes In Build에 만든 scene 파일을 드래그 앤 드롭하여 넣어준다.
    • 오른쪽에 보이는 숫자 0은 빌드 번호이다. 빌드에 포함되는 순서.
    • 앞 번호일 수록 게임을 실행했을 때 가장 먼저 열리는 씬이 된다.
  4. 아직 빌드 하지 말고 3번까지 실행한 상태에서 GameManager.cs 코드를 수정하자

📜 GameManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; // 💛 추가

public class GameManager : MonoBehaviour
{
    public GameObject winUI;

    public ItemBox[] itemBoxes;
    public bool isGameOver;

    void Start()
    {
        isGameOver = false;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // SceneManager.LoadScene("Main");
            SceneManager.LoadScene(0);
        }

        if (isGameOver == true)
        {
            return;
        }

        int count = 0;
        for (int i = 0; i < 3; i++)
        {
            if (itemBoxes[i].isOveraped == true)
            {
                count++;
            }
        }

        if (count >= 3)
        {
            Debug.Log("게임 승리!");
            isGameOver = true;
            winUI.SetActive(true);
        }
    }
}
  • using UnityEngine.SceneManagement
    • scene과 scene을 넘나 드는 작업을 하고 싶을 때 사용.
    • SceneManagement를 사용할 수 있다.
  • if (Input.GetKeyDown(KeyCode.Space))
    • 스페이스바를 누르는 순간
    • GetKey : 키를 누르는 동안
    • GetKeyUp : 키를 떼는 순간
    • GetKeyDown : 키를 누르는 순간
  • SceneManager.LoadScene(0);
    • 우리가 Build Settings에서 설정한 0번째로 빌드되는 씬을 불러 온다.
    • SceneManager.LoadScene(“Main”);
      • “Main”이라는 이름의 씬을 불러온다.
    • 즉 스페이스바를 누르면 해당 씬을 처음부터 다시 불러오는 것.


배경 음악 입히기

  1. 배경 음악으로 쓸 mp3파일을 /📁Assets에 넣어 준다.
  2. 배경 음악을 작동시키는 Audio Source 컴포넌트를 붙여 줄 빈 오브젝트를 생성한다.
    • 이름은 “BGM”으로 해주자.
    • Audio 컴포넌트를 검색하여 BGM 오브젝트에 붙여주자.
    • 유니티에서는 오디오 파일을 오디오 클립(Audio Clip)이라고 부른다.
  3. Audio Clip 부분에 배경음악으로 쓸 mp3 파일을 드래그 앤 드롭 해준다.
  4. 옵션 설정
    • Play On Awake : 게임 시작하자 마자 재생할건지
    • Loop : 음악이 끝나도 다시 반복할건지
    • 볼륨 크기 설정도 가능


게임이 끝나면 플레이어 오브젝트를 움직이지 못하도록 하기

  • Player 스크립트에서 GameManagement 오브젝트로부터 게임이 승리하고 종료되었다는 정보를 전달 받아서 Player 오브젝트를 못 움직이게끔 해주어야 함.
  • Player 스크립트의 update함수를 앞부분 if문을 놔서 return해 종료시켜 주면 된다.
    • 그럼 게임이 종료되었다는 if 조건문에 해당되면 아래에 키보드 입력을 받아 Player 오브젝트를 움직이는 코드를 이제 실행할 수 없게 됨.

📜 Player.cs

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

public class Player : MonoBehaviour
{
    public GameManager gameManager; // 추가

    public float speed = 10f;
    Rigidbody playerRigidbody;
    
    void Start()
    {
        playerRigidbody = GetComponent<Rigidbody>(); 
    }

    
    void Update()
    {
        if (gameManager.isGameOver == true) // 추가
        {
            return;
        }

        float inputX = Input.GetAxis("Horizontal");

        float inputZ = Input.GetAxis("Vertical");

        float fallSpeed = playerRigidbody.velocity.y; 

        Vector3 velocity = new Vector3(inputX, 0, inputZ);
        velocity = velocity * speed;

        velocity.y = fallSpeed; 

        playerRigidbody.velocity = velocity;  
    }
}
  • public GameManager gameManager;
    • 게임을 승리하면 True 값이 되는 GameManager 스크립트의 isGameOver 변수 정보가 필요하므로
    • GameManager 오브젝트를 참조할 gameManager 변수를 선언한다.
    • public 이므로 직접 Hierarchy창에 있는 GameManager 오브젝트를 드래그 하여 Player의 스크립트 gameManager 슬롯에 드롭 해주어야 함!
      • 잘 연결하면 이제 isGameOver 변수를 가져올 수 있다.
        • isGameOver 변수도 public이기 때문에 이렇게 외부에서 사용 가능한 것.
  • if (gameManager.isGameOver == true)
    • 게임이 종료됐다는 정보를 GameManager 오브젝트로부터 받으면
    • return
      • Update 함수를 종료한다.
        • isGameOver = True 가 되어 이제 매 프레임마다 Update 함수가 실행되도 여기서 return 될테니 아래에 키보드 입력을 받아 Player 오브젝트를 움직이는 코드를 이제 실행되지 않는다.


최종 빌드

  1. 빌드 전 Ctrl + S를 눌러 씬을 저장해준다.
  2. 메뉴 - File - Build Settings 에 들어간다.
  3. 작업한 Scene 파일을 드래그 하여 Scene in Build에 드롭해준다. 이미 되있다면 패스
  4. Build and Run을 해주고 저장하면 끝
    • 프로젝트 경로에 같이 저장하는건 비추다. 꼬일 수 있음.
    • 그냥 바탕화면 같은 곳에 새 폴더 만들어서 그곳에 저장해주자.
  5. 저장할 파일을 실행시켜 게임을 플레이 해보자.

cf) 만든 게임을 다른 사람에게 보낼 때는 만들어진 폴더, 파일을 전체 압축해야 한다.



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Unity Lesson 1 카테고리 내 다른 글 보러가기

댓글 남기기