본문 바로가기

프로그래밍/libGDX 엔진

libGDX 튜토리얼(Tutorial) #1 - 소개 및 설치(Introduction)

자바로 2D, 3D 게임을 구현할 수 있는 엔진 libGDX 튜토리얼을 시작합니다. libGDX가 무엇인지 궁금하신분은 아래 링크로 이동하여, 이전에 올렸던 libGDX 소개와 특징에 관한 포스팅을 읽어보세요.


■ libGDX 설치하기

libGDX 설치에 앞서 기본적으로 준비되어야할 것이 있습니다. 일단, 자바, 이클립스, 안드로이드 SDK가 설치되어 있어야 합니다. 그리고, 이것들이 정상적으로 작동되는 상태여야 합니다. 이제 libGDX를 다운로드 합니다.. 저는 현재 최신버전인 0.9.8 버전을 사용합니다. 설치하는 과정을 포함하는것 대신에 우선적으로 유투브의 이 동영상을 보길 원합니다. 이 동영상은 Mario Zechner("Beginning Android Games" 책 저자)라는 ligGDX의 메인 프로그래머가 배포한 것입니다. ligGDX 프레임워크를 사용하여 이클립스에 프로젝트를 셋업할 수 있도록 가이드해줍니다. (동영상을 보고 따라하시다가 궁금한점이 생기시면 댓글로 질문해주세요)


■ 게임 프로젝트 셋팅하기

우리가 함께 구현할 게임은 티리안(Tyrian) 입니다. 1995년 해외에서 출시된 게임이며, 오락실에 있는 평범한 비행기 슈팅 게임이라 보시면 됩니다. libGDX 엔진 개발자가 어렸을때, 이 게임을 많이 했었기에 이 게임을 구현하며 튜토리얼을 진행하게 됩니다. 물론, 실제 티리안보다는 많이 단순한 결과물을 구현할 것입니다. 그러나 libGDX를 사용하여 게임을 개발하는 법을 배울 수 있을것입니다. 튜토리얼 관련 소스들은 Google Code에서 확인할 수 있습니다. 자, 이제 프로젝트를 시작합니다. 저는 Zechner의 동영상을 따라 이클립스에 프로젝트를 구성하였습니다.

tyrian-android 프로젝트는 tyrian-game 프로젝트에 종속되어 있습니다. 종속성은 tyrian-android 프로젝트 셋팅의 "Java Build Path > Projects" 구역에 설정되어 있습니다. 그러나 이것으로 충분하지 않습니다. "Libraries" 탭에서 수동으로 tyrian-game의 "gtx.jar"를 추가해야만 합니다. 만약, 이 작업을 하지 않는다면, 안드로이드 디바이스에서 실행이 되지 않을것입니다.(컴파일시 에러가 없는데도 말입니다.) 프로젝트의 구조를 생성하였다면, tyrian-game을 데스크탑에서 개발하기 위한 구동, 그리고 tyrian-android 프로젝트를 안드로이드 스마트폰/타블렛에서 구동하는것이 가능해집니다.


아래 이미지는 제가 셋팅한 이클립스 구조입니다. 참조하세요.


■ 리소스 파일은 어디에 넣어야하나요?

언젠가는 우리 게임에 오디오와 이미지 파일을 추가해야합니다. 이러한 리소스들은 우리의 두 프로젝트에서 접근할 수 있어야합니다. 리소스들을 복사해서 쓰는것 대신에 이클립스에서는 멋진 기술(cool trick)을 쓸 수 있습니다. 우선, 안드로이드 프로젝트에 "assets" 폴더를 생성합니다. 우리가 어느정도 리소스들을 얻을때까지 이 폴더에 리소스들을 넣어둡니다. 이러한 리소스들을 게임 프로젝트에 공유하기 위하여, 링크된 소스 폴더를 생성합니다. 게임 프로젝트의 셋팅을 열고, "Link Source..." 버튼을 아래와 같이 클릭합니다.

완료가 되면, 안드로이드 프로젝트에서 쓰던 assets 폴더가 게임 프로젝트에서도 공유가 되어, 접근이 가능해집니다. (게임 프로젝트에도 공유된 assets 폴더가 생김)


■ 게임의 메인 클래스

우리가 만든 게임의 메인 클래스는 libgdx의 com.badlogic.gdx.ApplicationListener 를 가장 단순하게 구현한 것입니다. 그러므로 libgdx 클래스들의 Javadocs를 읽어 봐야합니다. 제가 그것들의 세세한 부분까지는 여기에 다 포스팅할 수가 없기 떄문입니다. (Tip: libgdx의 JAR파일안의 소스코드들은 프로젝트 내에서 접근이 가능합니다)


우리가 만든 게임의 메인 클래스는 특정 어플리케이션 이벤트들을 다루어야 할 책임이 있습니다. 예를들어, "create", "render", "dispose" 같은 것들 말입니다. 이벤트가 데스크탑에서 실행될 때, 안드로이드와 정확하게 똑같이 행동하진 않지만 그것들은 꽤 비슷하게 행동합니다. 아래 리스트에서 이벤트가 실행될 때의 어플리케이션 라이플사이클과 언제 그것들이 실행되는지를 보여줍니다.

  • create - 어플리케이션이 생성됐을때 한 번만 실행됩니다.
  • resize - 게임 스크린이 크기가 변경되거나 게임이 정지(pause)상태가 아닐때 실행되며, create 이벤트가 발생한 후에도 한 번 실행됩니다.
  • render - 어플리케이션의 게임루프로부터 항상 렌더링이 이루어집니다. 게임 업데이트는 실제 렌더링이 열리기전에 실행됩니다.
  • pause - 어플리케이션이 죽기(destroy)전에 실행됩니다. 안드로이드에서는 홈 버튼이 눌리거나 전화가 올 경우에 일어납니다. 이것은 정지(pause) 후, 재개(resumed)한다는것이 보장되지 않았기에 게임 상태를 저장하기 좋은 타이밍입니다. 반면, 데스크탑에서는 어플리케이션에서 나갈 때, 처분(disposal)하기 전에 일어납니다. 
  • resume - 어플리케이션이 포커스를 받았을때 일어나며, 오직 안드로이드에서만 실행됩니다.
  • dispose - 어플리케이션이 죽을때(destroy) 실행됩니다. 이것은 정지(pause) 이벤트가 먼저 선행됩니다.

이러한 이벤트를 포획(capture)하고 처리하기 위해, com.badlogic.gdx.ApplicationListener 인터페이스에 메소드를 구현해야합니다. libgdx가 우리 게임을 생성(create)할 때, 메인 루프 쓰레드(Main lop thread)라 불리는 각각의 쓰레드가 생성되고, OpenGL 컨텍스트에 연결된다. 모든 이벤트 처리는 UI 쓰레드 내부가 아닌 각각의 쓰레드 내부에서 실행됩니다.


이제 충분히 읽으셨으니, 우리 게임의 메인 클래스를 생성해봅시다. 아래 코드를 보세요. (주석도 읽어주세요)

package com.dpug.tyrian;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.FPSLogger;
import com.badlogic.gdx.graphics.GL10;

/**
 * 게임 메인 클래스, 어플리케이션 이벤트가 실행될 때, 호출되는 곳
 */
public class Tyrian implements ApplicationListener {
	// 로깅을 위한 유용한 상수 선언
	public static final String LOG = Tyrian.class.getSimpleName();

	// 현재 FPS의 기록을 도와주는 libgdx 클래스
	private FPSLogger fpsLogger;

	@Override
	public void create() {
		// Gdx는 싱글톤으로 구현되었으며, 그래픽, 오디오, 파일 등도 마찬가지임
		Gdx.app.log(Tyrian.LOG, "Creating Game");
		fpsLogger = new FPSLogger();
	}

	@Override
	public void dispose() {
		Gdx.app.log(Tyrian.LOG, "Disposing game");
	}

	@Override
	public void pause() {
		Gdx.app.log(Tyrian.LOG, "Pausing game");
	}

	@Override
	public void render() {
		// 아래의 코드는 화면을 클리어하고, RGB 컬러를 줌(green)
		Gdx.gl.glClearColor(0f, 1f, 0f, 1f);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

		// 현재 FPS의 츨력, 게임 퍼포먼스를 쉽게 확인이 가능하다.
		fpsLogger.log();
	}

	@Override
	public void resize(int width, int height) {
		Gdx.app.log(Tyrian.LOG, "Resizing game to: " + width + " x " + height);
	}

	@Override
	public void resume() {
		Gdx.app.log(Tyrian.LOG, "Resuming game");
	}

}


■ 게임 구동

다른 플랫폼에서도 우리 게임을 실행할 수 있다면, 우리는 각각에 대한 구체적인 실행기(Launcher)가 필요합니다. 데스크탑 실행기부터 시작합니다. 아래 코드를 보세요. (주석도 읽어주세요)

package com.dpug.tyrian;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;

/**
 * 데스크탑 LWJGL 어플리케이션을 생성한 간단한 클래스입니다.
 */
public class TyrianDesktopLauncher {

	public static void main(String[] args) {
		// 어플리케이션 이벤트들을 받을 리스너 생성
		ApplicationListener listener = new Tyrian();

		// 창 타이틀 정의
		String title = "Tyrian";

		// 창 사이즈 정의
		int width = 800, height = 480;

		// OpenGL ES 2.0 사용여부
		boolean useOpenGLES2 = false;

		// 게임 생성
		new LwjglApplication(listener, title, width, height, useOpenGLES2);
	}
}

다 됐습니다. 이 클래스를 실행시키면, 아래와 같은 결과를 볼 수 있을꺼에요.

그리고 안드로이드 디바이스를 위해, Activity에 아래와 같이 적어주세요.

package com.dpug.tyrian;

import com.badlogic.gdx.backends.android.AndroidApplication;

import android.os.Bundle;
import com.dpug.tyrian.Tyrian;

/**
 * 안드로이드 Activity를 정의한 간단한 클래스입니다.
 */
public class TyrianAndroidLauncher extends AndroidApplication {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// OpenGL ES 2.0 사용여부
		boolean useOpenGLES2 = false;

		// 게임 생성
		initialize(new Tyrian(), useOpenGLES2);
	}
}

그러면, 안드로이드에서 아래와 같은 초록색 화면을 볼 수 있습니다.


■ 결론

우리는 게임 프로젝트 구조를 만들고, 안드로이드와 데스크탑에서 모두 테스트를 마쳤습니다.  다음 포스팅에서는 libgdx의 내부와 멋진 게임 기능을 구현하는데 초점을 맞출 것입니다. 궁금한점이 생기시면 댓글로 질문하세요 :)