유니티를 사용하여 2.5D 게임을 만드는 방법 튜토리얼: 파트 2

Ray Wenderlich

이 포스트는 영어 언어로도 제공됩니다.

이 포스트는 12+년 경력의 소프트웨어 엔지니어이고, 인디 iOS 개발자이며,   Touch Code Magazine의 창립자이며, iOS 튜토리얼 팀 멤버인   Marin Todorov,가 썼습니다.

Learn how to create a simple 2.5D game with Unity!

Learn how to create a simple 2.5D game with Unity!

이 글은 유니티를 사용하여 아이폰용 2.5D 게임을 만드는 방법 튜토리얼의 두번째 파트다.

이 시리즈의 첫번째 튜토리얼에서 유니티와 C# 스크립트 기초를 배우고, 비행기를 날리고 상어를 폭격하여 크라운피쉬를 보호하는 심플한 게임을 만들었다.

튜토리얼 시리즈의 두번째와 마지막 파트에서 몇가지를 수정하여 게임을 확장할 것이다. 사운드 효과, 음악, 게임 로직을 정리하고, 멀티 씬을 추가할 것이다.

이전 튜토리얼을 아직 마치지 않았으면  프로젝트를 받아서 유니티로 열어 ScenesLevel 씬을 열어서 씬이 보이도록 한다.

몇가지 더 배우고 이 게임을 마무리 하도록 하자!

게임에 iCandy(눈사탕)를 추가하자

Adding iCandy to the game

폭탄이 상어를 맞추면 상어는 그냥 빨리 사라져 “이거 별로네”라는 생각이 들것이다.

좋다, 그럼 물속에서 폭발을 일으켜 날려버릴 준비를 하자!

메뉴에서 “GameObject/Create other/Particle System”을 고르면 씬에 파티클 시스템이 생긴다. Hierarchy에서 “Particle System” 이름을 “Explosion”으로 바꾸고, “Explosion”의 위치를 [1, 1, 8]로 하라.

이제 독자는 Particle System 전문가다. 독자가 전문가이면 Inspector에서 스스로 해보고, 전문가가 아니면 나의 설명에 따라 쉽게 해보자.

Creating a particle system in Unity

아주 중요!한 “One shot” 프로퍼티을 체크하면 폭발처럼 한번만 분출한다. 그리고 애니메이션 값들도 설정하고, 아래 색상처럼 맞춰봐라. (대충해도 된다):

Particle Animator settings for Particle System in Unity

매우 중요한 “Autodestruct” 프로퍼티를 체크하면, 파티클이 더 이상 존재할 필요가 없을때 씬에서 사라진다. 마치 자동 가비지 콜렉션처럼 이 게임에 꼭 필요한 기능이다.

이제 작은 폭발을 가지고 – 폭탄에서 했던 것처럼 Prefab을 만들어 보자 – 필요할때 인스턴스를 만들고, 필요 없으면 자동으로 소멸되어 화면에서 사라진다.

Project의 Prefabs 폴더에서 오른쪽을 눌러, “Create/Prefab”를 고르고, 이름을 “ExplosionPrefab”으로 하라. Hierarchy에서 Explosion을 Project패널의 ExplosionPrefab로 드래그하라. Hierarchy의 Explosion을 오른쪽을 눌러 Delete를 골라 삭제한다.

Project 패널에서 “Sync MonoDevelop Project”를 골라 MonoDevelop을 연다. BombClass.cs를 열어 아래를 추가하라:

// "ySpeed" 정의 바로 아래 hmm
public GameObject explosionPrefab;
 
// OnTriggerEnter의 Destroy(this.gameObject)의 바로 아래 hmm
Instantiate(explosionPrefab, transform.position, Quaternion.identity);

유니티로 되돌아가서 Project패널의 BombPrefab을 선택하면 – Inspector에서 “ExplosionPrefab”라는 새 프로퍼티가 보인다. Project 패널에서 ExplosionPrefab을 해당 프로퍼에 드래그 한다.

플레이를 해보자. 폭탄이 상어를 맞추면 이렇게 폭발한다.

Gratuitous explosions

게임에 earCandy(귀사탕)를 추가하기

그만해요. 잡스. 더 이상 iCandy로는 부족해요. 이제 earCandy가 필요하다구요.

배경 음악도 없는데 어떻게 하지? 훌륭한 작곡가 Kevin Macleod이 무료 라이센스로 영화나 게임용 음악을 배포하였다. 이 멋진 친구를 방문하자.

이 url을 연다: http://incompetech.com/m/c/royalty-free/index.html?keywords=the%20cannery

열었으면 “The Cannery” 곡을 다운로드 받아 디스크에 저장하고, Project 패널의 Audio 폴더에 드래그 하라.

게임중에는 음악을 계속 반복할 예정인데 어느 오브젝트에 음악을 붙여야 하나?

카메라에 붙이자! 카메라도 게임 오브젝트니까 추가 컴포넌트를 붙일 수 있다.

Project 패널에서 “The Cannery”를 Hierarchy의 Main Camera에 드래그하고, Inspector에서 Audio 스트립을 찾아, Loop 체크박스를 체크하고, Volume을 0.20으로 설정하라.

쉽다. 씬을 시작하여 새 배경 음악을 즐겨 보자!

유니티로 GUI 만들기

이제 분야를 해보자 – GUI 유니티는 표준 레이블과 버튼을 제공한다. 별로 대단하지는 않지만 레이블을 사용하여 현재 점수를 표시해보자. 먼저 점수 로직을 구현해 보자.

MonoDevelop에서 Player.Class.cs를 열어서 새 프로퍼티를 추가하라:

public static int score = 0;

아! “static”이라는 새로운 것이 있다. 이 프로퍼티는 클래스가 로드되면 생성되고 클래스 인스턴스에 관계 없이 유지된다. 그리고, 프로퍼티는 다른 클래스에서 액세스 할 수 있다 – 이것이 count 프로퍼티를 static으로 해둔 이유다.

주의 깊게 아래 메소드를 통해  점수를 반영한다. 프로퍼티를 액세스 할때 클래스를 어떻게 사용하는지 잘 봐두기 바란다.

public void updateScoreBy(int deltaScore) {
	PlayerClass.score += deltaScore;
}

PlayerClass에 메소드를 하나 더 추가하자 – 이것은 스크린에 점수를 그린다.

void OnGUI() {
	GUIStyle style = new GUIStyle();
	style.fontSize = 20;
	GUI.Label(new Rect(10,10,100,20), "Score:"+PlayerClass.score, style );	
}

“OnGUI” 이벤트 핸들러는 매 프레임마다 GUI 레이어에서 호출된다. 그래서 필요하면 GUI에 그려한다.

GUIStyle은 CSS 흉내내는 클래스다 – 그래서 CSS 처럼 fontSize, marginLeft 등을 사용할 수 있고 반면에 font size는 유지한다. GUI.Label() 은 3개의 인자가 필요하다: 레이블의 사각형 경계, 그릴 문자열, 텍스트 스타일이다.

이제 남은 작업은 명중했거나 빗나갔을때 점수에 반영하는 것이다! BombClass를 열어서 아래를 따라 수정하자:

// 프로퍼티 추가 hmm
public PlayerClass player;
 
// 기존 OnTriggerMethod을 대체 hmm
void OnTriggerEnter(Collider obj) {
	if (obj.gameObject.name == "Shark") {
		//reset shark
		obj.gameObject.transform.rotation = Quaternion.identity;
		obj.gameObject.transform.position = new Vector3(20f, -3f, 8f);
		player.updateScoreBy(+1);
		Destroy(gameObject);
		Instantiate(explosionPrefab, transform.position, Quaternion.identity);
	}
	if (obj.gameObject.name == "ClownFish") {
		//reset fish
		obj.gameObject.transform.rotation = Quaternion.identity;
		obj.gameObject.transform.position = new Vector3(-20f, -1f, 8f);
		player.updateScoreBy(-1);
		Destroy(gameObject);
		Instantiate(explosionPrefab, transform.position, Quaternion.identity);
	}	
}

전에 했던 것과 비슷하다. 하지만 “player”라는 새 프로퍼티가 있고 점수를 변경해야 할때 player.updateScoreBy()를 호출한다.

상어를 명중하였을때 점수를 얻고 크라운피쉬를 맞추었을때 점수를 잃게하면 더 재미 있다. 이제 더 어려운 게임이 되어간다.

마지막으로 하나 더 – 폭탄의 player 프로퍼티를 설정해야 한다. 하지만 자금은 폭탄이 동적으로 생성되므로 전에 했던 것처럼 되지 않는다. 그러나 다행이 폭탄은 플레이어가 생성한다. 그래서 플레이어가 생성할떄 player 프로퍼티를 설정할 수 있다.

PlayerClass.cs를 열어서 “bombObject.transform.position = this.gameObject.transform.position;” 아래에 다음을 추가하라:

BombClass bomb = (BombClass)bombObject.GetComponent("BombClass");
bomb.player = this;

뭔가 새로이 있다. 검토해보자: bombObject는 GameObject의 인스턴스다(인스턴스를 리턴하므로). GetCompoent를 호출하여 게임 오브젝트에 있는 모든 컴포넌트에 접근할 수 있다 – 리턴값을 BombClass로 캐스팅 했다 – C# 클래스에 대한 참조를 가진 게임 오브젝트를 얻어, player 프로퍼티에 this(PlayerClass 인스턴스)저장했다.

실행하면 씬에서 점수가 보인다!

Creating a Score Label with Unity

Unity 오브젝트와 컴포넌트

이쯤에서 유니티 게임 오브젝트 모델을 소개도 될것 같다. 어떻게 작동하는지 이해하는데 도움이 된다. 게임 오브젝트와 컴퍼넌트가 어떤 관계인지 살펴보자.

Inspector 패널에 추가된 모든 스트립들 – 이것들은 게임 오브젝트에 부착된 컴포넌트들이다. 빈 게임 오브젝트는 오직 transform 스트립만 있다 – position, rotation, scale. 모든 컴포넌트는 부착될 수 있다.

BombPrefab를 보면 – 서로 다른 많은 컴포넌트들이 있다:

  • Transform: 위에서 말한 것처럼 position, rotation, scale을 지원한다.
  • Mesh Filter: 보이는 개체에 대한 geometry를 제공한다.
  • Mesh Renderer: geometry를 렌더링한다.
  • Rigidbody: 물리를 다룬다.
  • Audio Source: 오디오를 재생한다.
  • Script: 오브젝트에 프로그램을 넣을 수 있다.

아래는 오브젝트에 부착할 수 있는 몇가지 컴포넌트를 보여주고 있다. 다이어그램을 살펴보고 알아두자.

Components in Unity diagram

스크립트 컴포넌트가 중요하니 잘 봐두길 바란다.

Destroy(this.gameObject);

이렇게 호출하면 GameObject를 제거할 수도 있다.

gameObject 프로퍼티에서 다른 모든 컴포넌트에 접근할 수 있으므로 물리나 오디오 볼륨 등을 조정할 수 있다.

씬을 추가해 보자

우리 게임이 더욱 개선되고 있다.

게임이란 이기거나 진다. 그래서 플레이어가 3점 이상을 얻으면 “you win”  화면을 추가해 보자.

메뉴에서 “New Scene”을 고르고, 또 메뉴에서 “Save Scene”을 골라, “[your project's directory]/Assets/Scenes” 폴더에 “WinScene”이름으로 저장하라.

Hierarchy에서 Main Camera를 선택하고 Position은 [0, 0, 0], Projection은 “Orthographic”, Size는 “10″, Near는 “0.5″, Far는 “22″ 설정하라. 메뉴에서 “GameObject/Create Other/Directional Light”를 고르고 Inspector에서Position을 [0, 0, 0]으로 한다.

이제 씬에 plane을 (게임 레벨의 배경처럼) 두고 “you win”을 나타내는 이미지를 두려고 한다. 파티1에서 했던 것처럼 “GameObject/Create Other/Plane” 메뉴를 고르고 Inspector에서: Position을 [0, 0, 8], Rotation을 [90, 180, 0], Scale을 [3, 1, 2]로 설정한다.

Vicki Wenderlich가 준비한 아래 이미지를 다운로드 받아 디스크에 저장하라. (아래 이미지를 클릭하면 풀 해상도가 된다):

Game Over image

“gameover_youwin.png”를 “Project” 패널의 “Textures” 폴더위에 드래그 하라. 텍스쳐를 압축-임포트를 한 후에, Project 패널에서 “game over_youwin” 텍스처를  선택하고, Inspector에서 Format을 찾아서 16bits를 선택하고 Apply 버튼을 클릭하라. 그리고 Proejct 패널에서 “gameover_youwin”을 Hierarchy의 Plane에 드래그 하라. Game 패널에서 “YOU WIN”이 보여야 한다. Vicki는 사악한 상어가 배꼽이 위로 오도록 뒤집혀 둥둥 뜨게 그렸다.

이제 사용자 반응을 넣어 보자 – 이 씬이 탭되면 게임은 다시 시작 해야 한다: Project 패널의 “Class” 폴더에서 오른쯕을 눌러 “Create/C Sharp Script”를 고르고 이름을 “GameOverClass”로 바꾸라. 다시 오른쪽을 누르고 “Sync MonoDevelop Project”를 고르라. MonoDevelop 이 열리면 새 GameOverClass.cs의 내용을 아래처럼 바꾸자.

using UnityEngine;
using System.Collections;
 
public class GameOverClass : MonoBehaviour {
	// Update is called once per frame
	void Update () {
		if (Input.anyKeyDown) {
			PlayerClass.score = 0;
			Application.LoadLevel("LevelScene");
		}
	}
}

플레이어가 화면을 탭하면, 점수가 초기화되어 리셋되고, 게임을 플레이하는 씬이 로드 된다. Application.LoadLevel()은 씬 이름을 받아서 로드한다. 쉽다.

유니티로 돌아가서: Project 패널의 Class 폴더에서 “GameOverClass” 스크립트를 Hierarchy의 “Main Camera”에 드래그하라.

프로젝트에 현재 씬을 포함하기 위해, 메뉴에서 “File/Build Settings”를 고르고, 창이 팝업되면, “Add current” 버튼을 클릭하라. 프로젝트를 빌드하기 위해 씬을 추가 했다.

방금 했던 것처럼 “You loose” 씬도 빠르게 추가해 보자.

  • “New scene”를 만들고, Scenes 폴더에 “LooseScene” 이름으로 “Save scene”를 하라.
  • Hierarchy에서 “Main Camera”를 선택하고 Position은 [0, 0, 0], Projection은 “Orthographic”, Size은 “10″, Near는 “0.5″, Far는 “22″으로 설정하라.
  • 메뉴에서 “GameObject/Create Other/Directional Light”를 고르고, Inspector에서 Position을 [0, 0, 0]로 설정하라.
  • 메뉴에서 “GameObject/Create Other/Plane”를 고르고, Inspector에서 Position을 [0, 0, 8], Rotation을 [90, 180, 0], Scale to [3, 1, 2]으로 설정하라.

아래의 “You Lose” 이미지를 받아서 디스크에 저장하라. (아래 이미지를 클릭하면 풀 해상도가 된다):

You lose image

앞의 씬에서 했던 작업을 이 씬에서도 반복한다.

  • “gameover_youlose.png”파일을 Project 패널의  “Textures” 폴더에 드래그 한다.
  • Project 패널의 “gameover_youlose” 텍스쳐를 선택하고, Inspector 에서 Format을 찾아 “16bits”로 바꾸고 Apply 버튼을 클릭하라.
  • Project 패널에서 “gameover_youlose”를 Hierarchy의 “Plane”에 드래그 하라.
  • Project 패널에서 “GameOverClass”를  Hierarchy의 “Main Camera”에 드래그 하라.
  • 메뉴에서 “File/Build Settings”를 고르고, 팝업 창이 나타나면, “Add current” 버튼을 누르고 창을 닫아라.

이제 씬이 3개다. 그 씬들을 연결해 보자.

Project 패널에서 “LevelScene”을 더블 클릭하여 LevelScene 씬을 로드하라. MonoDevelop로 전환하여, PlayerClass.cs을 열라. 3점을 얻었는가 -3점으로 깍였는가 체크하도록 updateScoreBy 메소드를 변경 하겠다.

//replace the updateScoreBy method with this code
public void updateScoreBy(int deltaScore) {
	PlayerClass.score += deltaScore;	
	if (PlayerClass.score>3) {
		Application.LoadLevel("WinScene");
	} else if (PlayerClass.score

이제 씬 작업 흐름이 갖춰졌다. 유니티에서 Play 버튼을 눌러 게임을 실행해 보자. 그런데 – 왜 메뉴에서 “File/Build&Run”를 선택하여 Xcode를 띄워고 – “Run”을 눌러 아이폰에서 실행해 보려고 하지 않지?

2.5D로 – 마지막

시간이 되었다. 이제 2.5 시리즈를 만들기 위한 기나긴 여정을 통해 능숙해 졌을것이다.

이제 더 알찬 비밀을 말해 주겠다 – 개발의 즐거움을 위해 거의 3D에 가깝게 양념을 해보려고 한다.

앞에서 우리는 카메레의 Projection 을 “Orthographic”으로 설정했다. 이것은 씬이 평평한 2D로 보이도록 해준다. 그건 이제 없애쟈.

“LevelScene”씬에서 “Main Camera”를 선택하고 Inspector에서 Projection을 “Perspective”로 바꾸고, “Field of View”를 “100″으로 하라 (perspective로 보정하도록). Play 버튼을 누르고 2.5D로 게임을 해봐라. 멋지지?

Using a perspective projection in Unity for a 2.5D effect

여기서 Stop을 하고.

계획이 하나 있다. 게임을 좀 더 어렵고 카메라를 회전하고 이동하는 방법을 위한 데모를 보이고, 매번 점수가 올라갈때마다 카메라가 원주를 따라 각도가 바뀌도록 하자. 이렇게 하면 게임이 차츰 기묘한 각도에서 케릭터를 보고 상어를 폭격할 수 있다.

MonoDevelop 로 전환하고 PlayClass.cs를 아래처럼 만들자.

// 프로퍼티에 추가 hmm
public GameObject mainCamera;
public GameObject gameBackground;
public float nextZ = 0;
 
// updateScoreBy 메소드의 마지막에 추가 hmm
if (PlayerClass.score>0) {
	nextZ = PlayerClass.score*2.5f;
}
 
// Update 메소드의 마지막에 추가 hmm
if (nextZ > mainCamera.transform.position.z) {
	mainCamera.gameObject.transform.Translate( 
		3* Mathf.Sin(transform.position.z/2 ) * Time.deltaTime, 
		0, 
		-Mathf.Sin(transform.position.z /2 ) * Time.deltaTime *0.3f
	);
	mainCamera.gameObject.transform.RotateAroundLocal( Vector3.up, Time.deltaTime*0.1f );
	gameBackground.gameObject.transform.RotateAroundLocal( Vector3.up, Time.deltaTime*0.1f );
}
// 역주: 코멘트가 한글로 끝나면 Mono 컴파일러 에러 발생 hmm

코드가 많지만, 다 필요한 코드들이다. 코드를 한 줄씩 씹어 보자.

먼저, Main Gamera와 Background plan을 참조하기 위한 프로퍼티를 선언 했다. 카메라를 움직이며 회전하고, 배경도 회전할 것이다.

카메라가 z축 0에서 배경쪽으로 약 7.5를 움직인다.  점수가 바뀔때마다 nextZ은 2.5에서 5.0에서 7.5로 설정된다 – 이 값에 따라 카메라가 sin 함수를 사용하여 원주를 따라 이동 한다.

sin 함수는 Mathf.Sin()를 사용하는 것처럼 모든 수학 함수들은 Mathf 클래스를 통해 사용이 가능하다. 그리고 transform.RotateAroundLocal에 회전할 중심축 (Vector3.up)과 각도를 전달하여 카메라를 회전할 수 있다. 카메라와 배경을 동시에 회전 하였다. 그래서 카메라는 항상 배경을 마주보고 있다 (배경이 화면 밖으로 나가지 않는다.).

One more thing – 새 public 프로퍼티에 연결해 보자. 유니티로 전환하여 Hierarchy 패널에서 “Player” 오브젝트를 선택한다. Hierarchy 패널에서 “Main Camera”를 Inspector의 새 “Main Camera” 프로퍼티에 드래그 하라. Hierarchy 패널에서 “Background” 오브젝트를 Inspector의 새 “Game Background” 프로퍼티에 드래그 한다.

ㅊㅋㅊㅋ, 다 해냈다! 메뉴에서 FileBuild를 눌러 아이폰에서 실행해 보자. 오묘한 각도에서 상어에게 폭격을 해보자 :)

The finished simple 2.5D game made with Unity

Debugging with Unity

개발, 실행, 디버깅을 할때 명심할게 있다:

  • 툴바에서 Pause 버튼을 누르는 것을 잊지 말기 바란다. Pause 버튼을 누르고 씬과 함께 Insepector에서 프로퍼티들의 값들을 조사해볼 수 있다.
  • 메소드에 문제가 발생하면 (Xcode처럼) Console에 메시지를 표시해 봐라. 메뉴에서 “Window/Console”를 골라서 Console 창을 앞으로 가져올 수 있다.
  • 습관을 가져라: MonoDevelop에서 코딩을 마치고 유니티로 돌아오면 유니티의 상태바를 보기 바란다. (유니티 앱 창의 가장 아래에 있다) – 코드가 잘못 되었으면 빨간색으로 문제에 대한 메시지를 표시될 것이다.
  • 오브젝트가 움직이지 않는다면 스크립트가 오브젝트에 부착되어 있는지 몇번이고 확인해 봐라!
  • 게임을 실행하는 도중에 Inspector에 값을 바꿔 다른 값을 시도해 볼 수 있다. 그런데 게임이 끝나면 Inspector의 모든 값은 게임 실행하기 전으로 초기화 된다. 값을 바꾸기 전에 게임을 Stop하는 것을 잊지 않기 바란다. 반드시 게임을 Stop하고 개발을 계혹속 하기 바란다.

Where To Go From Here?

이제까지 진행한 튜토리얼 시리즈의 완성된 샘플 프로젝트다.

유니티에 대해 더 배우려면 www.unity3D.com/support/에 예제, 포럼 등의 Support 섹션을 보기 바란다. Unity C# Reference도 도움이 될 것이다.

보다 숙련되고 싶으면 Shark Bomber에 아래 몇가지 특징들을 추가해 보기 바란다:

  • 폭발 사운드 추가, 게임 종료 음악이나 효과
  • 2가지 이상의 폭발을 랜덤하게 하기
  • 다른 레벨을 추가해 보기

이 튜토리얼은 유니티에 대한 매우 간단한 소개다 – 우리는 겨우 아이폰에서 입력을 받았을 뿐이다. 하지만 유니티에 대해 든든한 지식을 얻었고 중요한 출발점이 되리라 믿는다.

이제 다 알고 있으니 진짜 3D 게임을 만들어 보자. 3차원으로 오브젝트들을 이동 시키고, 멋진 각도로 회전 시키고, 카메라를 움직여 보자. 적당한 모델을 고르고, Terrain(지형)을 만들어 보고 (평면도 좋다), 원하는데로 모두 해보자. 유니티로 3D게임 제작 시작을 하는 빠른 길일 수도 있다.

질문, 코멘트, 제안이 있으면 아래 포럼의 토론에 참여하기 바란다.


이 블로그 포스트는 iOS 튜토리얼 팀 멤버이고, 12+년 의 경험을 가진 소프트웨어 개발자이며, 인디 iOS 개발자이고, Touch Code Magazine의 창립자인 Marin Todorov가 썼습니다.

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

User Comments

0 Comment

Other Items of Interest

Ray의 월간 뉴스레터

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in July: Facebook Pop Tech Talk!

Sign Up - July

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

  • Matthew Morey

... 50 total!

Update Team

  • Riccardo D'Antoni

Editorial Team

  • John Clem

... 23 total!

Code Team

  • Orta Therox

... 1 total!

번역 팀

  • Di Peng

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!