using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ResultManager : MonoBehaviour
{
public GameObject scoreText;
// Start is called before the first frame update
void Start() {
scoreText.GetComponent<Text>().text = GameManager.totalScore.ToString();
}
}
// +++ 시간 제한 추가 +++
public GameObject timeBar; // 시간 표시 이미지
public GameObject timeText; // 시간 텍스트
TimeController timeCnt; // TimeController 스크립트 참조
점수처를 위한 변수를 추가한다.
// +++ 점수 추가 +++
public GameObject scoreText; // 점수 텍스트
public static int totalScore; // 점수 총합
public int stageScore = 0; // 스테이지 점수
싱글톤정적변수인 gameState를 체크해 게임중이라면 player게임오브젝트의 playerController스크립트의 score변수가 0이 아니라면 stageScore를 추가해주고 0으로 변경한후 점수를 처리하기 위한 UpdateScore()를 호출한다.
else if (PlayerController.gameState == "playing")
{
// 게임 중
GameObject player = GameObject.FindGameObjectWithTag("Player");
// PlayerController 가져오기
PlayerController playerCnt = player.GetComponent<PlayerController>();
// +++ 시간 제한 추가 +++
// 시간 갱신
if(timeCnt != null)
{
~시간 종료 처리
}
// +++ 점수 추가 +++
if (playerCnt.score != 0)
{
stageScore += playerCnt.score;
playerCnt.score = 0;
UpdateScore();
}
}
UpdateScore()에서는 public으로 연결된 scoreText게임오브젝트의 Text컴포넌트를 갱신해준다. 재미있는건 하이라키의 모든건 게임오브젝트라 Text마저도 GameObject이다. Text 내용을 변경하기 위해서는 귀찮아도 다시 GetComponent<Text>를 통해 test에 접근해야 한다
using UnityEngine.UI; 간 선언되어 있기 때문에 Text타입을 이용해 좀더 편하게 사용할 수도 있다.
public Text ScoreText;
ScoreText.text = score.ToString();
시간은 TimeController에서 관리되여 있고 여기서는 시간을 표시하고 게임을 관리하기 위해서 사용한다. 소스를 읽어보면 이해할수 있으리라 믿습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // UI を使うのに必要
public class GameManager : MonoBehaviour
{
public GameObject mainImage; // 이미지를 담아두는 GameObject
public Sprite gameOverSpr; // GAME OVER 이미지
public Sprite gameClearSpr; // GAME CLEAR 이미지
public GameObject panel; // 패널
public GameObject restartButton; // RESTART 버튼
public GameObject nextButton; // NEXT 버튼
Image titleImage; // 이미지를 표시하고있는 Image 컴포넌트
// +++ 시간 제한 추가 +++
public GameObject timeBar; // 시간 표시 이미지
public GameObject timeText; // 시간 텍스트
TimeController timeCnt; // TimeController
// +++ 점수 추가 +++
public GameObject scoreText; // 점수 텍스트
public static int totalScore; // 점수 총합
public int stageScore = 0; // 스테이지 점수
// Start is called before the first frame update
void Start()
{
// 이미지 숨기기
Invoke("InactiveImage", 1.0f);
// 버튼(패널)을 숨기기
panel.SetActive(false);
// +++ 시간 제한 추가 +++
// TimeController 가져옴
timeCnt = GetComponent<TimeController>();
if(timeCnt != null)
{
if(timeCnt.gameTime == 0.0f)
{
timeBar.SetActive(false); // 시간 제한이 없으면 숨김
}
}
// +++ 점수 추가 +++
UpdateScore();
}
// Update is called once per frame
void Update()
{
if (PlayerController.gameState == "gameclear")
{
// 게임 클리어
mainImage.SetActive(true); // 이미지 표시
panel.SetActive(true); // 버튼(패널)을 표시
// RESTART 버튼 무효화
Button bt = restartButton.GetComponent<Button>();
bt.interactable = false;
mainImage.GetComponent<Image>().sprite = gameClearSpr;
PlayerController.gameState = "gameend";
// +++ 시간 제한 추가 +++
if (timeCnt != null)
{
timeCnt.isTimeOver = true; // 시간 카운트 중지
// +++ 점수 추가 +++
// 정수에 할당하여 소숫점 버림
int time = (int)timeCnt.displayTime;
totalScore += time * 10; // 남은 시간을 점수에 더한다.
}
// +++ 점수 추가 +++
totalScore += stageScore;
stageScore = 0;
UpdateScore();// 점수 갱신
}
else if (PlayerController.gameState == "gameover")
{
// 게임 오버
mainImage.SetActive(true); // 이미지 표시하기
panel.SetActive(true); // 버튼(패널)표시하기
// NEXT 버튼 비활성
Button bt = nextButton.GetComponent<Button>();
bt.interactable = false;
mainImage.GetComponent<Image>().sprite = gameOverSpr;
PlayerController.gameState = "gameend";
// +++ 시간 제한 추가 +++
if (timeCnt != null)
{
timeCnt.isTimeOver = true; // 시간 카운트 중지
}
}
else if (PlayerController.gameState == "playing")
{
// 게임 중
GameObject player = GameObject.FindGameObjectWithTag("Player");
// PlayerController 가져오기
PlayerController playerCnt = player.GetComponent<PlayerController>();
// +++ 시간 제한 추가 +++
// 시간 갱신
if(timeCnt != null)
{
if (timeCnt.gameTime > 0.0f)
{
// 정수에 할당하여 소수점 이하를 버림
int time = (int)timeCnt.displayTime;
// 시간 갱신
timeText.GetComponent<Text>().text = time.ToString();
// 타임 오버
if (time == 0)
{
playerCnt.GameOver(); // 게임 오버
}
}
}
// +++ 점수 추가 +++
if (playerCnt.score != 0)
{
stageScore += playerCnt.score;
playerCnt.score = 0;
UpdateScore();
}
}
}
// 이미지 숨김
void InactiveImage()
{
mainImage.SetActive(false);
}
// +++ 점수 추가 +++
void UpdateScore()
{
int score = stageScore + totalScore;
scoreText.GetComponent<Text>().text = score.ToString();
}
}
카운트다운과 카운트업을 모두 할수 있는 스크립트를 만들고 이름을 TimeController로 합니다.
프로젝트뷰의 Prefab폴더에서 Canvs 프리팹을 더블클릭후 편집모드 상태에서 TimeController 스크립트를 어태치합니다.
분석을 해보면
변수
times가 시계다.
public bool isCountDown = true; // true= 카운트 다운으로 시간 측정
public float gameTime = 0; // 게엠의 최대 시간
public bool isTimeOver = false; // true= 타이머 정지
public float displayTime = 0; // 표시 시간
float times = 0; // 현재 시간
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TimeController : MonoBehaviour{
public bool isCountDown = true; // true= 카운트 다운으로 시간 측정
public float gameTime = 0; // 게엠의 최대 시간
public bool isTimeOver = false; // true= 타이머 정지
public float displayTime = 0; // 표시 시간
float times = 0; // 현재 시간
// Start is called before the first frame update
void Start(){
if (isCountDown){
// 카운트다운
displayTime = gameTime;
}
}
// Update is called once per frame
void Update(){
if (isTimeOver == false){
times += Time.deltaTime;
if (isCountDown){
// 카운트다은
displayTime = gameTime - times;
if (displayTime <= 0.0f){
displayTime = 0.0f;
isTimeOver = true;
}
}else{
// 카운트업
displayTime = times;
if (displayTime >= gameTime){
displayTime = gameTime;
isTimeOver = true;
}
}
Debug.Log("TIMES: " + displayTime);
}
}
}
게임에 시간제한 UI 추가하기
Prefabs폴더의 Canvas를 선택하고 인스펙트뷰의 Open을 클릭한다.
하이라키의 +를 클릭해서 UI>Image를 2개 추가하고 이름을 TimeBar, Score로 한다
ItemData.cs 스크립트를 작성에 어태치해준다. 단순히 플레이어가 먹었을때 올라가는 점수값 변수만 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemData : MonoBehaviour {
public int value = 0; // 정수값을 설정할 수 있다
}
이제 이걸 프로젝트뷰 프리팹폴더로 끌어다 놓는다.
Ctrl-D로 3개 더 만든후 이름을 Item_blue, Item_green, Item_red, Item_white로 바꾸고
Stage1과 마찬가지고 Prefab 폴더의 배경및 지면과 플레이어 캐릭터를 배치하고 가로방향으로 화면이 스크롤 될 수 있도록 화면의 두배 크기로 스테이지를 배치합니다. Stage1을 복사하거나 다른이름으로 변경하면 쉽게 만들 수 있습니다. 이름은 BaseScene으로 합니다.
설정시 UI가 있어 편집이 어려우면 하이라키의 숨김기능이나 인스펙터의 비활성화를 하면 됩니다.
먼저 블록과 구멍을 없애고 지면을 여섯 개 나열합니다. 목표지점은 화면 오른쪽 끝에 배치하고 Collider의 EditCollider를 재 설정해 Wall Object은 좌우끝에 DeadZone은 아래쪽에 길게 배치합니다.
Ctrl-S로 저장합니다. 이름이 BaseScene이 아니면 이름도 바꿔줍니다. 게임에 직접사용되지는 않고 앞으로 새로운 씬을 만들때 템플릿으로 사용할겁니다.
카메라를 관리하는 스크립트 추가하기
게임을 하면 화면이 캐릭터에 따라가는 장면을 보셨을겁니다.
가장 간단한 방법은 Main Camera를 player의 자식오브젝트로 만들면 됩니다.
여기서는 스크립트를 짜서 구현해 보겠습니다.. CameraManager.cs를 만듭니다.
변수 : 카메라의 상하좌우 리미트를 정하는 변수 입니다.
실제 카메라의 위치를 캐릭터에 고정해주는 법은 캐릭터의 x,y좌표를 카메라에 복사해주면 됩니다. 여기에 조금더 처리를 추가해 줍니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraManager : MonoBehaviour
{
// Update is called once per frame
void Update() {
GameObject player = GameObject.FindGameObjectWithTag("Player"); // 플레이어 찾기
if(player != null) {
float x = player.transform.position.x;
float y = player.transform.position.y;
float z = transform.position.z; // z값은 카메라 자신을 사용합니다.
Vector3 v3 = new Vector3(x, y, z); // 카메라 위치의 Vector3 만들기
transform.position = v3;
}
}
Update() :
FindGameObjectWithTag("Player")로 Player를 찾습니다. 매번 찾는 이유는 Player가 Null인지 체크해주기 위해서입니다.
하이라키의 player를 선택하고 인스펙터뷰에서 Tag를 Player로 선택해줍니다.
Player가 Null이 아니라면 Player위치를 위치제한검사및 스크롤 처리를 해줍니다. 처리를 마치면 (x,y,z)를 카메라의 위치로 갱신합니다.
Vector3 v3 = new Vector3(x, y, z); // 카메라 위치의 Vector3 만들기 transform.position = v3;
스크립트를 MainCamera에 끌어다 어태치합니다.
이제 스크립트의 RightLimit값을 알아보겠습니다. 플레이를 시키고 Scene뷰탭을 클릭해서 씬뷰모드로 변경합니다.
하이라키에서 MainCamera를 선택하고 이동툴을 이용해 X축으로 끌어 화면 오른쪽끝이 포함되게 이동시켜봅니다. 전 약 18정도가 나왔습니다. 이 값을 스크립트의 RightLimit값으로 설정합니다. 플레이를 멈추면 다시 원상태로 됩니다.
플레이후 카메라위치를 바꾼이유는 플레이를 정지하면 변경한값들을 다시 원래 상태로 돌려주기 때문입니다. 게임 개발시 상당히 유용합니다.
하이라키의 back(배경화면)을 끌어다 카메라에 붙여줍니다.
게임을 실행해보면 캐릭터가 중간쯤 오면 화면이 스크롤하기 시작해서 Goal이 보이는 오른쪽 끝이 보이면 스크롤을 멈춥니다. 이때 배경은카메라를 따라 다니기 때문에 항상 멈춰 있는 것 처럼 보입니다. 잘 생각해보면 캐릭터>카메라>배경 3종류가 같이 움직이고 Ground만 스크롤 되는 것입니다. 좀더 생각해보면 실제 Ground가 스크롤 되는게 아니라 카메라가 오른쪽으로 움직일 뿐입니다. Ground가 스크롤 되는 시점의 씬뷰를 보면 카메라의 위치가 가운데로 이동해서 찍고 있죠.
그리고 신기한건 캐릭터가 움직이다가 화면 중간쯤 오면 스크롤이 시작됩니다. 이건 이동제한 로직 때문입니다.
// 양 끝에 이동 제한 적용
if (x < leftLimit) {
x = leftLimit;
} else if (x > rightLimit) {
x = rightLimit;
}
게임 시작시 캐릭터의 위치는 저의 경우 -7.44입니다. leftLimit는0이므로 캐릭터의 위치가 0이 될때까지는 x는 0으로 고정되죠. 따라서 카메라의 위치도 0으로 고정되서 안움직이는 겁니다. RigitLimit는 아까 오른쪽 골이 보이는 위치로 지정해 주었으니 오른쪽 골이 보이면 RightLimit에 고정되는거죠.
배경에 다중 스크롤 적용하기
현재 배경이 카메라에 고정되어 움직이지 않는것 처럼 보입니다. 그 위에 화면보다 조금 큰 배경 이미지를 표시하고 조금 느리게 스크롤 되도록 만들어 봅시다. 다중 스크롤 되면 게임 화면에 깊이감이 더해집니다.
하이라키에 빈게임오브젝트를 만들고 이름을 SubScreen으로 합니다. SubScreen의 Transform>Position의 X,Y,Z값은 0으로 리셋합니다. 프로젝트뷰에서 배경이미지 back2를 두번 끌어다 놓고 자식으로 만듭니다. back2중 하나를 옆으로 붙여 길게 만들어 줍니다. Back2 두개를 다 선택하고 Sprite Rendere의 Order in Layer를 1로 만들어 배경보다 위쪽으로 해줍니다.
CameraManager.cs 맨 밑에 subScreen 처리가 있습니다. x속도를 반으로 해서 효과를 줍니다. 또 하이라키의 SubScreen을 끌어다 MainCamera의 CameraManager스크립터 컴포넌트의 subScreen 참조에 연결해줍니다.
if (subScreen != null) {
y = subScreen.transform.position.y;
z = subScreen.transform.position.z;
Vector3 v = new Vector3(x / 2.0f, y, z);
subScreen.transform.position = v;
}
플레이 해보면 subScreen이 플레이어보다 느린 속도로 스크롤 되네요.
강제스크롤 게임오버 처리
게임의 긴박감을 주기 위해 자동으로 횡스크롤을 하게 만들겠습니다. 캐릭터의 위치를 참조하지 않습니다.
하이라키의 DeadZone과 Wall을 카메라의 자식으로 만듭니다. Wall의 오른쪽 충돌 위치를 화면의 오른쪽으로 바꿔줍니다. 화면 오른쪽으로 가면 캐릭터가 스크롤을 못하게 하기 위해서입니다. MainCamera의 CameraManager 스크립트 컴포넌트의 IsForceScrollX만 체크해 줍니다.
그럼 스크립트의 다음 부분이 처리 되어 자동으로 횡스크롤 합니다.
// 가로 방향 동기
if (isForceScrollX) {
// 가로 강제 스크롤
x = transform.position.x + (forceScrollSpeedX * Time.deltaTime);
}
오른쪽 화면 끝으로 이동하면 더이상 못 나가는건 스크립트가 아닌 아까 Wall의 Collider위치를 화면 오른쪽으로 배치해서 그럽니다.
동작을 확인하셨으면 File>SaveAs를 선 BaseForcedScrollStage라는 이름으로 씬을 저장합니다. 나중에 강제 스크롤 스테이지용 템플릿으로 사용합니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraManager : MonoBehaviour
{
public float leftLimit = 0.0f; // 왼쪽 스크롤 제한
public float rightLimit = 0.0f; // 오른쪽 스크롤 제한
public float topLimit = 0.0f; // 위 스크롤 제한
public float bottomLimit = 0.0f; // 아래 스크롤 제한
public GameObject subScreen; // 서브 스크린
public bool isForceScrollX = false; // X 축 강제 스크롤 플래그
public float forceScrollSpeedX = 0.5f; // 1초간 움직일 X의 거리
public bool isForceScrollY = false; // Y 축 강제 스크롤 플래그
public float forceScrollSpeedY = 0.5f; // 1초간 움직일 Y의 거리
// Start is called before the first frame update
void Start() {
}
// Update is called once per frame
void Update() {
GameObject player = GameObject.FindGameObjectWithTag("Player"); // 플레이어 찾기
if(player != null) {
// 카메라의 좌표 갱신
float x = player.transform.position.x;
float y = player.transform.position.y;
float z = transform.position.z;
// 가로 방향 동기
if (isForceScrollX) {
// 가로 강제 스크롤
x = transform.position.x + (forceScrollSpeedX * Time.deltaTime);
}
// 양 끝에 이동 제한 적용
if (x < leftLimit) {
x = leftLimit;
} else if (x > rightLimit) {
x = rightLimit;
}
// 세로 방향 동기
if (isForceScrollY) {
// 세로 강제 스크롤
y = transform.position.y + (forceScrollSpeedY * Time.deltaTime);
}
// 위 아래에 이동 제한 적용
if (y < bottomLimit) {
y = bottomLimit;
}
else if (y > topLimit){
y = topLimit;
}
Vector3 v3 = new Vector3(x, y, z); // 카메라 위치의 Vector3 만들기
transform.position = v3;
// 서브 스크린 스크롤
if (subScreen != null) {
y = subScreen.transform.position.y;
z = subScreen.transform.position.z;
Vector3 v = new Vector3(x / 2.0f, y, z);
subScreen.transform.position = v;
}
}
}
}
하나의 씬에서 시작이미지, UI등을 껏다 켰다 했지만 이제는 타이틀, 종료, 게임씬등을 분리해 보겠습니다. 스테이지도 여러게 만들어야 하고 본격적인 씬관리가 필요합니다.
타이틀 화면 씬 만들기
현재씬은 Stage1입니다. 그렇지 않은 분은 이름을 Stage1로 바꿉니다.
현재상태에서 Ctrl-S로 저장합니다.
File>New Scene을 선택 2D씬을 선택 새로 만듭니다.
아직 저장되지 않았으므로 File>Save를 선택한후 팝업창에서 Title로 Scene폴더에 저장합니다.
타이틀 씬을 빌드에 추가하기
File>Build Setting을 열고 Title씬을 드래그드랍해서 추가합니다. 주의할 점은 타이틀이 맨 위로 오개 합니다. 실행시 위에서 부터 로딩되기 때문입니다.
타이틀화면 UI만들기
하이라키뷰에서 +를 눌러 UI>Image를 추가합니다. 하이라키에서 Canvas를 선택하고 Render Mode를 Screen Space-Camera로 설정하고 Rendere Camera 를 Main Camera로 선택해 줍니다.
배경배치하기
Image를 선택하고 이름을 BackImage로 변경후, Images폴더의 title_back을 끌어다 Image컴포넌트의 SourceImage로 끌어다 줍니다. 화면에 꽉차지 않으면 transform Anchor Presets+ALT를 누르고 stretch를 선택해 줍니다.
UI>Image를 2개 더 추가해주고 CharaImage, LogoImage로 이름을 변경해줍니다. Image폴더에서 title_chra와 title_logo를 설정합니다. 위치는 그림처럼 합니다. 각이미지의 원본 비율을 사용하기 위해서는 Image컴포넌트의 Preserve Aspect를 사용할 필요도 있습니다.
스타트 버튼 배치하기
하이라키뷰 +를 눌러 UI>Legacy>Button을 추가후 이름을 StartButton으로 바꾸고 Image폴더에서 button이미지를 끌어다 Image컴포넌트에 놓습니다. Preserve Aspect를 체크, Set Native Size 클릭후, 하이라키에서 StartButton을 펼쳐 Text를 선택하고 인스펙터뷰에서 Text컴포넌트에서 START로 변경하고 사이즈를 64로 변경합니다.
타이틀 화면에서 게임 화면으로 이동하기
마지막으로 [START] 버튼을 눌렸을때 게임 씬인 Stage1으로 이동하는 처리를 구현하겠습니다. 5장에서 만든 ChangeScene스크립트를 StartButton에 어태치해줍니다.
게임 클리어되었을때 NEXT버튼이나 게임오버시 RESTART 버튼을 사용할 수 있도록 해보겠습니다.
씬 변경 스크립트 만들기
프로젝트뷰 +를 클릭해 ChangeScene이름의 스크립트를 만듭니다.
씬매니저를 사용하기 위해 using UnityEngine.SceneManagement; 를 선업합니다.
불러올 씬을 참조하기 위해 public string SceneName; 변수를 선언합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ChangeScene : MonoBehaviour
{
public string SceneName; //불러올씬
// Start is called before the first frame update
void Start(){
}
// Update is called once per frame
void Update(){
}
public void Load() {
SceneManager.LoadScene(SceneName);
}
}
게임 전반을 관리하는 GameManager를 만들겠습니다. 프로젝트뷰의 +를 클릭해 C# Script를 만들고 GameManager라 이름 변경합니다. 아이콘이 톱니바퀴모양으로 됩니다. 이걸 끌어다 하이라키의 Canvas에 끌어다 어태치합니다.
GameManager 스크립트를 다음과 같이 작성합니다.
UI를 컨트롤 하기 위해 다음과 같이 선언합니다.
using UnityEngine.UI; // UI 를 사용할 때 필요
GUI를 다루기 위해 변수를 선언합니다. 아까 만들었던 패널 버튼 이미지등입니다.
public GameObject mainImage; // 이미지를 담아두는 GameObject
public Sprite gameOverSpr; // GAME OVER 이미지
public Sprite gameClearSpr; // GAME CLEAR 이미지
public GameObject panel; // 패널
public GameObject restartButton; // RESTART 버튼
public GameObject nextButton; // NEXT 버튼
Image titleImage; // 이미지를 표시하고있는 Image 컴포넌트
보통은 시작화면을 게임씬과 분리하여 독립된 씬으로 사용하는데 여기서는 게임씬안에서 시작시 감추네요
원래 런타임시의 객체에 접근하려면 Public으로 참조하던지 Find해야하는데PlayerController는 아무런 참조없이 그냥 사용하고 있습니다. 이건gameState를 static으로 선언했기 때문입니다.PlayerController의 gameState외 다른것들은 꼭 연결후 사용하셔야 합니다. 일종의 싱글턴 기법입니다.
GameOver일시 NEXT버튼의 interactable변수를 false로 해 비활성 상태로 만듭니다. 또한 PlayerController.gameState=gameend로 설정해 다음 프레임부터 아무런 처리도 안하게 만들어 줍니다.
if (PlayerController.gameState == "gameClear"){
~생략
}
else if (PlayerController.gameState == "gameOver"){
~생략
}
else if (PlayerController.gameState == "playing"){
// 게임 중
}
이제 하이라키에서 Canvas를 선택하고 GameManager의 변수들을 연결해야합니다. 하이라키의 Cavans에서 Image, Panel, RestartButton, NextButtong을 다음과 같이 끌어서 연결합니다. 이후 Images폴더에서 GameOver, StageClear 스프라이트를 끌어다 놓와줍니다.
GameManager의 전체코드 입니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // UI 를 사용할 때 필요
public class GameManager : MonoBehaviour
{
public GameObject mainImage; // 이미지를 담아두는 GameObject
public Sprite gameOverSpr; // GAME OVER 이미지
public Sprite gameClearSpr; // GAME CLEAR 이미지
public GameObject panel; // 패널
public GameObject restartButton; // RESTART 버튼
public GameObject nextButton; // NEXT 버튼
Image titleImage; // 이미지를 표시하고있는 Image 컴포넌트
// Start is called before the first frame update
void Start() {
Invoke("InactiveImage", 1.0f); // 이미지 숨기기
panel.SetActive(false); // 버튼(패널)을 숨기기
}
// Update is called once per frame
void Update()
{
if (PlayerController.gameState == "gameClear")
{
// 게임 클리어
mainImage.SetActive(true); // 이미지 표시
panel.SetActive(true); // 버튼(패널)을 표시
// RESTART 버튼 무효화
Button bt = restartButton.GetComponent<Button>();
bt.interactable = false;
mainImage.GetComponent<Image>().sprite = gameClearSpr;
PlayerController.gameState = "gameEnd";
}
else if (PlayerController.gameState == "gameOver")
{
// 게임 오버
mainImage.SetActive(true); // 이미지 표시
panel.SetActive(true); // 버튼(패널)을 표시
// NEXT 버튼 비활성
Button bt = nextButton.GetComponent<Button>();
bt.interactable = false;
mainImage.GetComponent<Image>().sprite = gameOverSpr;
PlayerController.gameState = "gameEnd";
}
else if (PlayerController.gameState == "playing")
{
// 게임 중
}
}
void InactiveImage() {
mainImage.SetActive(false); // 이미지 숨기기
}
}
하이라키 + 를 누르고 UI>Image를 클릭한다. Canvas를 부모로 Image가 생기고 EventSystem까지 생겼다. ㅠㅠ
Canvas 표시 설정
하이라키의 Canvas를 더블클릭해서 포커스해보면 게임화면 보다 훨씬 큰 크기입니다.
현재 Canvas의 Renderer Mode는 Screen Overlay라 결국 최종 출력위에 오버레이 되는 거라 출력은 게임화면 사이즈를 무시하고 현재 이 상태가 오버레이되지만 게임화면과 좀더 직관적인 편집을 위해 모드를 Screen Space - Camera로 변경합니다. 나중에 다시 변경해도 문제없습니다. Renderer Camera를 하이라키의 Main Camera를 끌어다 놓거나 ◉검색브라우저를 눌러 찾아도 됩니다.
이제 하이라키의 Canvas를 더블클릭해서 포커스를 주면 게임화면과 같이 보이게 되나 이미지가 보이지 않습니다. 문제는 Order in Layer입니다. 이걸 10으로 변경해서 가장 위로 위치 시킵니다.
Overlay Mode에서는 무조건 게임씬 맨 위로 오버레이되기 때문에 이런 문제가 없습니다. 여기서 다루지 않은 World Space모드는 Cavas위치를 게임내 3D 월드공간 특정 위치에 배치됩니다. 따라서 자유도는 있지만 게임출력화면에 딱 붙어 있지 않으므로 잘 조절해야 합니다.
현재 이미지는 Cavas 좌하에 위치하고 있습니다. 중앙으로 옮겨보겠습니다. 하이라키의 Image를 클릭한후 손으로 옮겨도 되겠지만 좀더 편한 방법이 있습니다. 인스펙터뷰의 Transform아래 좌표그림을 눌러봅니다. Anchor 프리셋이 나타납니다. 잘 읽어보면 Alt를 누를 경우 set position을 바꿀수 있습니다. Alt를 누르면 화면이 바뀌고 가운데를 누르면 위치가 가운데로 변경됩니다.
Image가 선택되어져 있는 상태에서 프로젝트뷰 Images폴더에서 GameStart이미지를 Image컴포넌트의 Source Image에 끌어다 가져다 놓으면 표시가 바뀝니다.
그런데 사이즈가 좀 작고 글씨가 뭉쳐보이지요 같은 Image컴포넌트 내 PreserveApect를 체크하고 Set Native Size를 클릭하면 보기좋게 바뀝니다.
버튼 UI 추가하기
게임이 끝난 후 재시작하기 위한 RESTART버튼을 추가합니다. 버튼도 UI오브젝트이기에 Cavas안에 배치합니다.
계층 뷰의 Canvas를 선택하고 +>UI>Legacy>Button을 선택합니다. 이름을 RestartButton으로 변경합니다.
Images폴더내 Button스프라이트를 끌어다 Image컴포넌트의 Source Image로 끌어다 주고 Preserve Aspect 체크 Set Native Size를 클릭후 화면에 맞게 적당히 사이즈를 조절해줍니다.
하이라키의 RestartButton을 펼쳐보면 Text가 숨어 있습니다. RestartText로 이름을 변경해주고 인스펙터뷰의 Text컴포넌트에서 RESTART로 Text내용을 바꿔주고 사이즈도 적당히 바꿔줍니다.
하이라키뷰에서 RestartButton를 선택후 Ctrl-D를 눌러 복사한후 이름을 NextButton으로 변경한후 펼치후 Text를 인스펙터뷰에서 NEXT로 바꿉니다. RestartButton을 선택후 위치도 적당히 아래로 내려줍니다. (위치는 Text가 아닌 Button을 해야 함께 움직입니다.)