Unity Chapter 4-6. 소코반 게임 만들기 : 게임 매니저, 승리 UI, 최종 빌드
카테고리: Unity Lesson 1
태그: Unity Game Engine
인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!
Chapter 4. 소코반 게임 만들기 : 게임 매니저, 승리 UI, 최종 빌드
게임 매니저와 승리 조건
- 게임 매니저는 게임 로직을 관리하는 스크립트다.
- 게임 매니저도 스크립트이기 때문에 나중에 실행 전 빈 오브젝트에 붙여줄 것이다.
- “GameManager”라는 이름의 C# 스크립트를 생성한다. 유니티에서는 스크립트 이름을 게임매니저라고 지으면 톱니 바퀴 모양으로 바꿔준다.
- “GameManager”가 구현할 일
- ItemBox 3개가 EndPoint와 모두 한번씩 충돌했으면 승리
- 알아야 할 것
GameManager
는 ItemBox 오브젝트 3개를 알고 있어야 한다.- ItemBox 오브젝트를 담는 3 크기의 배열
ItemBox
는 자신이 EndPoint와 충돌했었는지 알아야 한다.- bool타입의 flag를 두기. 충돌하면 true로.
- 이 flag들을
GameManager
에서 참고해서 3개의 모든 flag가 true면 게임을 승리처리하고 종료해야함.
- “GameManager”가 구현할 일
📜 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 스크립트에서, 즉 외부에서 사용할 수 있어야 하니까.
- ItemBox가 EndPoint와 겹쳐지면 true로 바뀐다.
📜 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) 것을 유니티에서 확인할 수 있다
- ItemBox 오브젝트의 각각의
return
- Update함수를 종료한다.
- Update함수가 실행이 아예 안되는게 아님! 착각 노노. Update함수는 매 순간 실행되나 return을 앞에서 만나니 바로 Update함수가 종료되어 버리는 것일 뿐.
- 더 이상 아래 코드를 매 프레임마다 실행시키지 않음.
- Update함수를 종료한다.
승리 UI 만들기
UI 추가하기
Hierarchy에서 우클 한 후 UI - Text 해주면 텍스트 UI 오브젝트를 추가할 수 있다. 이름을 “Win UI”로 해주자.
UI를 생성하면 Canvas
와 EventSystem
이라는 것이 함께 생긴다.
Canvas와 EventSystem
Canvas
- UI를 위한 2D
좌표계
.- 이미지, 텍스트, 버튼 등등 모든 UI들은 이
Canvas
위에 나타난다. - UI는 게임 월드랑은 상관없는 본인만의 좌표계를 쓴다.
- 게임 월드의 크기, 위치와는 전혀 상관없음. 게임 월드와 독립적임.
- 이미지, 텍스트, 버튼 등등 모든 UI들은 이
- 위 사진을 보면 알겠지만 Canvas는 엄~청 크다. 빨간 동그라미는 현재까지 만든 소코반 게임 맵…
- 커다란 이유는 게임 월드와 사용하는 좌표계가 다른데 게임 월드에 적용하니 축척이 되서 저렇게 커진 것.
- 게임 월드의 좌표계와는 전혀 상관이 없고 플레이어가 보게 될 화면에 대응한다.
- 캔버스의 크기와 비율은 즉 Game창 비율에 비례한다.
- UI를 위한 2D
EventSystem
- 자동으로 실행되어 우리가 만질 필요 X
- 유저의 키보드, 마우스, 터치 등등 이러한 유저들의 입력을 받아 UI에게 전달해준다.
텍스트 UI를 편집해보자 : Rect Transform
- 텍스트 편집을 쉽게 하기 위해 Scene창의 바로 아래에 있는
2D
를 눌러 2D 뷰로 바꿔주자.- 이 상태에서 Hierarchy에서 Text UI를 더블 클릭하여 Text를 중점적으로 씬 카메라를 옮겨주자.
- Text UI를 캔버스 정중앙 위치에 배치해주자. 처음엔 Text가 엉뚱한 위치에 생기는 경우도 많아서..
- Inspector창의
Rect Transform
컴포넌트.
- 일반 Transform 컴포넌트와 비슷하되 사각형 영역에 '배치'하기 위한 Transform
Pos X,Y,Z
,Width
,Height
,Anchors
,Pivot
으로 UI의 배치를 세밀하게 디자인 할 수 있지만 귀찮으므로Anchor Presets
를 이용하자. (좌측에 있는 양궁 과녘같은 그림)
Anchor Presets
- 유니티에서 제공하는 자주 쓰이는 UI 배치.
- 선택해서 사용하기만 하면 된다.
- 선택시
Alt 키
를 누른 상태에서 선택해야 반영된다!
- Inspector창의
- Text 컴포넌트에서 텍스트 내용을 편집해주자.
- “You Win!”으로 바꿔주자.
- 폰트 사이즈를 키워준다.
- 폰트 사이즈를 너무 키워 내용이 짤릴 경우 텍스트 상자를 키워주는 방법도 있지만 아래에서 해결할 수도 있다.
- Paragraph
- Horizontal Overflow, Vertical Overflow 에서
- 각각 수평 혹은 수직으로 글씨 수가 텍스트 UI 크기를 넘어가서 잘릴 때
Overflow
라고 선택해주면 글씨가 안잘린다. 둘다 Overflow로 바꿔 준다.- 가운데 정렬 해준다.
- 텍스트 컬러를 선택한다.
- 폰트 종류도 바꿔준다.
- Paragraph
- 갖고 있는 폰트 파일을 유니티
📁/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가 보이게 될 것이다.
스페이스바 누르면 게임을 재시작하게끔 해보자.
Ctrl + S
를 눌러 씬을 저장해준다.- 메뉴 - File - Build Settings 에 들어간다.
- Scenes In Build에 만든 scene 파일을 드래그 앤 드롭하여 넣어준다.
- 오른쪽에 보이는 숫자
0
은 빌드 번호이다. 빌드에 포함되는 순서.
- 앞 번호일 수록 게임을 실행했을 때 가장 먼저 열리는 씬이 된다.
- 오른쪽에 보이는 숫자
- 아직 빌드 하지 말고 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”이라는 이름의 씬을 불러온다.
- 즉 스페이스바를 누르면 해당 씬을 처음부터 다시 불러오는 것.
배경 음악 입히기
- 배경 음악으로 쓸 mp3파일을
/📁Assets
에 넣어 준다. - 배경 음악을 작동시키는 Audio Source 컴포넌트를 붙여 줄 빈 오브젝트를 생성한다.
- 이름은 “BGM”으로 해주자.
- Audio 컴포넌트를 검색하여 BGM 오브젝트에 붙여주자.
- 유니티에서는 오디오 파일을 오디오 클립(Audio Clip)이라고 부른다.
- Audio Clip 부분에 배경음악으로 쓸 mp3 파일을 드래그 앤 드롭 해준다.
- 옵션 설정
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이기 때문에 이렇게 외부에서 사용 가능한 것.
- 잘 연결하면 이제
- 게임을 승리하면 True 값이 되는 GameManager 스크립트의
if (gameManager.isGameOver == true)
- 게임이 종료됐다는 정보를 GameManager 오브젝트로부터 받으면
return
- Update 함수를 종료한다.
isGameOver = True
가 되어 이제 매 프레임마다 Update 함수가 실행되도 여기서 return 될테니 아래에 키보드 입력을 받아 Player 오브젝트를 움직이는 코드를 이제 실행되지 않는다.
- Update 함수를 종료한다.
최종 빌드
- 빌드 전
Ctrl + S
를 눌러 씬을 저장해준다. - 메뉴 - File - Build Settings 에 들어간다.
- 작업한 Scene 파일을 드래그 하여 Scene in Build에 드롭해준다. 이미 되있다면 패스
Build and Run
을 해주고 저장하면 끝- 프로젝트 경로에 같이 저장하는건 비추다. 꼬일 수 있음.
- 그냥 바탕화면 같은 곳에 새 폴더 만들어서 그곳에 저장해주자.
- 저장할 파일을 실행시켜 게임을 플레이 해보자.
cf) 만든 게임을 다른 사람에게 보낼 때는 만들어진 폴더, 파일을 전체 압축해야 한다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글남기기