借寫本例的機會,剛剛修正了某個【小】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類在遊戲開發中只能起到減速作用,遊戲模塊越多,意味着速度被放緩的可能性也就越大。所以遊戲引擎也並不一定在所有場景都是快速的,因使用者不同,引擎即可能成爲遊戲加速的工具,也可能淪爲遊戲減速的利器。
僅以小弟愚見,提高速度的最簡便訣竅在於——“能夠執行一次的地方,就絕不給他第二次執行的機會”,真能做到這樣,足矣。