본문 바로가기

프로그래밍/libGDX 엔진

libGDX #5 메인메뉴 화면과 텍스트 출력

어제는 스플래쉬 화면을 만들어 fade-in 효과를 넣어보기까지 했다. 이제 스플래쉬 화면이 끝나면, 자동으로 메인메뉴로 이동을 시켜야한다. 그럼 어제 단순히 로그로만 출력했던 게임 메뉴로 이동하는 부분을 실제로 구현해보자. 아래와 같이 로그 출력문구 밑에 실제로 new MenuScreen 을 해보도록 하자.

@Override
public boolean act(float delta) {
	// fade-in 효과가 끝난 후, 하게 될 액션
	Gdx.app.log(PugGame.LOG, "게임 메뉴로 이동하도록 구현하자!");
	game.setScreen(new MenuScreen(game));
	return false;
}

MenuScreen이 없다고 나오면, 새로 만들도록 하자. MenuScreen은 단순히 show()를 구현하면된다. 나머지는 상위클래스에 있는걸 그냥 상속받아 사용할꺼다. 테이블을 가져와서 환영 문구를 입력해보도록 하자.

package com.dpug.puggame.screen;

import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.dpug.puggame.PugGame;

public class MenuScreen extends AbstractScreen {

	public MenuScreen(PugGame game) {
		super(game);
	}

	@Override
	public void show() {
		super.show();
		// 테이블 가져오기
		Table table = super.getTable();
		table.add("Welcome to Pug GAME").spaceBottom(50);
		table.row();
	}
}

getTable() 같은건 존재하지 않는다고 나올텐데 하나씩 만들어주자. 이 테이블을 어떻게 관리할지는 경험이 없어서 고민이 많은데, Table은 어느 화면에서든 사용하게되는 Stage같은 개념인지라 아마 Table은 Group을 상속받은 애일텐데, AbstractScreen에 만들어서 사용하는것이 맞다고는 생각하나 그러면 다른 화면으로 이동할 경우, 메인메뉴에서 사용하던 Table은 사라지게되고, 다시 메인메뉴로 돌아올 경우, 새로 메인메뉴 테이블을 생성해야함으로 로딩시간이 걸리게된다. 로딩화면이나 게임화면이면 모를까 메인화면이나 옵션화면 같은 경우에는 많은 리소스를 먹는것도 아니고, 대부분의 화면에서도 쉽게 접근함으로 로딩시간 없도록 AbstractScreen에서 따로 가지고 있는게 낫다고 본다. 즉, 메인메뉴와 게임화면 두 개의 화면을 사용한다 치면, AbstractScreen에서 관리할 Table은 menuTable과 gameTable 이런식으로 두 개를 사용해야하지 않을까? 


근데, 이런식으로 관리할 메뉴는 메인메뉴와 옵션화면 정도이니 이 두개 Screen 클래스에서만 AbstractScreen의 Table을 사용하지 않고 show()할때 Init()을 호출하게 하여 Init()에서 Table을 생성하고, 해당 스크린에서 table을 관리하게 만들고, 메인메뉴와 옵션화면이 아닌 Screen들은 AbstractScreen의 table을 사용하게끔 하는것이다. 이것도 나름 합리적일것 같다. 그 외에도 규모가 더 커지면 테이블을 관리하는 TableManager를 만드는것이 이러한 고민을 없애는 가장 확실한 방법이겠지만 여기서는 그냥 모든 화면 이동에 로딩이 있다고 가정하고 화면 이동할때마다 Table을 새로 만들도록 해 볼 예정이다.


여하튼, 나의 AbstractScreen은 다음과 같다.

package com.dpug.puggame.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.dpug.puggame.PugGame;

public class AbstractScreen implements Screen {

	protected final PugGame game;
	protected final Stage stage;

	private Table table;
	private Skin skin;
	private TextureAtlas atlas;

	public AbstractScreen(PugGame game) {
		this.game = game;
		this.stage = new Stage();
	}

	protected Table getTable(){
		if(table == null){
			table = new Table(getSkin());
			table.setFillParent(true);
			stage.addActor(table);
		}
		return table;
	}
	
	protected Skin getSkin(){
		if(skin == null){
			FileHandle skinFile = Gdx.files.internal("skin/uiskin.json");
			skin = new Skin(skinFile);
		}
		return skin;
	}
	
	/**
	 * 해당 클래스 이름 스트링으로 반환하는 함수
	 */
	protected String getName() {
		return getClass().getSimpleName();
	}

	/**
	 * atlas 가져오기
	 */
	TextureAtlas getAtlas() {
		if (atlas == null) {
			atlas = new TextureAtlas(
					Gdx.files.internal("textures/textures.atlas"));
		}
		return atlas;
	}

	@Override
	public void render(float delta) {
		// RGB 검은색으로 화면을 클리어함
		Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		// actor 업데이트
		stage.act(delta);
		
		// actor들 그리기
		stage.draw();
	}

	@Override
	public void resize(int width, int height) {
		Gdx.app.log(PugGame.LOG, "화면 크기 변경: " + getName() + " 이 " + width
				+ " x " + height);
	}

	@Override
	public void show() {
		Gdx.app.log(PugGame.LOG, "화면을 보여줌: " + getName());
	}

	@Override
	public void hide() {
		Gdx.app.log(PugGame.LOG, "화면을 숨김: " + getName());
	}

	@Override
	public void pause() {
		Gdx.app.log(PugGame.LOG, "화면을 멈춤: " + getName());
	}

	@Override
	public void resume() {
		Gdx.app.log(PugGame.LOG, "화면을 재개: " + getName());
	}

	@Override
	public void dispose() {
		Gdx.app.log(PugGame.LOG, "화면을 반환: " + getName());
		if (skin != null) {
			skin.dispose();
		}
		if (stage != null) {
			stage.dispose();
		}
		if (atlas != null) {
			atlas.dispose();
		}
	}
}

크게 변화된 점은 두 가지이다. getTable() 과 getSkin() 이다. table은 단순히 생성하고, stage에 추가해준것 뿐이다. 반면, skin의 경우, uiskin.json을 통해 skin관련 정보를 불러오게 되는데, libgdx에서 사용하는 skin에 대해서 조금 찾아보길 바란다(나중에 이에 대해 포스팅 하게되면 여기에도 링크를 걸어둘 예정). 간략히 설명하자면, skin은 버튼을 사용할때 쓸 이미지를 만들고 위치값을 저장해 놓은것인데 uiskin.png를 열어보면 감이 올것이다. 폰트에 관련된 파일은 default.png 와 default.fnt인데, 폰트는 hiero라는 libgdx의 extension 툴로 제작할 수 있다(이것도 나중에 포스팅하게 되면 링크를 걸 예정). 현재 폰트는 해외용이기 때문에 한글을 지원해주지 않는다. 어느정도 포스팅이 진행되면 한글 폰트 제작 및 적용도 포스팅할 것이다. 결국 uiskin.json을 통해 폰트 파일들도 연동되어 진다. 이 파일들은 첨부파일로 첨부했으니 자세한 정보를 공부할 때까지는 그냥 다운받아서 사용하자. 사실 나도 잘 모른다. 한글 폰트 제작하고 넣어봤는데 인식을 못한다 :(

skin.zip

skin에 대해서 아는분은 댓글로 정보 공유 부탁드려요. 이거때문에 정말 고생 많이했는데 아직도 잘 모르겠네요.


마지막으로 게임을 실행시키면 로딩화면이 끝난 후, 아래와 같이 텍스트가 출력됩니다.

"Welcome to Pug GAME"