借写本例的机会,刚刚修正了某个【小】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类在游戏开发中只能起到减速作用,游戏模块越多,意味着速度被放缓的可能性也就越大。所以游戏引擎也并不一定在所有场景都是快速的,因使用者不同,引擎即可能成为游戏加速的工具,也可能沦为游戏减速的利器。
仅以小弟愚见,提高速度的最简便诀窍在于——“能够执行一次的地方,就绝不给他第二次执行的机会”,真能做到这样,足矣。