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

修正声明:

借写本例的机会,刚刚修正了某个【小】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(); // 加载小图到LTexturePack imagePack.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(); // 加载小图到LTexturePack int 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(); // 加载小图到LTexturePack int 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); // 不显示logo this.setShowLogo(false); // 显示实际fps this.setShowFPS(true); // 注入游戏Screen this.setScreen(new MyScreen()); // 显示画面 this.showScreen(); }
如果我们设置initialization(true),这时游戏屏幕为横屏显示,而initialization(false)为竖屏显示,在布尔值后还可追加一项LMode,通过该类可以设定屏幕的显示方式,如全屏、自适屏、默认大小等等。而在调用initialization之前,我们可以调用maxScreen函数设定默认的屏幕大小,屏幕缩放以此作为依据,如果不进行设置,则默认游戏屏幕大小为480x320。

2、怎样提高游戏速度:

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

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




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值