如何使用LGame中的LTexturePack(移值到其它环境也行……)

来源:互联网 发布:为什么要优化系统 编辑:程序博客网 时间:2024/06/05 15:31
修正声明:

借写本例的机会,刚刚修正了某个【小】BUG。问题点在于LTexturePack的其中一个draw函数,本来在分图时需传入dx1,dy1,dx2,dy2,sx1,sy1,sx2,sy2等八个数值,结果小弟在接值时脑子进水,误将sx1,sx2,sy1,sy2写“扎堆”了(写错就是上述样子)……因为这是取图参数,即从sx1,sy1坐标开始取图,取到sx2,sy2的位置中止,弄错以后取出图立马变毕加索油画~所以小弟刚才将LGame-0.3.2-release这个包重新发了一遍,如果有用到LTexturePack的话请重新下载,抱歉抱歉(另外还改了3处问题)……

下载地址:http://loon-simple.googlecode.com/files/LGame-0.3.2-release.7z


关于TexturePack:

LGame中提供的LTexturePack类,是一个专门用于整合图像资源的辅助用类,它的最大作用是将许多零碎的图片画零为整,从而减少不必要的资源损耗。另一方面来讲,由于减少了渲染用纹理数量,避免了反复与图形系统交互,使用LTexturePack的速度通常会比单独使用零散LTexture为高。


最简单的使用方式:

package org.loon.test;import org.loon.framework.javase.game.GameScene;import org.loon.framework.javase.game.action.sprite.Arrow;import org.loon.framework.javase.game.core.graphics.Screen;import org.loon.framework.javase.game.core.graphics.opengl.GLEx;import org.loon.framework.javase.game.core.graphics.opengl.LTexturePack;import org.loon.framework.javase.game.core.input.LTouch;import org.loon.framework.javase.game.core.input.LTransition;import org.loon.framework.javase.game.core.timer.LTimerContext;public class LTexturePackTest extends Screen {LTexturePack imagePack;Arrow arrow;/** * 无Transition效果。 *  * PS:目前LGame设定为,如果首个Screen没有加载特效,将强制运行一个随机特效。 但是,返回Empty则不进行加载。 */public LTransition onTransition() {return LTransition.newEmpty();}public void onLoad() {imagePack = new LTexturePack();// 加载小图到LTexturePackimagePack.putImage("assets/h_a.png");imagePack.putImage("assets/h_b.png");imagePack.putImage("assets/h_c.png");imagePack.putImage("assets/e_a.png");imagePack.putImage("assets/e_b.png");imagePack.putImage("assets/e_c.png");// 宣布所有图像加载完毕(如果调用此函数,则释放所有已加载的资源,仅保留一块主纹理)imagePack.packed();}public void alter(LTimerContext timer) {}public void draw(GLEx g) {if (isOnLoadComplete()) {int size = 32;// 当执行glBegin后,将在GLEx触发一个渲染批处理事件,仅在执行glEnd后提交// 渲染内容到窗体。如果不调用此函数,则LTexturePack依旧可以执行,但是效率// 可能会受到一定影响(每次渲染都单独提交)。imagePack.glBegin();// LTexturePack中的数据可以按照索引加载imagePack.draw(0, 32, size);// 也可以按照文件名加载imagePack.draw("assets/h_b.png", 32, size += 32);imagePack.draw(2, 32, size += 32);size = 32;imagePack.draw(3, 256, size);imagePack.draw(4, 256, size += 32);imagePack.draw(5, 256, size += 32);// 提交渲染结果到游戏画面imagePack.glEnd();// 实例化一个动态箭头精灵if (arrow == null) {arrow = new Arrow(212, 212, 188, 188);add(arrow);}// 显示实际纹理g.drawTexture(imagePack.getTexture(), 32, size + 50);g.drawString("Texture", 235, size + 124);}}public void touchDown(LTouch e) {}public void touchDrag(LTouch e) {}public void touchMove(LTouch e) {}public void touchUp(LTouch e) {}public void dispose() {if (imagePack != null) {imagePack.dispose();imagePack = null;}}public static void main(String[] args) {GameScene game = new GameScene("LTexturePackTest", 480, 320);game.setShowFPS(true);game.setShowLogo(false);game.setScreen(new LTexturePackTest());game.showScreen();}}

这时大家可以看到,在上述画面中,其实只有游戏中箭头指出的地方才是真正的纹理。而其余细分处,仅是大图纹理中的一部分罢了,然而在用户眼中,这些又有什么不同呢?不过,我们所能节省出的图像资源,却是非常巨大的。

当然,如果载入的是连续画面,那么仅仅能得到原画根本没用,不过没关系,我们也可以继续细分出我们满意的画面(上图具体代码和下例重复,故不再赘述)。


图像移动:

package org.loon.test;import org.loon.framework.javase.game.GameScene;import org.loon.framework.javase.game.action.collision.Gravity;import org.loon.framework.javase.game.action.collision.GravityHandler;import org.loon.framework.javase.game.action.sprite.Bind;import org.loon.framework.javase.game.action.sprite.effect.SmashEffect;import org.loon.framework.javase.game.core.geom.RectBox;import org.loon.framework.javase.game.core.graphics.Screen;import org.loon.framework.javase.game.core.graphics.opengl.GLColor;import org.loon.framework.javase.game.core.graphics.opengl.GLEx;import org.loon.framework.javase.game.core.graphics.opengl.LTexturePack;import org.loon.framework.javase.game.core.input.LTouch;import org.loon.framework.javase.game.core.input.LTransition;import org.loon.framework.javase.game.core.timer.LTimer;import org.loon.framework.javase.game.core.timer.LTimerContext;public class LTexturePackTest extends Screen {SmashEffect smashEffect = new SmashEffect();// 显示用精灵大小final int show_size = 64;// 实际精灵大小final int really_size = 24;LTexturePack imagePack;/** * 建立一个移动对象,用以管理LTexturePack中图像的移动 */public class Move {RectBox rect = new RectBox(0, 0, show_size, show_size);LTimer timer = new LTimer(150);int id;float x, y;int action = 1;int type = -1;public void setX(float x) {this.x = x;rect.setX(x);}public void setY(float y) {this.y = y;rect.setY(y);}public float getX() {return x;}public float getY() {return y;}public boolean intersects(Move m) {return rect.intersects(m.rect);}public void update() {if (timer.action(elapsedTime)) {action++;if (action > 4) {action = 1;}}}}/** * 无Transition效果。 *  * PS:目前LGame设定为,如果首个Screen没有加载特效,将强制运行一个随机特效。 但是,返回Empty则不进行加载。 */public LTransition onTransition() {return LTransition.newEmpty();}public void onLoad() {imagePack = new LTexturePack();// 加载小图到LTexturePackint heroId = imagePack.putImage("assets/h_a.png");int enemyId = imagePack.putImage("assets/e_a.png");// 宣布所有图像加载完毕(如果调用此函数,则释放所有已加载的资源,仅保留一块主纹理)imagePack.packed();final Move moveHero = new Move();moveHero.id = heroId;moveHero.x = 0;moveHero.y = 128;moveHero.type = 0;final Move moveEnemy = new Move();moveEnemy.id = enemyId;moveEnemy.x = getWidth() - show_size;moveEnemy.y = 128;moveEnemy.type = 1;/** * 获得一个内置的重力管理器 *  * PS: 所有具备setX、setY函数的公有类(必须公有,否则反射不到……),都可以被GravityHandler绑定 */final GravityHandler gravityHandler = getGravityHandler();// 用重力控制器将moveHero对象打包,并且向右方匀速移动(速度100)Gravity g1 = new Gravity(moveHero);g1.setVelocityX(100);// 用重力控制器将moveHero对象打包,并且向左方匀速移动(速度100)Gravity g2 = new Gravity(moveEnemy);g2.setVelocityX(-100);// 添加重力干预gravityHandler.add(g1);gravityHandler.add(g2);// 构建重力监控GravityHandler.Update update = new GravityHandler.Update() {public void action(Gravity g, float x, float y) {// 让当前重力控制触边实效(具体到本例,人物也将消失)if (x < 0) {gravityHandler.remove(g);}if (x > getWidth() - show_size) {gravityHandler.remove(g);}}};// 添加监控(每次Gravity对象坐标发生变更时生效)gravityHandler.onUpdate(update);}public void alter(LTimerContext timer) {smashEffect.update(timer.getTimeSinceLastUpdate());}public void draw(GLEx g) {if (isOnLoadComplete()) {smashEffect.draw(g);// 当执行glBegin后,将在GLEx触发一个渲染批处理事件,仅在执行glEnd后提交// 渲染内容到窗体。如果不调用此函数,则LTexturePack依旧可以执行,但是效率// 可能会受到一定影响(每次渲染都单独提交)。imagePack.glBegin();// 获得重力控制器(如果绑定为Sprite,Actor,LComponent等对象则不必额外处理显示步骤,// 此处图像已统一打包入LTexturePack较为特殊)GravityHandler gravityHandler = getGravityHandler();// 获得重力实例数量int size = gravityHandler.getCount();// 保存上一个Move实例Move first = null;for (int i = 0; i < size; i++) {// 获得实例Gravity gravity = gravityHandler.get(i);Bind bind = gravity.getBind();// 反射回绑定的对象Move o = (Move) bind.ref();// 判定两个图像的移动位置是否发生了碰撞(这里使用了比较流氓(就俩,省了-_-)的方式,实际应该建立一个精灵预警区,// 然后搜索该区域内是否存在精灵,有的话再让移动中精灵与该精灵进行碰撞比较)if (first != null && first.intersects(o)) {// 碰撞后象征性的显示一个粉碎特效if (first.action != 5) {smashEffect.createSmallExplosionEffect(getHalfWidth(),getHalfHeight());}o.action = 5;first.action = 5;g.setColor(GLColor.red);}switch (o.type) {case 0:imagePack.draw(o.id, o.x, o.y, show_size, show_size,(o.action - 1) * really_size, 0, o.action* really_size, really_size);break;case 1:imagePack.draw(o.id, o.x, o.y, show_size, show_size,o.action * really_size, 0, (o.action - 1)* really_size, really_size);break;}first = o;o.update();}// 提交渲染结果到游戏画面imagePack.glEnd();}}public void touchDown(LTouch e) {}public void touchDrag(LTouch e) {}public void touchMove(LTouch e) {}public void touchUp(LTouch e) {}public void dispose() {if (imagePack != null) {imagePack.dispose();imagePack = null;}}public static void main(String[] args) {GameScene game = new GameScene("LTexturePackTest", 480, 320);game.setShowFPS(true);game.setShowLogo(false);game.setScreen(new LTexturePackTest());game.showScreen();}}

让LTexturePack中的两个精灵进行单挑

多个图像移动:

package org.loon.test;import java.util.ArrayList;import org.loon.framework.javase.game.GameScene;import org.loon.framework.javase.game.action.collision.Gravity;import org.loon.framework.javase.game.action.collision.GravityHandler;import org.loon.framework.javase.game.core.LSystem;import org.loon.framework.javase.game.core.geom.RectBox;import org.loon.framework.javase.game.core.graphics.Screen;import org.loon.framework.javase.game.core.graphics.opengl.GLEx;import org.loon.framework.javase.game.core.graphics.opengl.LTexturePack;import org.loon.framework.javase.game.core.input.LTouch;import org.loon.framework.javase.game.core.input.LTransition;import org.loon.framework.javase.game.core.timer.LTimer;import org.loon.framework.javase.game.core.timer.LTimerContext;public class LTexturePackTest extends Screen {ArrayList<Move> moveList;// 显示用精灵大小final int show_size = 64;// 实际精灵大小final int really_size = 24;LTexturePack imagePack;/** * 建立一个移动对象,用以管理LTexturePack中图像的移动 */public class Move {RectBox rect = new RectBox(0, 0, show_size, show_size);LTimer timer = new LTimer(150);int id;float x, y;int action = 1;int type = -1;public Move(int id, int type, float x, float y) {this.id = id;this.type = type;this.x = x;this.y = y;}public void setX(float x) {this.x = x;rect.setX(x);}public void setY(float y) {this.y = y;rect.setY(y);}public float getX() {return x;}public float getY() {return y;}public boolean intersects(Move m) {return rect.intersects(m.rect);}public void update() {if (timer.action(elapsedTime)) {action++;if (action > 4) {action = 1;}}}}public LTransition onTransition() {return LTransition.newCrossRandom();}public void onLoad() {imagePack = new LTexturePack();// 加载小图到LTexturePackint heroImgId = imagePack.putImage("assets/h_a.png");int enemyImgId = imagePack.putImage("assets/e_a.png");// 宣布所有图像加载完毕(如果调用此函数,则释放所有已加载的资源,仅保留一块主纹理;如果不调用此函数,// LTexturePack将允许动态增减图像,但是已加载的小图资源不会自动释放(可手动释放,或者dispose全部清空))imagePack.packed();// 构建一个Move集合,用以控制图像移动与显示this.moveList = new ArrayList<Move>(10);moveList.add(new Move(heroImgId, 0, 0, 32));moveList.add(new Move(heroImgId, 0, 0, 136));moveList.add(new Move(heroImgId, 0, 0, 220));moveList.add(new Move(enemyImgId, 1, getWidth() - show_size, 32));moveList.add(new Move(enemyImgId, 1, getWidth() - show_size, 136));moveList.add(new Move(enemyImgId, 1, getWidth() - show_size, 220));/** * 获得一个内置的重力管理器 *  * PS: 所有具备setX、setY函数的公有类(必须公有,否则反射不到……),都可以被GravityHandler绑定 */final GravityHandler gravityHandler = getGravityHandler();// 批量载入重力控制for (Move m : moveList) {Gravity g = new Gravity(m);if (m.type == 0) {g.setVelocityX(LSystem.getRandom(50, 80));} else if (m.type == 1) {g.setVelocityX(-LSystem.getRandom(50, 100));}gravityHandler.add(g);}// 构建重力监控GravityHandler.Update update = new GravityHandler.Update() {public void action(Gravity g, float x, float y) {synchronized (moveList) {Move src = (Move) g.getBind().ref();// 仅让我方与敌人产生碰撞if (src.type == 0 && src.action != 5) {for (Move obj : moveList) {if (src.type != obj.type && !src.equals(obj)&& src.intersects(obj)) {src.action = 5;obj.action = 6;}}}if (src.action < 5) {// 让移动有一定几率上下浮动(比较贴近真实行走)g.setVelocityY(LSystem.getRandom(-100, 100));} else {// 打人或挨打时不能上下浮动g.setVelocityY(0);}// 让当前重力控制触边实效(更上例不同,此方式仅暂停重力控制,而非删除,这意味着可以唤醒)if (x < 0) {g.setEnabled(false);}if (x > getWidth() - show_size) {g.setEnabled(false);}}}};// 添加监控(每次Gravity对象坐标发生变更时生效)gravityHandler.onUpdate(update);}public void alter(LTimerContext timer) {}public void draw(GLEx g) {if (isOnLoadComplete()) {synchronized (moveList) {// 当执行glBegin后,将在GLEx触发一个渲染批处理事件,仅在执行glEnd后提交// 渲染内容到窗体。如果不调用此函数,则LTexturePack依旧可以执行,但是效率// 可能会受到一定影响(每次渲染都单独提交)。imagePack.glBegin();for (Move o : moveList) {switch (o.type) {case 0:imagePack.draw(o.id, o.x, o.y, show_size, show_size,(o.action - 1) * really_size, 0, o.action* really_size, really_size);break;case 1:imagePack.draw(o.id, o.x, o.y, show_size, show_size,o.action * really_size, 0, (o.action - 1)* really_size, really_size);break;}o.update();}// 提交渲染结果到游戏画面imagePack.glEnd();}}}public void touchDown(LTouch e) {}public void touchDrag(LTouch e) {}public void touchMove(LTouch e) {}public void touchUp(LTouch e) {}public void dispose() {if (imagePack != null) {imagePack.dispose();imagePack = null;}}public static void main(String[] args) {GameScene game = new GameScene("LTexturePackTest", 480, 320);game.setShowFPS(true);game.setShowLogo(false);game.setScreen(new LTexturePackTest());game.showScreen();}}

让单独纹理中的多个精灵进行混战



——————————

除了动态加载小图为统一纹理外,LTexturePack工具也允许根据XML设定来分解单独的大图为指定格式小图。

以上例为准,当我们执行下列代码:

System.out.println(imagePack.toString());

将可以在控制台见到如下输出:

<?xml version="1.0" standalone="yes" ?>
<pack file="null">
<block id="0" name="assets/h_a.png" left="0" top="0" right="144" bottom="24"/>
<block id="1" name="assets/e_a.png" left="0" top="24" right="144" bottom="48"/>
</pack>


这一输出,其实就是LTexturePack工具的XML文档解析格式。如果有单独的复合素材图(所有小图合成一张大图的那种),只要我们构建一个符合上述格式的XML文档,就能直接导入LTexturePack中使用。具体来说,<pack file="null">这项必须存在,其中file需要填写为素材图所在路径;id、left、top、right、bottom这五项也必须存在,否则无法定位和获取小图;name项如果想根据name取小图时必须存在,如果无此要求可不填。

另外,在LAE和LSE包中并无此工具类及类似工具存在。原因在于,使用LTexturePack工具虽然可以有效的节约图像资源,可惜调用方法不够直观,与LAE和LSE包的Easy特性并不相符。而且在Bitmap分图时,效率损耗要大于Texture,虽然能节约一定的显存空间,却会使本就不快的Canvas绘图变得更慢。因此,没有为LAE和LSE包提供相关扩展。

____________________

示例中用到了一些MD版梦幻模拟战的素材,资源在此(用什么图原理都一样):

http://115.com/file/dnr4wd6b#


临时想到的一些杂项:

1、关于LGame屏幕设置:

LGame默认提供有的Activity子类LGameAndroid2DActivity,只要继承该类的Activity就可以获得onMain、onGamePaused、onGameResumed三个接口。通常来说,在onMain中就足以完成我们所有的游戏初始化设置。

下面是一个最典型的基本设置:

public void onMain() {// 横屏,全屏显示this.initialization(true,LMode.Fill);// 不显示logothis.setShowLogo(false);// 显示实际fpsthis.setShowFPS(true);// 注入游戏Screenthis.setScreen(new MyScreen());// 显示画面this.showScreen();}

如果我们设置initialization(true),这时游戏屏幕为横屏显示,而initialization(false)为竖屏显示,在布尔值后还可追加一项LMode,通过该类可以设定屏幕的显示方式,如全屏、自适屏、默认大小等等。而在调用initialization之前,我们可以调用maxScreen函数设定默认的屏幕大小,屏幕缩放以此作为依据,如果不进行设置,则默认游戏屏幕大小为480x320。

2、怎样提高游戏速度:

单以效率论,除了能起到缓存(或跳过冗余步骤)的模块外,绝大多数Java类在游戏开发中只能起到减速作用,游戏模块越多,意味着速度被放缓的可能性也就越大。所以游戏引擎也并不一定在所有场景都是快速的,因使用者不同,引擎即可能成为游戏加速的工具,也可能沦为游戏减速的利器。

仅以小弟愚见,提高速度的最简便诀窍在于——“能够执行一次的地方,就绝不给他第二次执行的机会”,真能做到这样,足矣。




原创粉丝点击