下载地址:http://loon-simple.googlecode.com/files/LGame-0.2.94.7z
此版对于LGame-0.2.9中所反映出的问题进行了集中修正,改正所有已知的0.2.9版BUG,并扩展了LGame组件及精灵API,提供了组件化的Layer(继承自LComponent,拥有LGame组件特性及大多数常规Layer API(比如AndEngine拥有的~))。
下面小弟提供有两个非常简单的Layer使用示例(其实本来想发塔防的例子,结果忘了拷最新代码回家(零零散散写的,就差那么一点,懒得重写),临时写两个简单的充数,塔防下周上传,总之代码极简~),示例中尽可能的使用了Layer API。
Sample1:
这是一个支持重力感应及键盘操作的简单机战DEMO,使用了最新开发的Layer组件(机体为某11区产物)。
启动LGame:
package org.loon.test; import org.loon.framework.android.game.LGameAndroid2DActivity; public class Main extends LGameAndroid2DActivity { public void onMain() { // 开启重力感应 this.setupGravity(); // 横屏 this.initialization(true); // 初始画面使用Test2 this.setScreen(new Test2()); // 不显示logo this.setShowLogo(false); // 显示实际fps this.setShowFPS(true); // 显示画面 this.showScreen(); } }
游戏窗体制作:
package org.loon.test; import org.loon.framework.android.game.core.LInput; import org.loon.framework.android.game.core.LSystem; import org.loon.framework.android.game.core.graphics.LImage; import org.loon.framework.android.game.core.graphics.Screen; import org.loon.framework.android.game.core.graphics.device.LGraphics; import org.loon.framework.android.game.core.graphics.window.LPaper; import org.loon.framework.android.game.core.graphics.window.actor.Actor; import org.loon.framework.android.game.core.graphics.window.actor.ActorSpeed; import org.loon.framework.android.game.core.graphics.window.actor.Layer; import org.loon.framework.android.game.core.graphics.window.actor.Speed; import org.loon.framework.android.game.utils.GraphicsUtils; import android.view.KeyEvent; import android.view.MotionEvent; public class Test2 extends Screen { /** * 杂兵用类 */ class Enemy extends ActorSpeed { private boolean exploded; private int health; public Enemy(LImage image) { super(new Speed(LSystem.random.nextInt(360), 2.0D)); setEnemy(image, image.getWidth()); // Action动作延迟50毫秒执行 setDelay(50); } public void action(long t) { if (this.exploded) { return; } move(); } public void setEnemy(LImage image, int size) { this.health = size; this.setImage(image); } private void explode() { if (this.exploded) { return; } this.exploded = true; getLayer().removeObject(this); } public void hit(int damage) { if (this.exploded) { return; } this.health -= damage; if (this.health <= 0) { explode(); } } } /** * 子弹用类 */ class Fire extends ActorSpeed { private int damage = 100; private int life = 35; public Fire(Speed speed, LImage image, int rotation) { super(speed); setImage(image); setRotation(rotation); increaseSpeed(new Speed(rotation, 7.0D)); // Action动作延迟50毫秒执行 setDelay(50); } public void action(long elapsedTime) { // 查询子弹生命(持久力)是否耗尽 if (this.life <= 0) { getLayer().removeObject(this); } else { // 移动子弹 move(); // 查询子弹是否超过边界 if (!getLayer().getCollisionBox().contains(getRectBox())) { getLayer().removeObject(this); return; } // 获得杂兵是否与子弹碰撞,返回唯一值(第一个查询到的) // PS:此仅为矩形碰撞,更精确可用SpriteImage取Polygon比对 Enemy e = (Enemy) getOnlyCollisionObject(Enemy.class); if (e != null) { getLayer().removeObject(this); e.hit(this.damage); } this.life -= 1; } } } /** * 主角用类 */ class Hero extends ActorSpeed { private Speed acceleration = new Speed(0, 0); private LImage fireImage; public Hero(LImage image) { this.setImage(image); } public void setAcceleration(int direction, double length) { acceleration.set(direction, length); setDelay(100); } public void action(long elapsedTime) { LInput input = getLayer().screenInput(); // 根据左右摇摆来旋转角色 if (input.isKeyDown(KeyEvent.KEYCODE_DPAD_LEFT)) { setRotation(getRotation() - 3); } if (input.isKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT)) { setRotation(getRotation() + 3); } if (input.isKeyDown(KeyEvent.KEYCODE_ENTER)) { fire(); } move(); } private void fire() { // 当子弹不存在于屏幕时 if (getCollisionObjects(Fire.class).isEmpty()) { Fire f = new Fire(getSpeed().copy(), fireImage, getRotation()); getLayer().addObject(f, getX(), getY()); f.move(); } } public LImage getFireImage() { return fireImage; } public void setFireImage(LImage fireImage) { this.fireImage = fireImage; } } public void onLoad() { // 将重力方向转为键盘方向 setGravityToKey(true); // 构建一个等于Screen大小的图层,不锁定角色移动范围(为true时将刚性制约角色坐标为Layer内, // 无论拖拽还是set坐标都无法超出) final Layer layer = new Layer(getWidth(), getHeight(), false); layer.setBackground("assets/back.png"); // 杂兵出现的边界位置 final int enemyWidth = getWidth() - 70; final int enemyHeight = getHeight() - 70; // 杂兵1使用的图形大小 final int e1size = 68; // 主角机图片 final LImage a1 = GraphicsUtils.loadImage("assets/a1.png"); // 敌人图片 final LImage e1 = GraphicsUtils.loadScaleImage("assets/e1.png", e1size, e1size); // 子弹图片 final LImage f1 = GraphicsUtils.loadImage("assets/f1.png"); // 此项参数决定了Layer拖拽是否受到限制;当Layer小于Screen大小时,Layer拖拽无法 // 超过Screen的显示范围;当Layer大于Screen大小时,Screen仅允许拖拽Layer能够被 // 显示出来的部分(比如设定一个800x480的Layer在480x320的Screen中,那么无论如何 // 拖拽也仅可见800x480范围内的Layer画面,而不会出现黑边),默认此项开启。 // PS:与标准LGame组件一样,Layer能被拖拽的大前提是setLocked项必须设为false // layer.setLimitMove(false); // layer.setLocked(false); // 此项参数决定了Layer中Actor角色是否能被直接拖拽,默认为true,即可以直接拖拽 // layer.setActorDrag(false); // 此项参数决定了Layer中Actor角色是否响应触屏点击,默认为true,即可以直接获得点击事件 // layer.setTouchClick(false); // 为Layer加载背景 // layer.setBackground("assets/back.png"); // 构建LPaper作为启动按钮,点击时触发【狼与车夫】机体的战斗事件 LPaper button1 = new LPaper("assets/b1.png") { public void downClick() { // 获得Button所在Layer Layer superLayer = getTopLayer(); // 清空Layer中所有LPaper组件 superLayer.remove(LPaper.class); // 构建【狼与车夫】机体 Hero hero = new Hero(a1); hero.setFireImage(f1); hero.setAcceleration(0, 0.05D); // 添加主角机并居中 superLayer.addObject(hero); superLayer.centerOn(hero); // 在Layer上设置杂兵 superLayer.addObject(new Enemy(e1), enemyWidth, enemyHeight); superLayer.addObject(new Enemy(e1), enemyWidth, 70); superLayer.addObject(new Enemy(e1), 70, enemyHeight); superLayer.addObject(new Enemy(e1), 70, 70); superLayer.addObject(new Enemy(e1), 70, enemyHeight); superLayer.addObject(new Enemy(e1), 300, 70); superLayer.addObject(new Enemy(e1), 300, enemyHeight); superLayer.addObject(new Enemy(e1), enemyWidth, 300); // 构建一个固定位置的Actor,充当射击按钮(用LPaper、LButton亦可,此处纯演示用) Actor click = new Actor("assets/b2.png") { public void downClick(int x, int y) { // 当点击此角色时,伪造enter事件给Layer(子弹发射) setKeyDown(KeyEvent.KEYCODE_ENTER); } public void upClick(int x, int y) { // 释放enter事件 setKeyUp(KeyEvent.KEYCODE_ENTER); } }; // 设定此Actor不能被拖拽 click.setDrag(false); superLayer.addObject(click, 10, this.getHeight() - 70); } }; // 居中显示按钮1 layer.centerOn(button1); // 在Layer中添加按钮1 layer.add(button1); // 将Layer添加到Screen当中 add(layer); } public void draw(LGraphics g) { } public void onTouchDown(MotionEvent e) { } public void onTouchMove(MotionEvent e) { } public void onTouchUp(MotionEvent e) { } }
游戏画面如下所示:
Sample2:
这是一个支持重力感应及键盘操作的上楼梯游戏DEMO,使用了最新开发的Layer组件(素材来自IPhone游戏)。
启动LGame:
package org.loon.test; import org.loon.framework.android.game.LGameAndroid2DActivity; public class Main extends LGameAndroid2DActivity { public void onMain() { // 开启重力感应 setupGravity(); // 竖屏 this.initialization(false); // 初始画面使用Test1 this.setScreen(new Test1()); // 不显示logo this.setShowLogo(false); // 显示实际fps this.setShowFPS(true); // 显示画面 this.showScreen(); } }
游戏窗体制作:
package org.loon.test; import org.loon.framework.android.game.action.map.shapes.RectBox; import org.loon.framework.android.game.core.LInput; import org.loon.framework.android.game.core.graphics.LColor; import org.loon.framework.android.game.core.graphics.LImage; import org.loon.framework.android.game.core.graphics.Screen; import org.loon.framework.android.game.core.graphics.device.LGraphics; import org.loon.framework.android.game.core.graphics.window.actor.Actor; import org.loon.framework.android.game.core.graphics.window.actor.ActorLayer; import org.loon.framework.android.game.core.graphics.window.actor.Layer; import org.loon.framework.android.game.utils.GraphicsUtils; import android.view.KeyEvent; import android.view.MotionEvent; public class Test1 extends Screen { final static LImage right = GraphicsUtils .loadNotCacheImage("assets/doodler.png"); final static LImage left = right.getMirrorImage(); final static LImage shooting = GraphicsUtils.loadNotCacheImage( "assets/shooting.png").scaledInstance(25, 40); final static LImage title = GraphicsUtils .loadNotCacheImage("assets/title.png"); final static LImage arrow = GraphicsUtils .loadNotCacheImage("assets/arrow.png"); final static LImage groundImage = GraphicsUtils .loadNotCacheImage("assets/ground.png"); class TestLayer extends Layer { private boolean started; private boolean once; private int scrollSpeed; private boolean scroll; private boolean fall; private boolean ended; private int height; public TestLayer() { // Layer大小为300x400,不限制角色活动区域(为true时角色无法掉出屏幕) super(300, 400, false); // 禁止画面中的角色被拖拽 setActorDrag(true); // 不锁定Layer拖拽 setLocked(false); // 无拖拽限制 setLimitMove(false); // 设定Layer中Action触发间隔为500毫秒 setDelay(300); // 初始化Layer画面 init(); } /** * 初始化游戏状态 * */ public void init() { cleanup(); this.started = false; this.addObject(new Ground(), 31, 312); this.addObject(new Doodler(false), 31, 195); this.setBackground("assets/title.png"); this.once = true; this.started = false; this.height = 0; this.fall = false; this.ended = false; } public void downClick(int x, int y) { // 检查是否点击了图片上的游戏开始按钮(也可以直接add一个按钮) if (!this.started) { if ((x >= 48 && x < 140) && (y >= 104 && y <= 130)) { this.started = true; } } } public void action(long elapsedTime) { if (this.started && this.once) { // 以瓦片方式填充背景(即拼装等于Layer大小的背景) setTileBackground("assets/paper.png"); cleanup(); setLevel(1); } if (this.fall) { init(); } } public void cleanup() { this.once = false; // 删除所有Doodler和Ground对象 this.removeObject(Doodler.class); this.removeObject(Ground.class); } public void setLevel(int level) { switch (level) { case 1: gamePlay(); } } /** * 在Layer上绘制得分 */ public void paint(LGraphics g) { g.setColor(LColor.black); g.drawString(height + " doodles.", 200, 390); } /** * 初始化游戏 * */ public void gamePlay() { int move = 0; // 添加Doodler角色到画面中心 addObject(new Doodler(), (getWidth() - groundImage.getWidth()) / 2, 100); // 添加最底层Ground addObject(new Ground(false), move, 390); addObject(new Ground(false), move += groundImage.getWidth(), 390); addObject(new Ground(false), move += groundImage.getWidth(), 390); addObject(new Ground(false), move += groundImage.getWidth(), 390); addObject(new Ground(false), move += groundImage.getWidth(), 390); addObject(new Ground(false), move += groundImage.getWidth(), 390); // 随机生成三个跳跃用位置,并以此添加Ground RectBox[] boxs = getRandomLayerLocation(groundImage.getWidth(), groundImage.getHeight(), 3); addObject(new Ground(), boxs[0].x, 250); addObject(new Ground(), boxs[1].x, 150); addObject(new Ground(), boxs[2].x, 50); } } // 向上箭头 class Arrow extends Actor { private boolean flag; public Arrow() { this.setImage("assets/arrow.png"); this.flag = false; } public void action(long elapsedTime) { if (!flag) { this.setX((getLayer().getWidth() - getWidth()) / 2); flag = true; } } } // 跳跃用瓦片 class Ground extends Actor { private boolean hasBeenBounced; private int x_pos; private int y_pos; public Ground() { this.hasBeenBounced = false; this.setImage(groundImage); // 设定Action触发间隔为50毫秒 this.setDelay(50); } public Ground(boolean bouncable) { this.hasBeenBounced = true; this.setImage(groundImage); // 设定Action触发间隔为50毫秒 this.setDelay(50); } public void action(long elapsedTime) { TestLayer layer = (TestLayer) getLayer(); if (layer.scroll == true) { scroll(layer.scrollSpeed); } if (layer.fall == true) { fall(layer.scrollSpeed); } if (this.y_pos < 0) { layer.removeObject(this); return; } } protected void addLayer(ActorLayer world) { this.x_pos = getX(); this.y_pos = getY(); } public void scroll(int speed) { if (speed > 0) { this.y_pos += speed; setLocation(this.x_pos, this.y_pos); } } public void fall(int speed) { if (!((TestLayer) getLayer()).ended) { this.y_pos += speed; setLocation(this.x_pos, this.y_pos); } } } // 跳跃用游戏角色 class Doodler extends Actor { private float ys; private float xs; private boolean canMove; private int hits; public Doodler() { setImage(right); // 设定Action触发间隔为50毫秒 this.setDelay(50); this.canMove = true; } public Doodler(boolean moveable) { setImage(right); // 设定Action触发间隔为50毫秒 this.setDelay(50); this.canMove = moveable; } public void action(long elapsedTime) { TestLayer layer = (TestLayer) getLayer(); if (!layer.fall) { move(); } if (this.ys > 12.0F) { this.ys = 10.0F; } if (this.canMove) { keys(); } this.ys += 0.3F; setLocation(this.getX() + (int) this.xs, this.getY() + (int) this.ys); limitJump(); if ((this.canMove & this.getY() < layer.getHeight())) { scrollUp(); } if (this.getY() > layer.getHeight()) { fall(); } layer.height = this.hits; arrow(); } public void move() { // 判定角色是否和任意Ground对象碰撞 Ground g = (Ground) getOnlyCollisionObject(Ground.class); if ((g != null) && (this.ys > 0.0F) && getX() + groundImage.getWidth() >= g.getX() && getY() + groundImage.getHeight() <= g.getY()) { this.ys = -10.0F; // 生成跳跃用瓦片 if ((this.canMove & !g.hasBeenBounced)) { ActorLayer world = getLayer(); g.hasBeenBounced = true; RectBox[] boxs = world.getRandomLayerLocation(groundImage .getWidth(), groundImage.getHeight(), 2); world.addObject(new Ground(), boxs[0].x, 0); world.addObject(new Ground(), boxs[1].x, 70); } } } /** * 判定键盘事件(已设定此部分同重力感应联动,重力事件也会转到此) * */ public void keys() { LInput input = getLayer().screenInput(); if (input.isKeyDown(KeyEvent.KEYCODE_DPAD_UP)) { move_up(9); setImage(shooting); } if (input.isKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT)) { this.xs += 0.25F; setImage(right); } if (input.isKeyDown(KeyEvent.KEYCODE_DPAD_LEFT)) { this.xs -= 0.25F; setImage(left); } if ((!input.isKeyDown(KeyEvent.KEYCODE_DPAD_LEFT)) && (!input.isKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT)) && (this.xs != 0.0F)) { this.xs = 0.0F; } } /** * 限制角色移动区域(上、左、右) * */ public void limitJump() { RectBox rect = getRectBox(); int limitWidth = getLayer().getWidth() - rect.getWidth(); int limitHeight = 0 + rect.getHeight(); if (this.getX() > limitWidth) { setLocation(limitWidth, getY()); } if (this.getX() < 0) { setLocation(0, getY()); } if (this.getY() < limitHeight) { setLocation(getX(), limitHeight); } } /** * 调整滚动区域 * */ public void scrollUp() { TestLayer layer = (TestLayer) getLayer(); if (this.getY() < 200 && this.getY() > 100) { layer.scrollSpeed = (int) (-this.ys); layer.scroll = true; this.hits += 1; } else if (this.getY() < 100) { layer.scrollSpeed = ((int) (-this.ys) * 2); layer.scroll = true; this.hits += 1; } else { layer.scroll = false; } } public void fall() { TestLayer layer = (TestLayer) getLayer(); layer.fall = true; layer.scrollSpeed = (int) (-this.ys); } public void arrow() { // 当游戏角色触顶时绘制向上箭头 if ((this.getY() <= groundImage.getWidth() & getLayer() .getCollisionObjects(Arrow.class).isEmpty())) { getLayer().addObject(new Arrow()); } // 当游戏角色非触顶时删除向上箭头 if (((this.getY() > groundImage.getWidth()) && (!getLayer() .getCollisionObjects(Arrow.class).isEmpty()))) { getLayer().removeObject(Arrow.class); } } } public void onLoad() { // 将重力方向转为键盘方向 setGravityToKey(true); // 构建Layer TestLayer test = new TestLayer(); // 居中显示 centerOn(test); // 添加到Screen add(test); } public void draw(LGraphics g) { } public void onTouchDown(MotionEvent e) { } public void onTouchMove(MotionEvent e) { } public void onTouchUp(MotionEvent e) { } }
游戏画面如下所示:
上述示例完整工程已置于更新的LGame-0.2.94中。
下载地址:http://loon-simple.googlecode.com/files/LGame-0.2.94.7z
___________________________
小弟本来准备1月1号写完塔防示例并发布,结果元旦那几天硬是没时间写(无限串门,杯具,估计过年也是~),前一段看了些AndEngine开发的游戏源码,觉得LGame在某些类型游戏的开发上还是太繁琐了(不过,移植J2ME或JavaSE游戏别的引擎望尘莫及~),所以突击完成了Layer,把塔防例子也改成了基于Layer开发,这样极大简化了代码量,也更符合LGame的正式名——Loon……
关于此0.2.94版,如果有问题希望大家能告诉小弟一声,小弟争取在0.3之前一起改正,也会有一些新的API在0.3版加入,目前业余进行SLG模块Android化中,预计2月中旬可以完成(其实基本写完,正把JavaSE版转Android版,此模块会使用独立jar(300多KB),支持我军、敌军、友军配置,支持战场剧情等(话说机战系列支持的小弟就支持))。
下周会发塔防示例,这次不会变了。