Prosto

따라하는 유니티 2D 프로젝트ⓐ -8 본문

Programing/Unity 3D

따라하는 유니티 2D 프로젝트ⓐ -8

Prosto 2016.09.21 00:18

따라하는 유니티 2D 프로젝트ⓐ 강좌 여덟 번째 시간입니다.

 

이번에 다룰 내용은 추가적인 UI 관련된 작업과 추가적인 부분들,

일시정지와 계속하기, 재시작, 게임종료에 대한 부분들을 다룰 것입니다.

소팅 레이어(레이어 순서 정렬) 등의 내용도 중간에 포함되어 있습니다..

 

 

그럼 시작하겠습니다.

 

가장 먼저 저번에 작업했던 프로젝트를 실행합니다. 그리고 순서대로 직접 해보며 따라오시면 됩니다.

 

먼저 일시정지 버튼을 추가하기 위하여 Canvas - NormalUI를 선택합니다.

 

NormalUI에서 마우스 우측 클릭을 한 후 UI - Button 순으로 선택합니다.

 

 

Button이 생성되면

Inspector창에서 Anchor Presets를 top/right로 선택해줍니다.

 

그리고 pos와 width,height도 알맞게 설정해줍니다.

 

이렇게 이름도 PauseBt으로 바꿔주도록 하고요.

 

PauseBt 하위의 Text를 수정하도록 하겠습니다.

Text를 선택하여

입력된 글자를 Pause로 바꿔주세요.

 

그리고 게임을 실행해보면

정상적으로 버튼이 등록되어있습니다. 클릭도 되고요.

(UI로 스코어와 마찬가지로 고정되어있음)

 

그런데 이때, Pause를 누르면 멈추지는 않고 볼이 그대로 점프해버리죠?

일단 우리는 아직 Pause 일시정지에 대한 기능을 만들지도 않고 버튼만 만들었으니

멈추지 않는 것은 맞습니다만, UI를 눌렀는데 화면 클릭으로 점프를 하는 건 옳지 않겠죠?

이따가 그 부분에 대한 처리도 추가하니 끝까지 따라해보세요.

 

(여기서 잠깐, 관계 없는 부분이지만 Assets 폴더 정리를 위하여 잠시 다른 내용이 나옵니다...)

지금보니 System 폴더에 저번에 만든 애니메이션들이 있네요.

애니메이션, 애니메이터 모두 선택하여

Animations 폴더로 드래그해줍니다.

 

Animations 폴더에 정상적으로 들어간 모습입니다.

이렇게 애니메이션이나 모든 파일들은 Project탭의 디렉토리에서 쉽게 옮길 수 있습니다.

(끝났습니다. 다시 원래 내용으로 돌아옵니다.)

 

 

새로운 UI에 관련된 스크립트를 작성하기 위하여

우선 Scripts 하위에 UI폴더를 만들어줍니다.

그리고 UIevent라는 스크립트를 생성해주세요.

 

 

해당 스크립트에 들어가

이렇게 간단하게 스크립트를 작성해줍니다.

Time.timeScale은 게임 내의 시간이 흐르는 속도와 관계가 있는 변수입니다.

timeScale은 시간의 크기입니다.

만약 timeScale이 1이면 보통(1배속)이고,

 timeScale이 1.5이면 보통(1.5배속)이고,

timeScale이 0이면 보통(0배속)이겠죠?

 

 

해당 스크립트를 버튼 클릭 시 사용하기 위하여

우선 GameManager를 선택하고

컴포넌트를 추가해줍니다.(드래그 앤 드랍)

 

 

이번에는 Canvas - NormalUI - PauseBt을 선택하여줍니다.

Inspector 창에서 가장 밑에 Button(Script) 부분을 확인해주세요.

On Click()이라는 부분이 클릭했을 때 어떠한 처리를 해줄지에 대한 부분입니다.

'+'를 클릭하면 새로운 처리를 등록해줄 수 있습니다.

눌러주세요.

 

'+'를 누르면 이러한 모습이 됩니다.

 

여기 Object 자리에 우리는 GameManager를 넣어주도록 합니다.

여기서도 마찬가지로 드래그 앤 드랍하시면 됩니다.

 

그러면 실제로 우리가 작성한 스크립트를 등록해볼까요?

No Function이라고 있던 부분을 크릭하여

UIevent - ActivePauseBt() 순으로 선택해주세요.

 

정상적으로 등록이 완료된 모습입니다.

이렇게 간단하게 버튼에 이벤트를 추가해줄 수 있습니다.

 

 

이제 실제로 게임에서 Pause 버튼을 눌러보면

게임이 멈추는 것을 확인할 수 있습니다.

하지만 게임이 멈췄으면, 다시 시작할 수 있게도 해줘야겠죠?

 

스크립트를 추가해주도록 합시다.

pauseOn이라는 bool을 이용하여 현재 상태가 '멈춤'인지 '진행 중'인지 판단하여

그에 맞는 처리를 해줄 수 있도록 만들었습니다.

마지막에 pauseOn = !pauseOn;으로

일시정지를 누를 때마다 상태가 반대로 바뀌도록 만들어줬죠.

 

이제 실행해보면 일시정지 후 같은 버튼을 다시 누르면 풀리는 것을 확인할 수 있습니다.

일시정지를 누르면 이렇게 멈췄다가

다시 누르면

이렇게 게임이 계속 진행됩니다.

 

여기까지만 해도 일단 일시정지 기능을 구현한 거죠?

 

하지만 우리는 조금 더 나가보도록 하겠습니다.

일시정지 화면을 평상 시 화면과 다르게 표현해주고 싶습니다.

일시정지는 어두운 화면에 계속하기, 다시시작, 종료 같은 버튼을 두도록 하겠습니다.

 

자 다시 시작해봅시다.

새로운 패널을 추가해주기 위하여

Canvas에 마우스 우측 클릭을 하여 UI - Panel 순으로 선택합니다.

 

추가된 패널에 RectTransform의 Scale과 Image의 Color를 변경해주세요.

Scale은 x와 y가 1이면 테두리 경계가 살짝 보여서

화면 전체를 가릴 때는 약간만 더 크게 만들어줍니다.

Color는 알파값이 들어가 현재 진행중인 게임이 어둡게 보이도록 합니다.

 

좌측 화면을 보시면 PauseUI 패널로 어두워진 것을 볼 수 있습니다.

이번에는 PAUSE라는 텍스트를 추가해볼까요?

PauseUI 우측 클릭 -> UI - Text 순으로 선택해주세요.

 

생성된 텍스트에 이름을 변경하고

RectTransform 값 수정, Text(Script)에 체크된 부분들을 변경해주세요.

(Text에 대한 부분들은 저번에 Score 텍스트를 할 때 설명했죠?)

 

이번에는 버튼을 추가해보도록 하죠.

PauseUI 우측 클릭 - UI - Button 순으로 선택해줍니다.

 

그리고 추가된 버튼에 이런식으로 위치와 크기, 색깔을 정해주고

하위에 있는 Text도 수정해줍니다.

(버튼은 생성하면 자동으로 하위에 Text를 가지고 있습니다. 필요 없으면 제거해주면 되겠죠.)

이렇게 Text 문구, Size, Alignment를 수정해줍니다.

 

그러면 이렇게 만들어진 ContinueBt을 확인해볼 수 있습니다.

(지금 제가 만든 것과 색깔이나 크기, 문구가 같을 필요는 없습니다.)

 

 

다음 버튼을 만들기 위하여

ContinueBt을 선택한 후 Ctrl+C -> Ctrl+V를 해줍니다.

그러면 ContinueBt(1)이 생성된 것을 확인할 수 있습니다.

 

ContinueBt(1)을 약간 아래로 이동시킨 후

색깔, Text 내용을 수정해줍니다.

 

그 후 방금 전 작업과 같이 처리해서

Quit 버튼도 만들어주도록 하죠.

 

이렇게 여기까지하면

PauseUI 하위에 PAUSE 텍스트와 세 가지 버튼이 만들어졌습니다.

(Game 패널서 보이는 화면)

 

그리고 PauseUI를 선택한 후 Inspector창의 체크를 해제하면

Game과 Scene 패널에서 안 보이는 것을 확인해보세요.

 

실제 게임에서 사용할 때는 이렇게 활성화, 비활성화로 필요한 것들만 보여주면 됩니다.

 

여기까지 했다면 Ctrl + S를 눌러 현재 작업된 Scene을 저장해줍시다.

이런 UI 작업 같은 Scene에 관련된 Hierarchy 영역 작업은

ctrl+s로 자주 저장해줘야 합니다.

가끔 오류가 발생해서 작업한 내용이 모두 날아가면 안되겠죠?

 

그럼 이번에는 작업된 씬을 빌드 셋팅에 추가해볼까요?

상단 메뉴의 File - Build Settings를 선택합니다.

 

나오는 창에서 Add Open Scenes 버튼을 누르면

Scenes In Build 영역에 추가된 모습을 볼 수 있습니다.

 

일단 여기까지만 해두고 X 버튼을 눌러 원래 작업 화면으로 돌아가죠.

 

(여기서부터 Sorting Layer 부분입니다. 중간에 잠깐 지금까지 정렬 안 해둔 레이어들을 정렬했습니다..)

-> UI가 가장 위쪽에 보여야하기 때문에 말이죠.

Canvas를 선택하여 우측의 Inspector 부분을 보면

Canvas - Sorting Layer 부분을 볼 수 있습니다.

(소팅레이어는 스프라이트나 이미지 글자 관련 부분에 그래픽에 대해 우선적으로(더 위쪽으로) 보여주기 위한 순서를 정합니다.)

 

Default 부분을 눌러 Add Sorting Layer를 선택해주세요.

 

이렇게 Inspector 패널이 바뀌었다면

'+'를 눌러 Layer를 추가할 수 있습니다.

두 번 눌러 레이어 두 개를 추가하고,

위쪽엔 Object, 아래엔 UI를 입력해주세요.

( 아래로 갈수록 우선순위가 높습니다.(더 위에 그려집니다.) )

 

이렇게 Canvas는 UI로 바꿔주시고요.

 

Hierarchy에서 Ball을 선택하여 Object로 바꿔주세요.

 

Assets - Prefabs에서 obstacle을 선택하여 Object로 바꿔주세요.

(여기까지 Sorting Layer에 대한 부분은 끝입니다.)

 

자, 이제 일시정지 화면(패널)도 구성했겠다.

스크립트를 수정해볼까요?

일반 플레이 중일 때는 NormalUI(Panel)가 보여지고,

일시정지 중일 때는 PauseUI(Panel)가 보여지도록 말이죠.

 

아까 작업하면 UIevent.cs 스크립트를 열어주세요.

이렇게

GameObject로 패널들을 받아옵니다.

( 여기서 Find로 Canvas를 찾고 transform을 받아 findChild로 얻어온 이유는

GameObject.Find의 경우는 활성화된 오브젝트만 찾을 수 있습니다.

하지만 transform을 통한 자식은 활성화/비활성화 모두 찾아갈 수 있기 때문이죠. )

 

그리고 기존의 ActivePauseBt 부분을 수정해주고,

RetryBt과 QuitBt도 만들어줍니다.

 

패널GameObject를 각 상황에 맞게 SetActive(true)를 이용해서 보였다 숨겼다 하는 게 보이시죠?

 

또, RetryBt 부분의

Application.LoadLevel(씬이름);을 이용하면 씬을 다시 불러올 수 있습니다.

처음부터 시작하는 거죠.

 

(이렇게 씬로드 시 static이나 싱글톤(singleton), DontDestroy 등은 별도로 처리해줘야 합니다..)

- 지금 우리랑은 관계 없는 얘기지만요..

 

그런데 보니까 CS0618 경고로 저희가 쓰려는 Application.LoadLevel(string)이 구식이니까

Scenemanager.LoadScene을 사용하라네요..

그러면 한번 바꿔봅시다.

이렇게 상단에 using UnityEngine.Scenemanagement;

를 입력해줍니다.

 

그리고 기존의 Application.LoadLevel(string); 부분을

SceneManager.LoadScene(string);으로 바꿔줍니다.

 

자 이제 여기까지 했으면 스크립트도 작성이 완료됐으니,

마지막으로 버튼에 연결만 시켜주면 되겠네요.

아까 Pause 버튼에 했던 것과 같은 방법입니다.

그럼 하나씩 등록해주죠.

먼저 ContinueBt입니다.

아까처럼 Button(Script) 부분의 On Click()의 '+'를 눌러 한 칸을 만든 후에

GameObject를 Object칸에 넣어주고,

스크립트를 UIevent - ActivePauseBt 순으로 선택해주면 됩니다.

(아까 PauseBt과 똑같습니다.)

 

다음으로 RetryBt은 다 똑같지만,

함수를 다른 것을 쓰죠.

여기는 UIevent - RetryBt 순으로 선택해주세요.

 

다음으로 QuitBt도 다 똑같지만,

함수를 다른 것을 쓰죠.

여기는 UIevent - QuitBt 순으로 선택해주세요.

그러면 연결이 끝났으면 실행해서 결과를 보겠습니다.

실행하고 Pause를 눌러보세요.

 

이렇게 제대로 결과가 나왔죠?

Hierarchy 부분을 보면 Canvas 하위에 NormalUI는 비활성화가 되고,

PauseUI는 활성화된 모습을 확인할 수 있습니다.

 

그리고 Continue를 눌러봅시다.

이렇게 원래 화면으로 돌아오는 것을 볼 수 있습니다.

Canvas 하위의 NormalUI와 PauseUI도 서로 활성/비활성화가 바뀌었죠?

 

다른 테스트를 위하여 Pause 버튼을 다시 누릅니다.

이번에는 Retry를 할 텐데요. 위쪽 #Scene을 보세요.

누르기 전에는 장애물들이 있는 게 보이죠?

 

누르고 난 후는 처음으로 돌아온 것을 확인할 수 있습니다.

스코어도 0으로 바뀌었고, 시작점으로 돌아갔습니다. (장애물도 아직 안 나왔죠.)

 

여기까지 했으면 이제 제대로 된 일시정지 화면을 만든거죠?

UI는 글씨보다는 아이콘이면 더 보기 좋겠죠?

사용 방법은 똑같습니다.

단지 텍스트는 제거하고 컴포넌트 중 이미지를 추가해줘야겠죠.

 

 

그러면 마지막으로 UI 부분을 눌렀을 때는 클릭 이벤트 처리를 하지 않도록

예외 처리를 해보도록 할까요?

UI를 누르면 UI처리만 해줘야 정상 작동이니까요.

 

PlayerAction.cs에 마우스 클릭 이벤트 처리가 있었죠?

열어서 수정해줍시다.

 

먼저

using UnityEngine.EventSystems;를 추가해줍니다.

이벤트 시스템에 대한 처리를 하기 위해 using이 필요합니다.

 

Update() 부분이 좀 길어졌죠?

크게 복잡한 부분은 없을 것 같습니다만...

가장 먼저 jumpOn 확인이고,

마우스 버튼 다운과 업을 확인하고

이때 마우스 포인트가 UI 위에 있지 않다면 원래와 같이 실행합니다.

 

여기에 추가로 달아준 부분은 버튼을 땔 때 추가 처리로 달린 부분들입니다.

UI에서 클릭을 시작했다가 게임 화면에서 땐다면? 이때는 점프가 정상처리 될 필요가 없겠죠.

그 부분이 if(checkTime > 0)입니다. checkTime이 0보단 커야 점프동작을 시키겠다는 거죠.

또, UI 밖에서 클릭을 시작했는데 UI에서 땐다면? 이때는 그 전에 카운팅하던 수치를 종료해줘야겠죠?

그 부분이

if(checkTime > 0) { StopCoroutine("CheckButtonDownSec"); checkTime = 0;} 입니다.

 

이제 실행해봅시다.

일시정지를 눌러도 볼이 그대로 바닥에 붙어있고,

일시정지를 풀어도 점프를 진행하지 않는 것을 볼 수 있습니다.

이렇게 UI에서 터치가 안 되는 것까지 처리했습니다.

 

 

 

지금까지 따라하는 유니티 2D 프로젝트ⓐ -8번 강좌를 보셨습니다.

이번 시간에는 간단하게 만드는 일시정지 기능을 중점으로 작업을 진행했습니다.

이제 "일시정지 - 계속하기 - 다시시작 - 종료" 같은 UI 구성이나,

그와 비슷한 추가적인 UI들도 만들 수 있겠죠?

또한 필요할 때 UI들을 가지고 있는 패널을 바꿔보여줘 다양한 표현도 가능할 테고요.

(소팅 레이어, 폴더 정리도 잠깐 나왔습니다만... 이번 주제는 아니라..)

 

 

다음 유니티 2D 프로젝트ⓐ의 아홉 번째 강의에서는..

(볼이 장애물에게 부딪히면 게임 종료로 이어지는..)

게임 결과 처리와 결과 화면 UI에 대한 처리를 해보겠습니다.

혹시 너무 길지 않다면 추가적인 요소도 함께 보고요.

(생각보다 스크린샷이 너무 많이 나와서.. 편집도 오래걸리고, 글 쓰는데 오래걸려 담기는 내용이 줄어드네요..)

 

 

고생하셨습니다. 따라오며 하니 어떤가요? 도움은 좀 됐나요?

그럼 프로젝트의 다음 단계를 진행하며 나오는 새로운 것들에 대해서는

지금과 같이 설명하며 진행하도록 하겠습니다.

 

궁금한 점 있으시면 댓글이나 따로 메일로 질문하시면 시간되는 대로 답변드리겠습니다. ( 연락 )

11 Comments
  • 프로필사진 초보자 2017.01.04 03:53 신고 정말 큰 도움이 됐습니다. 너무 고맙습니다.
    책을 더 많이 읽어봐야 할 것 같네요
    알려주신 내용들이 없었어서 ㅠㅠ
  • 프로필사진 Prosto 2017.01.05 13:07 신고 도움이 되었다니 다행입니다!
    저도 응원하겠습니다.
  • 프로필사진 학생 2017.03.01 11:15 신고 안녕하세요.
    많은 도움이 되고있습니다.
    너무 감사드립니다.

    한 가지 궁금한 점이 있습니다.

    처음 normalPanel 과 pausePanel을 찾아서 GameObject로 받아올때 마지막에 as GameObject가 아닌 .gameObject를 사용하셨는데 두 가지의 사용상황을 잘 모르겠습니다. as GameObject와 .gameObject 사용 상황을 알려주실수 없을까요?
  • 프로필사진 Prosto 2017.03.10 14:10 신고 안녕하세요.
    .gameObject와
    as GameObject는 조금 다른 개념이라고 생각됩니다.

    일단
    .gameObject의 .
    .transform의 .
    같은 .은 클래스의 하위 맴버(함수,변수(선언된 구조체나 클래스 포함 public한 값들) 등) 접근/사용 용도입니다.

    예를 하나 보자면,
    GameObject.Find("Canvas");함수를 사용했을 때,
    반환되는 값은 GameObject 타입입니다.
    그렇다면, GameObject 하위 맴버(변수,함수)들을 사용할 수 있겠죠?
    그 GameObject 하위 함수로 trasform을 가지고있습니다.
    (-> GameObject.Find("Canvas").trasform; 이러면 결국 얻을 수 있는 건 Transform이 됩니다!
    --그 이유는 GameObject클래스의 함수 transform의 반환(return) 형태가 Transform이기 때문. 그냥 transform변수를 가져온다 생각하셔도 되고요.)

    그리고 그 Transform 하위 함수로 가지고있는 여러 가지들 중 자식을 찾는 FindChild("") 함수를 사용한 것입니다.
    (-> GameObject.Find("Canvas").trasform.FindChild("NormalUI");여기서도 반환되는 건 transform 값입니다.)

    그리고 마지막으로 가지고 싶은 건 transform이 아닌 gameObject이기 때문에 .gameObject를 붙여준 겁니다.
    마찬가지로 transform 하위에 gameObject 함수를 가지고 있고 반환 값으로 해당 GameObject를 받을 수 있습니다.

    같은 맥락에서 조금 나아가보면, 만약 우리가 NormalUI의 transform을 normalUITf라는 변수에 가지고 있었고,
    새롭게 GameObject를 받아오고 싶다면.
    normalUITf.gameObject를 사용하면 받아올 수 있겠죠?

    이렇듯 클래스 개념에서 사용된 거라고 볼 수 있습니다.


    추가로 as는 객체(Object)로 반환된 값을 특정 클래스로 캐스팅(형변환)을 시도하고 올바른 캐스팅이라면 해당 클래스를
    (물어보신 as GameObject는 캐스팅 성공 시 GameObject클래스 반환), 아니면 null을 반환하여 줍니다.
    이는
    https://msdn.microsoft.com/ko-kr/library/cc488006.aspx
    ms에 설명된 글을 읽어보시면 도움이 되리라 생각됩니다!
  • 프로필사진 유니티초보자 2017.05.21 01:18 신고 일시정지버튼인 Pause를 게임 실행 중에 누르면 NormalUI는 비활성화가 되고 PauseUI가 활성화가 돼야 하는데 Puase버튼도 사라지고 그냥 게임이 정지된것만 나오네요..
    Layer문제인거 같은데.. 어떻게 해야할까요?ㅜㅠ
  • 프로필사진 유니티초보자 2017.05.21 15:57 신고 아! 스크립트를 다시 보니 true를 false로 잘못적었네요;; 버튼동작도 잘 됩니다! ㅎㅎ
  • 프로필사진 Prosto 2017.05.22 00:04 신고 찾아서 해결하셨다니 다행이네요!
    또 궁금한 점 있으시면 말씀하세요!
  • 프로필사진 이은찬 2017.08.17 05:26 신고 일시정지시 배경음악은 정지가 안되던데.. 어떻게해야하나요..?
  • 프로필사진 Prosto 2017.08.18 05:06 신고 배경음악은 AudioSource 컴포넌트 참조하여 Play, Stop 설정해주시면 됩니다.

    예를 들어 스크립트가 있는 게임오브젝트에 AudioSource가 같이 있다면

    gameObject.GetComponent<AudioSource>().Play();

    gameObject.GetComponent<AudioSource>().Stop();

    같은 식으로 말이죠!
  • 프로필사진 YUN 2018.02.05 16:29 관리자의 승인을 기다리고 있는 댓글입니다
  • 프로필사진 개발지망생 2018.10.10 14:52 관리자의 승인을 기다리고 있는 댓글입니다
댓글쓰기 폼