接着上一篇,我们完成后续的扫尾工作:游戏中个物体创建及其碰撞检测,分数保存,音效处理。
1.World类:(加入所有物体,及其碰撞检测,代码里有详细注解)
package com.zhf.mylibgdx; import java.util.ArrayList; import java.util.List; import java.util.Random; import com.badlogic.gdx.math.Vector2; /** * 统一管理世界中各个部分 * @author ZHF * */ public class World { /**世界监听器接口**/ public interface WorldListener { //跳跃 public void jump (); //高跳 public void highJump (); //碰撞 public void hit (); //收集金币 public void coin (); } //宽和高 public static final float WORLD_WIDTH = 10; public static final float WORLD_HEIGHT = 15 * 20; //状态 public static final int WORLD_STATE_RUNNING = 0; //运行 public static final int WORLD_STATE_NEXT_LEVEL = 1; //下一关 public static final int WORLD_STATE_GAME_OVER = 2; //游戏结束 //世界监听器 public WorldListener listener; //重力 public static final Vector2 gravity = new Vector2(0, -12); //随机数 public Random rand; public float heightSoFar; // public int score; public int state; //游戏中物体 public final List<Platform> platforms; //跳板 public final Bob bob; //主角 public final List<Spring> springs; //弹簧 public final List<Squirrel> squirrels; //会飞的松鼠 public final List<Coin> coins; //金币 public Castle castle; //城堡 public World(WorldListener listener) { this.listener = listener; //初始化游戏中的物体 this.bob = new Bob(5, 1); this.platforms = new ArrayList<Platform>(); this.springs = new ArrayList<Spring>(); this.squirrels = new ArrayList<Squirrel>(); this.coins = new ArrayList<Coin>(); rand = new Random(); generateLevel(); //生成关卡中除了Bob外所有的物体 } /**生成关卡中除了Bob外所有的物体**/ private void generateLevel() { float y = Platform.PLATFORM_HEIGHT / 2; float maxJumpHeight = Bob.BOB_JUMP_VELOCITY * Bob.BOB_JUMP_VELOCITY / (2 * -gravity.y); while (y < WORLD_HEIGHT - WORLD_WIDTH / 2) { int type = rand.nextFloat() > 0.8f ? Platform.PLATFORM_TYPE_MOVING : Platform.PLATFORM_TYPE_STATIC; float x = rand.nextFloat() * (WORLD_WIDTH - Platform.PLATFORM_WIDTH) + Platform.PLATFORM_WIDTH / 2; //跳板的位置 Platform platform = new Platform(type, x, y); platforms.add(platform); //弹簧的位置 if (rand.nextFloat() > 0.9f && type != Platform.PLATFORM_TYPE_MOVING) { Spring spring = new Spring(platform.position.x, platform.position.y + Platform.PLATFORM_HEIGHT / 2 + Spring.SPRING_HEIGHT / 2); springs.add(spring); } //松鼠的位置 if (y > WORLD_HEIGHT / 3 && rand.nextFloat() > 0.8f) { Squirrel squirrel = new Squirrel(platform.position.x + rand.nextFloat(), platform.position.y + Squirrel.SQUIRREL_HEIGHT + rand.nextFloat() * 2); squirrels.add(squirrel); } //金币的位置 if (rand.nextFloat() > 0.6f) { Coin coin = new Coin(platform.position.x + rand.nextFloat(), platform.position.y + Coin.COIN_HEIGHT + rand.nextFloat() * 3); coins.add(coin); } //游戏中的物体的位置根据跳板的位置来确定 y += (maxJumpHeight - 0.5f); y -= rand.nextFloat() * (maxJumpHeight / 3); } //城堡的位置是确定的 castle = new Castle(WORLD_WIDTH / 2, y); } /**刷新界面**/ public void update(float deltaTime, float accelX) { updateBob(deltaTime, accelX); //刷新主角 updatePlatforms(deltaTime); //刷新跳板 updateSquirrels(deltaTime); //刷新松鼠 updateCoins(deltaTime); //刷新金币 if (bob.state != Bob.BOB_STATE_HIT) checkCollisions(); //游戏结束判断 checkGameOver(); } /**碰撞检测**/ private void checkCollisions() { // TODO Auto-generated method stub checkPlatformCollisions(); //跳板的碰撞 checkSquirrelCollisions(); //松鼠的碰撞 checkItemCollisions(); //金币和弹簧的碰撞 checkCastleCollisions(); //城堡的碰撞 } /**跳板碰撞**/ private void checkPlatformCollisions() { if (bob.velocity.y > 0) return; int len = platforms.size(); for (int i = 0; i < len; i++) { Platform platform = platforms.get(i); if (bob.position.y > platform.position.y) { //调用工具类中矩形块碰撞检测 if (OverlapTester.overlapRectangles(bob.bounds, platform.bounds)) { bob.hitPlatform(); listener.jump(); if (rand.nextFloat() > 0.5f) { platform.pulverize(); } break; } } } } /**松鼠碰撞**/ private void checkSquirrelCollisions () { int len = squirrels.size(); for (int i = 0; i < len; i++) { Squirrel squirrel = squirrels.get(i); if (OverlapTester.overlapRectangles(squirrel.bounds, bob.bounds)) { bob.hitSquirrel(); listener.hit(); } } } /**金币和弹簧碰撞**/ private void checkItemCollisions () { int len = coins.size(); for (int i = 0; i < len; i++) { Coin coin = coins.get(i); if (OverlapTester.overlapRectangles(bob.bounds, coin.bounds)) { coins.remove(coin); len = coins.size(); listener.coin(); score += Coin.COIN_SCORE; //加分 } } if (bob.velocity.y > 0) return; //若是上升状态不去考虑碰撞检测 len = springs.size(); for (int i = 0; i < len; i++) { Spring spring = springs.get(i); if (bob.position.y > spring.position.y) { //弹簧的碰撞检测 if (OverlapTester.overlapRectangles(bob.bounds, spring.bounds)) { bob.hitSpring(); listener.highJump(); } } } } /**城堡的碰撞**/ private void checkCastleCollisions () { if (OverlapTester.overlapRectangles(castle.bounds, bob.bounds)) { state = WORLD_STATE_NEXT_LEVEL; } } /**刷新Bob**/ private void updateBob(float deltaTime, float accelX) { //碰撞跳板 if (bob.state != Bob.BOB_STATE_HIT && bob.position.y <= 0.5f) bob.hitPlatform(); //主角x轴方向移动的速度 if (bob.state != Bob.BOB_STATE_HIT) bob.velocity.x = -accelX / 10 * Bob.BOB_MOVE_VELOCITY; bob.update(deltaTime); //竖直最大高度 heightSoFar = Math.max(bob.position.y, heightSoFar); } /**刷新跳板**/ private void updatePlatforms(float deltaTime) { int len = platforms.size(); for (int i = 0; i < len; i++) { Platform platform = platforms.get(i); //取出集合中的跳板对象,调用其自身的刷新方法 platform.update(deltaTime); //若状态为破碎状态,将该跳板对象移除出去 if (platform.state == Platform.PLATFORM_STATE_PULVERIZING && platform.stateTime > Platform.PLATFORM_PULVERIZE_TIME) { platforms.remove(platform); len = platforms.size(); } } } /**刷新松鼠**/ private void updateSquirrels (float deltaTime) { int len = squirrels.size(); for (int i = 0; i < len; i++) { Squirrel squirrel = squirrels.get(i); squirrel.update(deltaTime); } } /**刷新金币**/ private void updateCoins (float deltaTime) { int len = coins.size(); for (int i = 0; i < len; i++) { Coin coin = coins.get(i); coin.update(deltaTime); } } /**游戏结束判断**/ private void checkGameOver () { //目前的Bob的高度小于场景的高度 if (heightSoFar - 7.5f > bob.position.y) { state = WORLD_STATE_GAME_OVER; } } }
看着代码挺多,其实就是一个update()和checkCollisions(),还有重要的generateLevel()。接下来就是渲染WorldRenderer类中绘制各个物体
/**渲染游戏中各种物体(Bob,跳板,松鼠,弹簧,城堡,金币)**/ private void renderObjects() { batch.enableBlending(); batch.begin(); renderPlatforms(); //绘制跳板 renderBob(); //绘制主角 renderItems(); //绘制金币和弹簧 renderSquirrels(); //绘制松鼠 renderCastle(); //绘制城堡 batch.end(); }
由于代码过多,这里就不一一贴出来了,大家可以自行参考源码,有注解的哦!
既然添加了游戏中的物体,那自然又得在Asset中加载资源
声明:
//游戏中各种物体 public static TextureRegion platform; //跳板 public static Animation brakingPlatform; //破碎的跳板(动画) //主角 public static Animation bobJump; //跳跃(动画) public static Animation bobFall; //下落(动画) public static TextureRegion bobHit; //碰撞图片 public static TextureRegion spring; //弹簧 public static TextureRegion castle; //城堡 public static Animation coinAnim; //金币 (动画) public static Animation squirrelFly; //飞着的松鼠 (动画)
实例化:
//游戏中各个物体 platform = new TextureRegion(items, 64, 160, 64, 16); //跳板 brakingPlatform = new Animation(0.2f, new TextureRegion(items, 64, 160, 64, 16), new TextureRegion(items, 64, 176, 64, 16), new TextureRegion(items, 64, 192, 64, 16), new TextureRegion(items, 64, 208, 64, 16));//破碎的跳板 spring = new TextureRegion(items, 128, 0, 32, 32); //弹簧 castle = new TextureRegion(items, 128, 64, 64, 64); //城堡 coinAnim = new Animation(0.2f, new TextureRegion(items, 128, 32, 32, 32), new TextureRegion(items, 160, 32, 32, 32), new TextureRegion(items, 192, 32, 32, 32), new TextureRegion(items, 160, 32, 32, 32)); //金币 squirrelFly = new Animation(0.2f, new TextureRegion(items, 0, 160, 32, 32), new TextureRegion(items, 32, 160, 32, 32)); //飞着的松鼠 //主角 bobJump = new Animation(0.2f, new TextureRegion(items, 0, 128, 32, 32), new TextureRegion(items, 32, 128, 32, 32)); bobFall = new Animation(0.2f, new TextureRegion(items, 64, 128, 32, 32), new TextureRegion(items, 96, 128, 32, 32)); bobHit = new TextureRegion(items, 128, 128, 32, 32);
运行一下代码,我们发现,我们操作者主角向上移动,会遇到弹簧,会破碎的跳板,飞着的松鼠,金币,一切都正常的运行着! 仔细一看,加上金币后分数值没有改变哦!当然还有死亡的判断,音效的加入等等。
效果图:
在此先做一个代码版本,方便初学者能够清楚的了解代码
源码下载:http://down.51cto.com/data/897211
下面我们来完成分值的计算:
在GameScreen中:
/**游戏运行状态**/ private void updateRunning (float deltaTime) { if (Gdx.input.justTouched()) { guiCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0)); //点击暂停 if (OverlapTester.pointInRectangle(pauseBounds, touchPoint.x, touchPoint.y)) { Assets.playSound(Assets.clickSound); state = GAME_PAUSED; return; } } ApplicationType appType = Gdx.app.getType(); // should work also with Gdx.input.isPeripheralAvailable(Peripheral.Accelerometer) if (appType == ApplicationType.Android || appType == ApplicationType.iOS) { world.update(deltaTime, Gdx.input.getAccelerometerX()); } else { float accel = 0; if (Gdx.input.isKeyPressed(Keys.DPAD_LEFT)) accel = 5f; if (Gdx.input.isKeyPressed(Keys.DPAD_RIGHT)) accel = -5f; world.update(deltaTime, accel); } //当前分数(变化) if (world.score != lastScore) { lastScore = world.score; scoreString = "SCORE: " + lastScore; } //本关结束 if (world.state == World.WORLD_STATE_NEXT_LEVEL) { state = GAME_LEVEL_END; } //游戏结束,分值计算 if (world.state == World.WORLD_STATE_GAME_OVER) { state = GAME_OVER; if (lastScore >= Settings.highscores[4]) scoreString = "NEW HIGHSCORE: " + lastScore; else scoreString = "SCORE: " + lastScore; //获取最后分数 Settings.addScore(lastScore); //保存分数 Settings.save(); } }
这里我们每次碰到金币就会加上10分,到游戏结束后,将最终得分保存起来,同时更新排行榜。
Settings:
package com.zhf.mylibgdx; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import com.badlogic.gdx.Gdx; /** * 设置类:三个方法: 1.load()读取声音开关和最高分. 2.save()保存配置 3.addScore()最高分排行榜,对数组赋值。 * @author ZHF * */ public class Settings { //记录声音开起与关闭 public static boolean soundEnabled = true; //默认分数排行榜分数 public final static int[] highscores = new int[] {100, 80, 50, 30, 10}; //保存的文件名 public final static String file = ".superjumper"; /**加载配置文件**/ public static void load () { BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(Gdx.files.external(file).read())); soundEnabled = Boolean.parseBoolean(in.readLine()); for (int i = 0; i < 5; i++) { highscores[i] = Integer.parseInt(in.readLine()); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (in != null) in.close(); } catch (IOException e) { } } } /**保存分值**/ public static void save () { BufferedWriter out = null; try { //声音的开关 out = new BufferedWriter(new OutputStreamWriter(Gdx.files.external(file).write(false))); out.write(Boolean.toString(soundEnabled)); for (int i = 0; i < 5; i++) { //将分数写入文件 out.write(Integer.toString(highscores[i])); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) out.close(); } catch (IOException e) { } } } /**将所得分数与排行榜分数比较,从高到低重新排一下**/ public static void addScore (int score) { for (int i = 0; i < 5; i++) { if (highscores[i] < score) { for (int j = 4; j > i; j--) highscores[j] = highscores[j - 1]; highscores[i] = score; break; } } } }
再次运行代码,加了几个金币,分值发生变化了,回到主界面分数排行榜也发生变化了!(原先默认:100
,
80
,
50
,
30
,
10
)
接下来就是声音部分:
我们再次回到GameScreen中:
//实例化场景 worldListener = new WorldListener() { @Override public void jump () { Assets.playSound(Assets.jumpSound); } @Override public void highJump () { Assets.playSound(Assets.highJumpSound); } @Override public void hit () { Assets.playSound(Assets.hitSound); } @Override public void coin () { Assets.playSound(Assets.coinSound); } };
在监听器中给对应方法播放对应的声音,当然少不了在Asset中添加对声音资源的加载:
声明:
//声音部分 public static Sound clickSound; //按下音效 public static Music music; //背景音乐 public static Sound jumpSound; //跳跃 public static Sound highJumpSound; //高跳 public static Sound hitSound; //碰撞 public static Sound coinSound; //金币
实例化:
//背景音乐 music = Gdx.audio.newMusic(Gdx.files.internal("data/music.mp3")); music.setLooping(true); //循环 music.setVolume(0.5f); //大小 if (Settings.soundEnabled) music.play(); jumpSound = Gdx.audio.newSound(Gdx.files.internal("data/jump.ogg")); highJumpSound = Gdx.audio.newSound(Gdx.files.internal("data/highjump.ogg")); hitSound = Gdx.audio.newSound(Gdx.files.internal("data/hit.ogg")); coinSound = Gdx.audio.newSound(Gdx.files.internal("data/coin.ogg")); //点击音效 clickSound = Gdx.audio.newSound(Gdx.files.internal("data/click.ogg"));
ok!运行一下!貌似没有什么问题,对应的声音都播放出来了!
到此为止,libgdx中demo程序superjumper分版本的学习就结束了,由于本人也是边学边总结,中间肯定有许多地方讲解的不完善,不妥的地方,这里先想大家说声抱歉哈,希望大家发现问题能及时提出来,这样我也能进步么!这里还是那句话,在学习新东西的时候,我们的不求甚解也不失为一种快速掌握的好方法! 希望这一些列学习笔记能帮助到初学者!
完整代码下载:http://down.51cto.com/data/897232 代码里详细注解哦!