본문 바로가기

프로그래밍/libGDX 엔진

libGDX #8 플레이어 자동차 움직이기

움직이는 도로가 지난번 포스팅에서 완성되었으니 이번엔 플레이어가 조종할 자동차를 도로 위에 띄우고, 사용자가 조작할 수 있도록 만들어보려고 합니다. 하지만, 그 전에 우리 도로가 3개의 차선이 있는데, 이 3개의 차선에 차선1,2,3 이라는 이름을 정해주고, 각 레인별 높이를 정의해 주는 작업을 먼저 합시다. 이전에 만든 LoadBackground.java 에 3줄 추가하면 됩니다. 다만, 어느 클래스에서든 접근 가능하고 값을 변경하지 못하도록 선언했습니다.

package com.dpug.puggame.model;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.dpug.puggame.screen.AbstractScreen;

public class LoadBackground extends Actor {

	public static final float lane0 = 100;
	public static final float lane1 = 250;
	public static final float lane2 = 400;

	public LoadBackground(float width, float height) {
		setWidth(width);
		setHeight(height);
		setPosition(width, 0);
		addAction(Actions.forever(Actions.sequence(Actions.moveTo(0, 0, 1f),
				Actions.moveTo(width, 0))));
	}

	@Override
	public void draw(SpriteBatch batch, float parentAlpha) {
		super.draw(batch, parentAlpha);
		TextureAtlas atlas = AbstractScreen.atlas;
		TextureRegion road = atlas.findRegion("level-screen/road");
		batch.draw(road, getX() - getWidth(), getY(), getWidth() * 2,
				getHeight());
	}

}

도로에 lane에 관한 위치 정보가 담겨있으니 이제 플레이어 자동차를 추가하고, 사용자 조작에 따라 lane을 이동할 수 있도록 moveToLane 함수를 만들었습니다.

package com.dpug.puggame.model;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.dpug.puggame.screen.AbstractScreen;

public class PlayerCar extends Image {

	private int lane;

	public PlayerCar() {
		setWidth(160);
		setHeight(85);
		lane = 1;
		setPosition(100, LoadBackground.lane1 - getHeight() / 2);		
	}

	@Override
	public void act(float delta) {
		super.act(delta);
		setBounds(getX(), getY(), getWidth(), getHeight());
	}

	@Override
	public void draw(SpriteBatch batch, float parentAlpha) {
		super.draw(batch, parentAlpha);
		TextureAtlas atlas = AbstractScreen.atlas;
		TextureRegion car = atlas.findRegion("level-screen/car");
		batch.draw(car, getX(), getY(), getWidth() / 2, getHeight() / 2,
				getWidth(), getHeight(), 1, 1, getRotation());
	}

	public void tryMoveUp() {
		if ((getActions().size == 0) && (lane != 2))
			moveToLane(lane + 1);
	}

	public void tryMoveDown() {
		if ((getActions().size == 0 && (lane != 0)))
			moveToLane(lane - 1);
	}

	private void moveToLane(int lane) {
		this.lane = lane;

		switch (lane) {
		case 0:
			addAction(Actions.moveTo(getX(), LoadBackground.lane0 - getHeight()
					/ 2, 0.5f));
			break;
		case 1:
			addAction(Actions.moveTo(getX(), LoadBackground.lane1 - getHeight()
					/ 2, 0.5f));
			break;
		case 2:
			addAction(Actions.moveTo(getX(), LoadBackground.lane2 - getHeight()
					/ 2, 0.5f));
			break;
		}
	}

}

자동차 이미지 파일을 첨부파일로 추가하였으니, 다운받아 텍스쳐로 만들어 사용하세요.


자동차를 추가하는 방법은 도로추가하는것과 크게 다르지 않기에 설명은 생략합니다. 단순히 lane을 위 아래로 변경하는 기능만 하는 클래스입니다. 이 자동차 조작을 관리하는 클래스는 LevelScreen에 구현했습니다. 조작하는 자동차가 위치한 화면에서 사용자 입력을 받고, 조작하는게 가장 합리적이라고 생각했기 때문이죠.


제가 구현할 조작 방법은 fling이라는 제스쳐인데, 데스크탑에선 클릭 후, 드래그 모션이 되는것이고, 모바일에서는 터치 후, 드래그를 통해 위/아래 중 어느쪽으로 드래그를 했느냐에 따라서 자동차의 이동방향이 결정됩니다. 이러한 제스쳐를 사용자로 받기 위해서는 기존의 inputProcessor에서는 불가능합니다. 이에 따라, AbstractScreen에서 GestureListener를 implement 받습니다. 필요한 화면에서만 GestureListener를 받을까도 생각했지만 대부분의 화면에서 해당 제스쳐를 쓸 가능성이 높고, GestureListener를 받으면, 기본적으로 구현해야할 함수가 8개인데, 이를 매 화면클래스마다 구현하기엔 너무 번잡하기 때문입니다. 단, inputProcessor를 GestureDector로 셋팅하는 작업은 해당 제스쳐를 사용하는곳의 show()에서 직접 적어줍니다.


아래는 AbstractScreen에서 GestureListener를 받고 Abstract 메소드를 구현한 모습입니다. 중간 소스는 생략하였습니다. 맨 아래 추가하시면됩니다.

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.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Vector2;
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, GestureListener {		

	// 중간 소스 생략

	@Override
	public boolean touchDown(float x, float y, int pointer, int button) {
		return false;
	}

	@Override
	public boolean tap(float x, float y, int count, int button) {
		return false;
	}

	@Override
	public boolean longPress(float x, float y) {
		return false;
	}

	@Override
	public boolean fling(float velocityX, float velocityY, int button) {
		return false;
	}

	@Override
	public boolean pan(float x, float y, float deltaX, float deltaY) {
		return false;
	}

	@Override
	public boolean panStop(float x, float y, int pointer, int button) {
		return false;
	}

	@Override
	public boolean zoom(float initialDistance, float distance) {
		return false;
	}

	@Override
	public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2,
			Vector2 pointer1, Vector2 pointer2) {
		return false;
	}
}

이제 LevelScreen에서 플레이어 자동차를 조작할 수 있게 fling함수를 구현하여 오바리이딩합시다. 물론, 플레이어 자동차를 생성해서 table에 넣어주는 작업도 해야지요.

package com.dpug.puggame.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.dpug.puggame.PugGame;
import com.dpug.puggame.model.LoadBackground;
import com.dpug.puggame.model.PlayerCar;

public class LevelScreen extends AbstractScreen {

	private LoadBackground loadBackground;
	private PlayerCar playerCar;

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

	@Override
	public void show() {
		super.show();
		Gdx.input.setInputProcessor(new GestureDetector(this));
		Table table = super.getTable();
		table.setBounds(0, 0, 800, 500);
		table.setClip(true);
		loadBackground = new LoadBackground(table.getWidth(), table.getHeight());
		table.addActor(loadBackground);
		playerCar = new PlayerCar();
		table.addActor(playerCar);
	}

	@Override
	public boolean fling(float velocityX, float velocityY, int button) {
		if (velocityY < -100)
			playerCar.tryMoveUp();
		if (velocityY > 100)
			playerCar.tryMoveDown();
		return super.fling(velocityX, velocityY, button);
	}
}

이제 실행해보면, 데스크탑이라면 마우스 드래그를 통해, 모바일에선 터치드래그를 통해 자동차가 움직이게 됩니다.