如前所述,我们的主角是兔子头。接下来我们实现它。

首先对AbstractGameObject添加变量并初始化:

public Vector2 velocity;
public Vector2 terminalVelocity;
public Vector2 friction;
public Vector2 acceleration;
public Rectangle bounds;

分别是速度,极限速度,摩擦力,加速度和边界。

然后我们需要加点物理仿真:

protected void updateMotionX(float deltaTime) {
if (velocity.x != 0) {
// Apply friction
if (velocity.x > 0) {
velocity.x = Math.max(velocity.x - friction.x * deltaTime, 0);
} else {
velocity.x = Math.min(velocity.x + friction.x * deltaTime, 0);
}
}
// Apply acceleration
velocity.x += acceleration.x * deltaTime;
// Make sure the object's velocity does not exceed the
// positive or negative terminal velocity
velocity.x = MathUtils.clamp(velocity.x, -terminalVelocity.x,
terminalVelocity.x);
} protected void updateMotionY(float deltaTime) {
if (velocity.y != 0) {
// Apply friction
if (velocity.y > 0) {
velocity.y = Math.max(velocity.y - friction.y * deltaTime, 0);
} else {
velocity.y = Math.min(velocity.y + friction.y * deltaTime, 0);
}
}
// Apply acceleration
velocity.y += acceleration.y * deltaTime;
// Make sure the object's velocity does not exceed the
// positive or negative terminal velocity
velocity.y = MathUtils.clamp(velocity.y, -terminalVelocity.y,
terminalVelocity.y);
}

然后在update里调用:

updateMotionX(deltaTime);
updateMotionY(deltaTime);
// Move to new position
position.x += velocity.x * deltaTime;
position.y += velocity.y * deltaTime;

这样所有的子类都拥有了这些物理特性。

添加金币:

package com.packtpub.libgdx.canyonbunny.game.objects;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.packtpub.libgdx.canyonbunny.game.Assets; public class GoldCoin extends AbstractGameObject {
private TextureRegion regGoldCoin;
public boolean collected; public GoldCoin() {
init();
} private void init() {
dimension.set(0.5f, 0.5f);
regGoldCoin = Assets.instance.goldCoin.goldCoin;
// Set bounding box for collision detection
bounds.set(0, 0, dimension.x, dimension.y);
collected = false;
} public void render(SpriteBatch batch) {
if (collected)
return;
TextureRegion reg = null;
reg = regGoldCoin;
batch.draw(reg.getTexture(), position.x, position.y, origin.x,
origin.y, dimension.x, dimension.y, scale.x, scale.y, rotation,
reg.getRegionX(), reg.getRegionY(), reg.getRegionWidth(),
reg.getRegionHeight(), false, false);
} public int getScore() {
return 100;
}
}

金币就一个图片,供玩家收集,收集一个就得100分,然后消失。

创建羽毛:

package com.packtpub.libgdx.canyonbunny.game.objects;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.packtpub.libgdx.canyonbunny.game.Assets; public class Feather extends AbstractGameObject {
private TextureRegion regFeather;
public boolean collected; public Feather() {
init();
} private void init() {
dimension.set(0.5f, 0.5f);
regFeather = Assets.instance.feather.feather;
// Set bounding box for collision detection
bounds.set(0, 0, dimension.x, dimension.y);
collected = false;
} public void render(SpriteBatch batch) {
if (collected)
return;
TextureRegion reg = null;
reg = regFeather;
batch.draw(reg.getTexture(), position.x, position.y, origin.x,
origin.y, dimension.x, dimension.y, scale.x, scale.y, rotation,
reg.getRegionX(), reg.getRegionY(), reg.getRegionWidth(),
reg.getRegionHeight(), false, false);
} public int getScore() {
return 250;
}
}

它的代码几乎和金币一样,就是分数高点。

创建兔子头:

我们需要根据羽毛来激活是否兔子头应该powerUp,当powerUp的时候,我们把兔子头变成黄色。

package com.packtpub.libgdx.canyonbunny.game.objects;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.packtpub.libgdx.canyonbunny.game.Assets;
import com.packtpub.libgdx.canyonbunny.util.Constants; public class BunnyHead extends AbstractGameObject {
public static final String TAG = BunnyHead.class.getName();
private final float JUMP_TIME_MAX = 0.3f;
private final float JUMP_TIME_MIN = 0.1f;
private final float JUMP_TIME_OFFSET_FLYING = JUMP_TIME_MAX - 0.018f; public enum VIEW_DIRECTION {
LEFT, RIGHT
} public enum JUMP_STATE {
GROUNDED, FALLING, JUMP_RISING, JUMP_FALLING
} private TextureRegion regHead;
public VIEW_DIRECTION viewDirection;
public float timeJumping;
public JUMP_STATE jumpState;
public boolean hasFeatherPowerup;
public float timeLeftFeatherPowerup; public BunnyHead() {
init();
} public void init() {
dimension.set(1, 1);
regHead = Assets.instance.bunny.head;
// Center image on game object
origin.set(dimension.x / 2, dimension.y / 2);
// Bounding box for collision detection
bounds.set(0, 0, dimension.x, dimension.y);
// Set physics values
terminalVelocity.set(3.0f, 4.0f);
friction.set(12.0f, 0.0f);
acceleration.set(0.0f, -25.0f);
// View direction
viewDirection = VIEW_DIRECTION.RIGHT;
// Jump state
jumpState = JUMP_STATE.FALLING;
timeJumping = 0;
// Power-ups
hasFeatherPowerup = false;
timeLeftFeatherPowerup = 0;
} public void setJumping(boolean jumpKeyPressed) {
switch (jumpState) {
case GROUNDED: // Character is standing on a platform
if (jumpKeyPressed) {
// Start counting jump time from the beginning
timeJumping = 0;
jumpState = JUMP_STATE.JUMP_RISING;
}
break;
case JUMP_RISING: // Rising in the air
if (!jumpKeyPressed)
jumpState = JUMP_STATE.JUMP_FALLING;
break;
case FALLING:// Falling down
case JUMP_FALLING: // Falling down after jump
if (jumpKeyPressed && hasFeatherPowerup) {
timeJumping = JUMP_TIME_OFFSET_FLYING;
jumpState = JUMP_STATE.JUMP_RISING;
}
break;
}
} public void setFeatherPowerup(boolean pickedUp) {
hasFeatherPowerup = pickedUp;
if (pickedUp) {
timeLeftFeatherPowerup = Constants.ITEM_FEATHER_POWERUP_DURATION;
}
} public boolean hasFeatherPowerup() {
return hasFeatherPowerup && timeLeftFeatherPowerup > 0;
} @Override
public void update(float deltaTime) {
super.update(deltaTime);
if (velocity.x != 0) {
viewDirection = velocity.x < 0 ? VIEW_DIRECTION.LEFT
: VIEW_DIRECTION.RIGHT;
}
if (timeLeftFeatherPowerup > 0) {
timeLeftFeatherPowerup -= deltaTime;
if (timeLeftFeatherPowerup < 0) {
// disable power-up
timeLeftFeatherPowerup = 0;
setFeatherPowerup(false);
}
}
} @Override
protected void updateMotionY(float deltaTime) {
switch (jumpState) {
case GROUNDED:
jumpState = JUMP_STATE.FALLING;
break;
case JUMP_RISING:
// Keep track of jump time
timeJumping += deltaTime;
// Jump time left?
if (timeJumping <= JUMP_TIME_MAX) {
// Still jumping
velocity.y = terminalVelocity.y;
}
break;
case FALLING:
break;
case JUMP_FALLING:
// Add delta times to track jump time
timeJumping += deltaTime;
// Jump to minimal height if jump key was pressed too short
if (timeJumping > 0 && timeJumping <= JUMP_TIME_MIN) {
// Still jumping
velocity.y = terminalVelocity.y;
}
}
if (jumpState != JUMP_STATE.GROUNDED)
super.updateMotionY(deltaTime);
} @Override
public void render(SpriteBatch batch) {
TextureRegion reg = null;
// Set special color when game object has a feather power-up
if (hasFeatherPowerup)
batch.setColor(1.0f, 0.8f, 0.0f, 1.0f);
// Draw image
reg = regHead;
batch.draw(reg.getTexture(), position.x, position.y, origin.x,
origin.y, dimension.x, dimension.y, scale.x, scale.y, rotation,
reg.getRegionX(), reg.getRegionY(), reg.getRegionWidth(),
reg.getRegionHeight(), viewDirection == VIEW_DIRECTION.LEFT,
false);
// Reset color to white
batch.setColor(1, 1, 1, 1);
}
}

不太明白的可以看流程图:

setJumping的流程:

更新岩石:

    public void setLength(int length) {
this.length = length;
// Update bounding box for collision detection
bounds.set(0, 0, dimension.x * length, dimension.y);
}

修改Level的init()和render()

package com.packtpub.libgdx.canyonbunny.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.Array;
import com.packtpub.libgdx.canyonbunny.game.objects.AbstractGameObject;
import com.packtpub.libgdx.canyonbunny.game.objects.BunnyHead;
import com.packtpub.libgdx.canyonbunny.game.objects.Clouds;
import com.packtpub.libgdx.canyonbunny.game.objects.Feather;
import com.packtpub.libgdx.canyonbunny.game.objects.GoldCoin;
import com.packtpub.libgdx.canyonbunny.game.objects.Mountains;
import com.packtpub.libgdx.canyonbunny.game.objects.Rock;
import com.packtpub.libgdx.canyonbunny.game.objects.WaterOverlay; public class Level {
public static final String TAG = Level.class.getName(); public enum BLOCK_TYPE {
EMPTY(0, 0, 0), // black
ROCK(0, 255, 0), // green
PLAYER_SPAWNPOINT(255, 255, 255), // white
ITEM_FEATHER(255, 0, 255), // purple
ITEM_GOLD_COIN(255, 255, 0); // yellow
private int color; private BLOCK_TYPE(int r, int g, int b) {
color = r << 24 | g << 16 | b << 8 | 0xff;
} public boolean sameColor(int color) {
return this.color == color;
} public int getColor() {
return color;
}
}
public BunnyHead bunnyHead;
public Array<GoldCoin> goldcoins;
public Array<Feather> feathers; // objects
public Array<Rock> rocks;
// decoration
public Clouds clouds;
public Mountains mountains;
public WaterOverlay waterOverlay; public Level(String filename) {
init(filename);
} private void init(String filename) {
// player character
bunnyHead = null;
// objects
rocks = new Array<Rock>();
goldcoins = new Array<GoldCoin>();
feathers = new Array<Feather>();
// load image file that represents the level data
Pixmap pixmap = new Pixmap(Gdx.files.internal(filename));
// scan pixels from top-left to bottom-right
int lastPixel = -1;
for (int pixelY = 0; pixelY < pixmap.getHeight(); pixelY++) {
for (int pixelX = 0; pixelX < pixmap.getWidth(); pixelX++) {
AbstractGameObject obj = null;
float offsetHeight = 0;
// height grows from bottom to top
float baseHeight = pixmap.getHeight() - pixelY;
// get color of current pixel as 32-bit RGBA value
int currentPixel = pixmap.getPixel(pixelX, pixelY);
// find matching color value to identify block type at (x,y)
// point and create the corresponding game object if there is
// a match
// empty space
if (BLOCK_TYPE.EMPTY.sameColor(currentPixel)) {
// do nothing
}
// rock
else if (BLOCK_TYPE.ROCK.sameColor(currentPixel)) {
if (lastPixel != currentPixel) {
obj = new Rock();
float heightIncreaseFactor = 0.25f;
offsetHeight = -2.5f;
obj.position.set(pixelX, baseHeight * obj.dimension.y
* heightIncreaseFactor + offsetHeight);
rocks.add((Rock) obj);
} else {
rocks.get(rocks.size - 1).increaseLength(1);
}
}
// player spawn point
else if (BLOCK_TYPE.PLAYER_SPAWNPOINT.sameColor(currentPixel)) {
obj = new BunnyHead();
offsetHeight = -3.0f;
obj.position.set(pixelX,baseHeight * obj.dimension.y
+ offsetHeight);
bunnyHead = (BunnyHead)obj;
}
// feather
else if (BLOCK_TYPE.ITEM_FEATHER.sameColor(currentPixel)) {
obj = new Feather();
offsetHeight = -1.5f;
obj.position.set(pixelX,baseHeight * obj.dimension.y
+ offsetHeight);
feathers.add((Feather)obj);
}
// gold coin
else if (BLOCK_TYPE.ITEM_GOLD_COIN.sameColor(currentPixel)) {
obj = new GoldCoin();
offsetHeight = -1.5f;
obj.position.set(pixelX,baseHeight * obj.dimension.y
+ offsetHeight);
goldcoins.add((GoldCoin)obj);
}
// unknown object/pixel color
else {
int r = 0xff & (currentPixel >>> 24); // red color channel
int g = 0xff & (currentPixel >>> 16); // green color channel
int b = 0xff & (currentPixel >>> 8); // blue color channel
int a = 0xff & currentPixel; // alpha channel
Gdx.app.error(TAG, "Unknown object at x<" + pixelX + "> y<"
+ pixelY + ">: r<" + r + "> g<" + g + "> b<" + b
+ "> a<" + a + ">");
}
lastPixel = currentPixel;
}
}
// decoration
clouds = new Clouds(pixmap.getWidth());
clouds.position.set(0, 2);
mountains = new Mountains(pixmap.getWidth());
mountains.position.set(-1, -1);
waterOverlay = new WaterOverlay(pixmap.getWidth());
waterOverlay.position.set(0, -3.75f);
// free memory
pixmap.dispose();
Gdx.app.debug(TAG, "level '" + filename + "' loaded");
} public void update (float deltaTime) {
bunnyHead.update(deltaTime);
for(Rock rock : rocks)
rock.update(deltaTime);
for(GoldCoin goldCoin : goldcoins)
goldCoin.update(deltaTime);
for(Feather feather : feathers)
feather.update(deltaTime);
clouds.update(deltaTime);
} public void render(SpriteBatch batch) {
// Draw Mountains
mountains.render(batch);
// Draw Rocks
for (Rock rock : rocks)
rock.render(batch);
// Draw Gold Coins
for (GoldCoin goldCoin : goldcoins)
goldCoin.render(batch);
// Draw Feathers
for (Feather feather : feathers)
feather.render(batch);
// Draw Player Character
bunnyHead.render(batch);
// Draw Water Overlay
waterOverlay.render(batch);
// Draw Clouds
clouds.render(batch);
}
}

最后修改controller的update:level.update(deltaTime);

增加游戏逻辑:在相应的object加上碰撞方法和相应处理

也可以都交给controller,比如在WorldController中加入:

// Rectangles for collision detection
private Rectangle r1 = new Rectangle();
private Rectangle r2 = new Rectangle(); private void onCollisionBunnyHeadWithRock(Rock rock) {
}; private void onCollisionBunnyWithGoldCoin(GoldCoin goldcoin) {
}; private void onCollisionBunnyWithFeather(Feather feather) {
}; private void testCollisions() {
r1.set(level.bunnyHead.position.x, level.bunnyHead.position.y,
level.bunnyHead.bounds.width, level.bunnyHead.bounds.height);
// Test collision: Bunny Head <-> Rocks
for (Rock rock : level.rocks) {
r2.set(rock.position.x, rock.position.y, rock.bounds.width,
rock.bounds.height);
if (!r1.overlaps(r2))
continue;
onCollisionBunnyHeadWithRock(rock);
// IMPORTANT: must do all collisions for valid
// edge testing on rocks.
}
// Test collision: Bunny Head <-> Gold Coins
for (GoldCoin goldcoin : level.goldcoins) {
if (goldcoin.collected)
continue;
r2.set(goldcoin.position.x, goldcoin.position.y,
goldcoin.bounds.width, goldcoin.bounds.height);
if (!r1.overlaps(r2))
continue;
onCollisionBunnyWithGoldCoin(goldcoin);
break;
}
// Test collision: Bunny Head <-> Feathers
for (Feather feather : level.feathers) {
if (feather.collected)
continue;
r2.set(feather.position.x, feather.position.y,
feather.bounds.width, feather.bounds.height);
if (!r1.overlaps(r2))
continue;
onCollisionBunnyWithFeather(feather);
break;
}
}

现在3个碰撞事件都是空的,实现逻辑是这样的:

private void onCollisionBunnyHeadWithRock(Rock rock) {
BunnyHead bunnyHead = level.bunnyHead;
float heightDifference = Math.abs(bunnyHead.position.y
- (rock.position.y + rock.bounds.height));
if (heightDifference > 0.25f) {
boolean hitLeftEdge = bunnyHead.position.x > (rock.position.x + rock.bounds.width / 2.0f);
if (hitLeftEdge) {
bunnyHead.position.x = rock.position.x + rock.bounds.width;
} else {
bunnyHead.position.x = rock.position.x - bunnyHead.bounds.width;
}
return;
}
switch (bunnyHead.jumpState) {
case GROUNDED:
break;
case FALLING:
case JUMP_FALLING:
bunnyHead.position.y = rock.position.y + bunnyHead.bounds.height
+ bunnyHead.origin.y;
bunnyHead.jumpState = JUMP_STATE.GROUNDED;
break;
case JUMP_RISING:
bunnyHead.position.y = rock.position.y + bunnyHead.bounds.height
+ bunnyHead.origin.y;
break;
}
} private void onCollisionBunnyWithGoldCoin(GoldCoin goldcoin) {
goldcoin.collected = true;
score += goldcoin.getScore();
Gdx.app.log(TAG, "Gold coin collected");
} private void onCollisionBunnyWithFeather(Feather feather) {
feather.collected = true;
score += feather.getScore();
level.bunnyHead.setFeatherPowerup(true);
Gdx.app.log(TAG, "Feather collected");
}

试试效果,在WorldController的update加入:testCollisions();

跑起来,看看:

操作键:上,下,左,右,逗号,点等键。

最后,我们跟前面一样,让摄像机跟随兔子头:在Controller的initLevel中添加cameraHelper.setTarget(level.bunnyHead);

修改handleDebugInput()和keyUp():

@Override
public boolean keyUp(int keycode) {
if (keycode == Keys.R) {
init();
Gdx.app.debug(TAG, "Game World Resetted!");
}// Toggle camera follow
else if (keycode == Keys.ENTER) {
cameraHelper.setTarget(cameraHelper.hasTarget() ? null
: level.bunnyHead);
Gdx.app.debug(TAG,
"Camera follow enabled: " + cameraHelper.hasTarget());
}
return false;
} private void handleInputGame(float deltaTime) {
if (cameraHelper.hasTarget(level.bunnyHead)) {
// Player Movement
if (Gdx.input.isKeyPressed(Keys.LEFT)) {
level.bunnyHead.velocity.x = -level.bunnyHead.terminalVelocity.x;
} else if (Gdx.input.isKeyPressed(Keys.RIGHT)) {
level.bunnyHead.velocity.x = level.bunnyHead.terminalVelocity.x;
} else {
// Execute auto-forward movement on non-desktop platform
if (Gdx.app.getType() != ApplicationType.Desktop) {
level.bunnyHead.velocity.x = level.bunnyHead.terminalVelocity.x;
}
}
// Bunny Jump
if (Gdx.input.isTouched() || Gdx.input.isKeyPressed(Keys.SPACE))
level.bunnyHead.setJumping(true);
else
level.bunnyHead.setJumping(false);
}
}
private void handleDebugInput(float deltaTime) {
if (Gdx.app.getType() != ApplicationType.Desktop)
return;
if (!cameraHelper.hasTarget(level.bunnyHead)) {
// Camera Controls (move)
float camMoveSpeed = 5 * deltaTime;
float camMoveSpeedAccelerationFactor = 5;
if (Gdx.input.isKeyPressed(Keys.SHIFT_LEFT))
camMoveSpeed *= camMoveSpeedAccelerationFactor;
if (Gdx.input.isKeyPressed(Keys.LEFT))
moveCamera(-camMoveSpeed, 0);
if (Gdx.input.isKeyPressed(Keys.RIGHT))
moveCamera(camMoveSpeed, 0);
if (Gdx.input.isKeyPressed(Keys.UP))
moveCamera(0, camMoveSpeed);
if (Gdx.input.isKeyPressed(Keys.DOWN))
moveCamera(0, -camMoveSpeed);
if (Gdx.input.isKeyPressed(Keys.BACKSPACE))
cameraHelper.setPosition(0, 0);
}
// Camera Controls (zoom)
float camZoomSpeed = 1 * deltaTime;
float camZoomSpeedAccelerationFactor = 5;
if (Gdx.input.isKeyPressed(Keys.SHIFT_LEFT))
camZoomSpeed *= camZoomSpeedAccelerationFactor;
if (Gdx.input.isKeyPressed(Keys.COMMA))
cameraHelper.addZoom(camZoomSpeed);
if (Gdx.input.isKeyPressed(Keys.PERIOD))
cameraHelper.addZoom(-camZoomSpeed);
if (Gdx.input.isKeyPressed(Keys.SLASH))
cameraHelper.setZoom(1);
}

在update里加上handleInputGame(deltaTime);

大家都知道有更加成熟的Box2D可以仿真物理世界。但是我们的兔子游戏并不是完全精确的仿真物理,甚至还有比较超物理的动作,如果用Box2D来模拟不真实的物理效果是很麻烦和多余的。

掉命,游戏结束以及其他:

我们通常都会在游戏结束之后经过3秒的过渡再跳转画面。

首先定义常量在Constants:// Delay after game over
public static final float TIME_DELAY_GAME_OVER = 3;

把下面的代码加到WorldController:判断玩家掉水里了没

    private float timeLeftGameOverDelay;

    public boolean isGameOver() {
return lives < 0;
} public boolean isPlayerInWater() {
return level.bunnyHead.position.y < -5;
}

修改init()和update():死掉一条命,右上角的小兔头就变透明

private void init() {
Gdx.input.setInputProcessor(this);
cameraHelper = new CameraHelper();
lives = Constants.LIVES_START;
timeLeftGameOverDelay = 0;
initLevel();
} public void update(float deltaTime) {
handleDebugInput(deltaTime);
if (isGameOver()) {
timeLeftGameOverDelay -= deltaTime;
if (timeLeftGameOverDelay < 0)
init();
} else {
handleInputGame(deltaTime);
}
level.update(deltaTime);
testCollisions();
cameraHelper.update(deltaTime);
if (!isGameOver() && isPlayerInWater()) {
lives--;
if (isGameOver())
timeLeftGameOverDelay = Constants.TIME_DELAY_GAME_OVER;
else
initLevel();
}
}

看看效果怎么样。

为了防止摄像机向下移动的太快,修改CameraHelpe的update()

public void update (float deltaTime) {
if (!hasTarget()) return;
position.x = target.position.x + target.origin.x;
position.y = target.position.y + target.origin.y;
// Prevent camera from moving down too far
position.y = Math.max(-1f, position.y);
}

在WorldRenderer中加上game over的信息:

    private void renderGuiGameOverMessage(SpriteBatch batch) {
float x = cameraGUI.viewportWidth / 2;
float y = cameraGUI.viewportHeight / 2;
if (worldController.isGameOver()) {
BitmapFont fontGameOver = Assets.instance.fonts.defaultBig;
fontGameOver.setColor(1, 0.75f, 0.25f, 1);
fontGameOver.drawMultiLine(batch, "GAME OVER", x, y, 0,
BitmapFont.HAlignment.CENTER);
fontGameOver.setColor(1, 1, 1, 1);
}
}

加上兔子捡到羽毛的剩余时间:

private void renderGuiFeatherPowerup(SpriteBatch batch) {
float x = -15;
float y = 30;
float timeLeftFeatherPowerup = worldController.level.bunnyHead.timeLeftFeatherPowerup;
if (timeLeftFeatherPowerup > 0) {
// Start icon fade in/out if the left power-up time
// is less than 4 seconds. The fade interval is set
// to 5 changes per second.
if (timeLeftFeatherPowerup < 4) {
if (((int) (timeLeftFeatherPowerup * 5) % 2) != 0) {
batch.setColor(1, 1, 1, 0.5f);
}
}
batch.draw(Assets.instance.feather.feather, x, y, 50, 50, 100, 100,
0.35f, -0.35f, 0);
batch.setColor(1, 1, 1, 1);
Assets.instance.fonts.defaultSmall.draw(batch, ""
+ (int) timeLeftFeatherPowerup, x + 60, y + 57);
}
}

然后把新加的这两个方法加入到renderGUI();

private void renderGui (SpriteBatch batch) {
batch.setProjectionMatrix(cameraGUI.combined);
batch.begin();
// draw collected gold coins icon + text
// (anchored to top left edge)
renderGuiScore(batch);
// draw collected feather icon (anchored to top left edge)
renderGuiFeatherPowerup(batch);
// draw extra lives icon + text (anchored to top right edge)
renderGuiExtraLive(batch);
// draw FPS text (anchored to bottom right edge)
renderGuiFpsCounter(batch);
// draw game over text
renderGuiGameOverMessage(batch);
batch.end();
}

Nice!到这里,游戏的主体基本完成了。犒赏一下自己,嗯,实在太棒了!多玩一玩自己的杰作,等休息好了我们再继续。

在下一章,我们将给游戏加上进入游戏之前的菜单界面,让游戏完整。

[libgdx游戏开发教程]使用Libgdx进行游戏开发(6)-添加主角和道具的更多相关文章

  1. [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画

    前文章节列表:  使用libGDX进行游戏开发(11)-高级编程技巧   使用libGDX进行游戏开发(10)-音乐音效不求人,程序员也可以DIY   使用libGDX进行游戏开发(9)-场景过渡   ...

  2. [libGDX游戏开发教程]使用libGDX进行游戏开发(1)-游戏设计

    声明:<使用Libgdx进行游戏开发>是一个系列,文章的原文是<Learning Libgdx Game Development>,大家请周知.后续的文章连接在这里 使用Lib ...

  3. 《ArcGIS Engine+C#实例开发教程》第六讲 右键菜单添加与实现

    原文:<ArcGIS Engine+C#实例开发教程>第六讲 右键菜单添加与实现 摘要:在这一讲中,大家将实现TOCControl控件和主地图控件的右键菜单.在AE开发中,右键菜单有两种实 ...

  4. 使用Html5+C#+微信 开发移动端游戏详细教程: (四)游戏中层的概念与设计

    众所周知,网站的前端页面结构一般是由div组成,父div包涵子div,子div包涵各种标签和项, 同理,游戏中我们也将若干游戏模块拆分成层,在后续的代码维护和游戏程序逻辑中将更加清晰和便于控制. We ...

  5. 微信小程序开发教程 #043 - 在小程序开发中使用 npm

    本文介绍了如何在微信小程序开发中使用 npm 中包的功能,大大提高微信小程序的开发效率,同时也是微信小程序系列教程的视频版更新. 微信小程序在发布之初没有对 npm 的支持功能,这也是目前很多前端开发 ...

  6. PythonWeb开发教程(一),开发之前需要准备什么

    什么是web开发呢,其实就是开发一个网站了.那开发网站需要用到哪些知识呢 1.python基础,因为用python开发的,所以python指定要会,最起码你也得会条件判断,循环,函数,类这些知识: 2 ...

  7. [libgdx游戏开发教程]使用Libgdx进行游戏开发(11)-高级编程技巧 Box2d和Shader

    高级编程技巧只是相对的,其实主要是讲物理模拟和着色器程序的使用. 本章主要讲解利用Box2D并用它来实现萝卜雨,然后是使用单色着色器shader让画面呈现单色状态:http://files.cnblo ...

  8. [libgdx游戏开发教程]使用Libgdx进行游戏开发(10)-音乐和音效

    本章音效文件都来自于公共许可: http://files.cnblogs.com/mignet/sounds.zip 在游戏中,播放背景音乐和音效是基本的功能. Libgdx提供了跨平台的声音播放功能 ...

  9. [libgdx游戏开发教程]使用Libgdx进行游戏开发(2)-游戏框架搭建

    让我们抛开理论开始code吧. 入口类CanyonBunnyMain的代码: package com.packtpub.libgdx.canyonbunny; import com.badlogic. ...

  10. [libgdx游戏开发教程]使用Libgdx进行游戏开发(9)-场景过渡

    本章主要讲解场景过渡效果的使用.这里将用到Render to Texture(RTT)技术. Libgdx提供了一个类,实现了各种常见的插值算法,不仅适合过渡效果,也适合任意特定行为. 在本游戏里面, ...

随机推荐

  1. [剑指Offer] 14.链表中倒数第k个结点

    [思路]利用两个相隔为k-1个结点的指针进行遍历,当后一个指针移到末尾时,前一个指针就是要求的结点. /* struct ListNode { int val; struct ListNode *ne ...

  2. express框架 中间件

  3. windows curl 命令

    windows 64 curl 命令的使用 https://blog.csdn.net/qq_27093465/article/details/53545693 curl命令可以通过命令行的方式,执行 ...

  4. 集群hadoop ubuntu版

    搭建ubuntu版hadoop集群 用到的工具:VMware.hadoop-2.7.2.tar.jdk-8u65-linux-x64.tar.ubuntu-16.04-desktop-amd64.is ...

  5. 解决IIS的Server Application Error

    问题描述一下: Server Application ErrorThe server has encountered an error while loading an application dur ...

  6. ARC075 F.Mirrored

    题目大意:给定D,询问有多少个数,它的翻转减去它本身等于D 题解做法很无脑,利用的是2^(L/2)的dfs,妥妥超时 于是找到了一种神奇的做法. #include <iostream> u ...

  7. 【题解】SDOI2014旅行

    洛谷P3313 大概是一道树链剖分的裸题.可以看出如果不是查询相同宗教的这一点,就和普通的树链剖分毫无两样了.所以针对每一个宗教都单独开一棵线段树,变成单点修改+区间查询.只不过宗教数目很多,空间消耗 ...

  8. [POJ1784]Huffman's Greed

    题面在这里 题意 给出一棵\(n\)个节点的二叉查找树的中序遍历中每个节点的访问次数\(p[i]\),和相邻两节点\(i\)和\(i+1\)的访问次数\(q[i]\),构造一棵二叉查找树使得\(\su ...

  9. [Leetcode] Linked list cycle ii 判断链表是否有环

    Given a linked list, return the node where the cycle begins. If there is no cycle, returnnull. Follo ...

  10. 【BZOJ 2460 元素】

    Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1555  Solved: 809[Submit][Status][Discuss] Descripti ...