如何使用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类在游戏开发中只能起到减速作用,游戏模块越多,意味着速度被放缓的可能性也就越大。所以游戏引擎也并不一定在所有场景都是快速的,因使用者不同,引擎即可能成为游戏加速的工具,也可能沦为游戏减速的利器。

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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章