如何使用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類在遊戲開發中只能起到減速作用,遊戲模塊越多,意味着速度被放緩的可能性也就越大。所以遊戲引擎也並不一定在所有場景都是快速的,因使用者不同,引擎即可能成爲遊戲加速的工具,也可能淪爲遊戲減速的利器。

僅以小弟愚見,提高速度的最簡便訣竅在於——“能夠執行一次的地方,就絕不給他第二次執行的機會”,真能做到這樣,足矣。




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