이전 Appual's 튜토리얼인 "기본 유니티 플랫폼 게임 만드는 방법"(및 그 2부)에서는 유니티와 내장 WebGL 플러그인을 활용해 간단한 HTML5 기반 브라우저 게임을 만드는 과정을 다뤘습니다. 이번 튜토리얼에서는 같은 방식으로 유니티를 이용해 안드로이드용 기본 게임을 만드는 방법을 단계별로 살펴보겠습니다.
준비물
- Unity
- NotePad++ 같은 텍스트 에디터 (선택 사항이지만 사용 권장)
- Android SDK
아직 준비되지 않았다면 먼저 Unity SDK와 Android SDK를 다운로드해 설치하세요. Android SDK가 필요한 이유는 완성된 게임의 APK를 실제 안드로이드 환경에서 테스트할 수 있어야 하기 때문입니다.
1. 프로젝트 생성과 에디터 화면 익히기
유니티를 실행하고 새 프로젝트를 생성합니다. 이때 반드시 2D 프로젝트로 선택하세요.
처음 화면에서 익혀야 할 패널은 크게 세 가지입니다.
- Hierarchy(계층) 패널: 현재 씬(scene)에 포함된 모든 요소가 표시됩니다. 씬이란 지금 작업 중인 '레벨' 전체를 의미합니다.
- Game 탭: 에디터 안에서 게임을 실행하고 테스트할 때 사용합니다.
- Inspector(인스펙터) 패널: 오른쪽에 위치하며 조명, 물리 등 다양한 속성을 편집하는 곳입니다.
2. 캐릭터 스프라이트와 플랫폼 만들기
가장 먼저 할 일은 스프라이트(sprite)를 만드는 것입니다. 이것이 우리가 조작할 메인 캐릭터가 됩니다. 눈 달린 사각형 정도를 직접 그려도 좋고, 인터넷에서 스프라이트를 가져와도 됩니다. 단, 반드시 .PNG 파일(투명 배경)이어야 합니다.
스프라이트를 메인 씬 창(가장 큰 창)으로 드래그 앤 드롭하면 왼쪽 Hierarchy 패널에도 자동으로 등록됩니다.
다음으로 몇 개의 플랫폼을 만듭니다. 단순한 사각형이면 충분합니다. 나중에 크기를 자유롭게 조절해 벽이나 다른 발판으로 활용할 수 있기 때문입니다. 새 사각형 스프라이트를 만들고 캐릭터와 마찬가지로 씬 창에 드래그 앤 드롭하세요.
3. 물리(Physics) 적용하기
이제 유니티에게 메인 캐릭터에 물리를 부여하라고 지시해야 합니다. 씬 창에서 메인 스프라이트를 클릭해 선택(파란색으로 하이라이트)한 뒤, Inspector 패널에서 GameObjects 관련 옵션을 확인합니다.
Add Component > Physics 2D > Rigidbody 2D를 클릭하세요. 이 한 번의 클릭으로 스프라이트에 물리가 자동으로 적용됩니다. 다른 SDK에서는 직접 코드를 작성해야 하는 작업이지만, 유니티에서는 컴포넌트 추가만으로 해결됩니다.
캐릭터가 제멋대로 빙글빙글 회전하는 것을 막으려면 Inspector 패널의 Constraints 탭에서 Freeze Rotation Z 항목에 체크하세요.
이제 Play 버튼을 누르면 캐릭터가 하늘에서 떨어져 끝없이 추락하는 것을 볼 수 있습니다. 이 문제는 뒤에서 해결할 예정이니, 일단 얼마나 쉽게 물리가 적용되는지 확인만 하면 됩니다. 참고로 적용된 물리는 대상의 형태에 따라 다르게 동작합니다. 예를 들어 같은 물리를 구(sphere)에 적용하면 굴러다니게 됩니다.
무한 추락을 막으려면 콜라이더(collider), 즉 형태의 '단단한 외곽선'을 캐릭터에 추가해야 합니다. 메인 스프라이트를 선택한 상태에서 Add Component > Physics 2D > Box Collider 2D를 클릭하세요.
그런 다음 아까 만든 플랫폼에도 똑같이 Box Collider 2D를 추가합니다. 이제 캐릭터가 플랫폼 위에 '착지'해서 서 있게 됩니다.
4. 카메라가 캐릭터를 따라오게 하기
씬에는 기본적으로 카메라 오브젝트가 이미 존재합니다. 이 카메라를 메인 스프라이트 캐릭터 위로 드래그하세요.
특히 3D 러너 게임(Slope처럼 카메라가 항상 플레이어 뒤에 위치해야 하는 장르)을 만들 때는 이 설정이 매우 중요합니다.
Hierarchy 패널의 GameObject 목록에서 카메라를 메인 캐릭터(Player GameObject) 아래로 드래그합니다. 그러면 캐릭터가 카메라의 '부모(parent)'가 되어, 플레이어가 화면 어디로 움직이든 카메라가 함께 따라옵니다.
5. 플레이어 조작 스크립트 작성하기
이번 튜토리얼에서는 Run 3 같은 무한 러너(endless runner) 장르의 기본 게임을 만들어 보겠습니다. 캐릭터가 화면을 가로질러 달리며 장애물을 피하고, 부딪히면 '게임 오버'되는 방식입니다. 이를 위해 몇 가지 스크립트가 필요하며, 대부분 이 가이드에서 제공합니다.
Assets 폴더를 우클릭해 새 폴더를 만들고 이름을 Scripts로 지정합니다. 그 폴더를 우클릭해 Create > C# Script를 선택하고 이름을 PlayerControls로 정하세요. 이 스크립트가 메인 캐릭터의 행동을 정의하게 됩니다.
스크립트를 더블클릭하면 Visual Studio에서 열리는 것이 기본이지만, 개인적으로는 NotePad++ 같은 에디터로 수정하는 것을 선호합니다.
새 스크립트 파일에는 이미 '보일러플레이트' 코드가 들어 있습니다. 이 코드는 건드리지 않거나 필요에 맞게만 수정하면 되므로 많은 시간을 절약해 줍니다. 여기에 새 변수를 하나 추가하겠습니다.
void Start() 라인 위에 다음을 입력합니다:
public Rigidbody2D rb;
다음 코드는 Start() 함수 안에 넣어 리짓바디(rigidbody)를 찾는 역할을 합니다. 즉, GameObjects(메인 캐릭터)에 붙여둔 물리 컴포넌트를 유니티가 찾도록 지시하는 것이며, Start()는 새 오브젝트나 스크립트가 생성될 때 실행됩니다.
rb = GetComponent<Rigidbody2D>();
그리고 Update() 안에는 다음 줄을 추가합니다.
rb.velocity = new Vector2(3, rb.velocity.y);
Update()는 계속해서 새로고침되는 함수이므로, 여기에 넣은 코드는 오브젝트가 파괴되기 전까지 끊임없이 실행됩니다. 위 코드는 Y축 속도(rb.velocity.y)는 그대로 유지하면서 X축(수평) 속도를 3으로 설정하라는 의미입니다. 참고로 나중에는 FixedUpdate()도 활용할 수 있는데, 비슷하지만 스크립트의 갱신 주기를 직접 제어할 수 있다는 점이 다릅니다.
스크립트를 저장하고 유니티로 돌아간 뒤, 메인 캐릭터를 클릭해 선택합니다. Inspector 패널에서 Add Component > Scripts로 방금 저장한 스크립트를 추가하세요. 이제 Play 버튼을 누르면 캐릭터가 플랫폼 끝을 향해 계속 달려가는 것을 볼 수 있습니다.
점프 입력 컨트롤 만들기
장애물이 있는 무한 러너 게임이므로 '점프' 버튼 같은 플레이어 조작 기능이 필요합니다.
앞서 만든 스크립트를 다시 열고 다음 코드를 추가합니다.
if (Input.GetMouseButtonDown(0)) {
rb.velocity = new Vector2(rb.velocity.x, 5);
}이 코드는 Update() 메서드 안에 넣으며, 마우스 버튼을 클릭하면(안드로이드에서는 화면 탭으로 변환됩니다) 캐릭터가 공중으로 '점프'하도록 유니티에 지시합니다. Y축에 값 5의 속도를 더했기 때문인데, 값이 클수록 점프 높이도 높아집니다.
코딩이 익숙하지 않은 분들을 위해 설명하자면, If문은 말 그대로 '~하면 ~하라'는 조건 명령입니다. 플레이어가 클릭(탭)하지 않으면 당연히 해당 코드는 실행되지 않습니다.
Play 버튼을 눌러 정상 동작하는지 확인한 뒤 다음 단계로 넘어가세요.
지면 체크(Ground Check) 구현하기
지금까지 테스트해 보면 한 가지 문제가 있습니다. 점프 버튼을 누르면 캐릭터가 바닥에 닿아 있든 공중에 있든 무조건 점프한다는 점입니다. 이건 원하는 러너 게임이 아니라 그냥 Flappy Bird 방식이죠. 이제 이를 수정하겠습니다.
스크립트의 Update() 메서드 위쪽에 다음 코드를 추가합니다.
public Transform groundCheck; public Transform startPosition; public float groundCheckRadius; public LayerMask whatIsGround; private bool onGround;
그다음 Update 메서드 안의 if문 위에 다음 줄을 추가합니다.
onGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
그리고 if문에 && onGround 조건을 붙여 다음과 같이 변경합니다.
if (Input.GetMouseButtonDown(0) && onGround) {완성된 전체 스크립트는 대략 다음과 같습니다.
public class PlayerControls : MonoBehaviour
{
public Rigidbody2D rb;
public Transform groundCheck;
public Transform startPosition;
public float groundCheckRadius;
public LayerMask whatIsGround;
private bool onGround;
void Start() {
rb = GetComponent<Rigidbody2D>();
}
void Update() {
rb.velocity = new Vector2(3, rb.velocity.y);
onGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
if (Input.GetMouseButtonDown(0) && onGround) {
rb.velocity = new Vector2(rb.velocity.x, 5);
}
}
}간단히 설명하면, 우리는 새로운 'Transform', 즉 게임 월드 공간상의 위치를 하나 만들었습니다. 반지름 값을 설정하고, 유니티에게 이 반경이 'Ground' 레이어와 겹치는지 검사하라고 지시한 것입니다. 이 반경이 지면과 맞닿아 있을 때만 캐릭터가 점프할 수 있고, 이미 점프해 공중에 있다면 재차 점프할 수 없습니다. 즉, groundCheck라는 Transform이 지면 레이어와 겹치면 onGround가 true가 되는 원리입니다.
스크립트를 저장하고 유니티로 돌아가면, 플레이어를 선택했을 때 Inspector에 새로운 항목들이 추가된 것을 볼 수 있습니다. 이것들이 바로 public 변수이며, 값들을 자유롭게 조정할 수 있습니다.
이제 Hierarchy에서 우클릭해 새 빈 오브젝트(Create Empty)를 만들고, 메인 씬 창에서 캐릭터 스프라이트 아래로 드래그합니다. 이 빈 오브젝트를 바닥을 감지하고 싶은 위치에 배치하세요. 오브젝트 이름을 'Check Ground'로 변경한 뒤, 앞서 카메라를 넣었던 것처럼 플레이어 게임 오브젝트 아래로 드래그해 부모-자식 관계를 만듭니다. 이제 이 빈 오브젝트는 카메라처럼 캐릭터를 따라다니며 지면과의 거리를 계속 검사하게 됩니다.
메인 캐릭터 스프라이트를 선택하고 Inspector 패널로 이동한 뒤, Check Ground 오브젝트를 'groundCheck' 항목 칸으로 드래그합니다. Transform 위치가 이 새 오브젝트의 위치와 일치해야 하며, 'Radius' 값은 0.1로 설정하세요.
다음은 지면 레이어를 정의할 차례입니다. 지면 지형을 선택하고 Inspector에서 'Layer: Default' 드롭다운 버튼을 찾아 'Add Layer'를 선택합니다.
그다음 플랫폼의 레이어를 'Ground'로 지정하고, 게임 창의 다른 모든 플랫폼에도 반복 적용합니다. 메인 캐릭터 오브젝트의 'What Is Ground' 항목에도 Ground 레이어를 선택하세요.
이렇게 하면 플레이어 스크립트가 검사를 수행합니다. 화면상의 작은 지점이 해당 레이어와 일치하는 무언가와 겹칠 경우에만 true가 되고, 그때만 캐릭터가 점프할 수 있습니다.
6. 충돌 시 게임 오버 처리와 게임 메뉴
마지막으로 할 일은 두 가지입니다. A: 장애물과 충돌하면 게임 오버되도록 만들기, B: '다시 플레이' 버튼이 있는 게임 오버 메뉴 화면 만들기입니다.
기본적인 진행 순서는 다음과 같습니다.
- Player 오브젝트를 추가하고 원하는 rigidbody와 collider를 할당합니다.
- Enemy 오브젝트를 추가하고 마찬가지로 rigidbody와 collider를 할당합니다. (선택 사항으로 'Enemy' 태그를 붙이면 편리합니다.)
- 새 C# 스크립트를 만들어 Player에 컴포넌트로 추가합니다. (이미 플레이어에 붙어 있는 스크립트를 재사용해도 되므로, 굳이 새로 만들 필요는 없습니다.)
- 스크립트에 다음 코드를 추가합니다.
void OnCollisionEnter(Collision coll){
Debug.Log("Collision"); //충돌이 감지되는지 확인용, 정상 동작하면 이 줄은 삭제 가능
if(coll.gameobject.tag == "Enemy"){ //플레이어가 충돌한 오브젝트의 태그가 "Enemy"인지 확인. 이름 비교(coll.gameobject.name == ...)로 대체할 수도 있지만 태그를 쓰는 것이 더 간편합니다.
//여기에 원하는 코드를 추가하세요. 예:
Application.LoadLevel("GameOver");
Debug.Log("Working"); //정상 동작하면 이 줄은 삭제 가능
}참고로 최신 유니티 버전에서는 Application.LoadLevel()이 폐기(deprecated)되었으므로, 대신 UnityEngine.SceneManagement 네임스페이스의 SceneManager.LoadScene("GameOver")를 사용하는 것이 좋습니다.
위 예제 스크립트를 직접 만져보면서 시행착오를 거치면, 원하는 동작을 정확히 구현할 수 있을 것입니다.
Play 버튼이 있는 GameOver '레벨'을 만드는 구체적인 방법이 궁금하다면, Appual's 가이드 "기본 유니티 플랫폼 게임 만드는 방법 Pt. 2"의 마지막 섹션을 참고하세요.