關於LGame中的LTextureList


LTextureList,是一個與前例介紹的LTexturePack形似而神異的不同存在。


兩者的差異在於:

LTexturePack專注於“分散紋理的統一管理,會將注入當中的所有小圖合併爲一”。

LTextureList專注於“分散紋理的分散管理,會保留所有小圖的引用而分別調用”。

事實上,由於Android中提供個單獨程序的圖像空間非常有限,無論怎麼有效釋放,Bitmap都不能同時存在太多。而LTextureList就是專爲處理海量圖片所準備的,在配置了相關xml文檔後,LTextureList將僅在執行對應名稱紋理的loadTexture時,纔會加載LTexture(也就是所謂的“惰性加載”)。而不會一次性加載完畢,且加載一次後會立即產生緩存,只要不註銷資源,即使再次loadTexture也不會重複加載。因此,即使您在xml中填入幾萬張圖片地址也無所謂,完全不必擔心實時加載所帶來的程序癱瘓。

LTextureList最簡單的用法莫過於此:

images.xml配置:

<?xml version="1.0" standalone="yes"?>
<images>
    <ref name="hero">
        <image src="assets/hero_a.png">
        </image>
    </ref>
    <ref name="enemy">
        <image src="assets/enemy_a.png">
        </image>
    </ref>
</images>

對應的java配置如下:

package org.loon.test;

import org.loon.framework.javase.game.GameScene;
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.LTextureList;
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 LTextureListTest extends Screen {

    LTextureList list;

    public LTransition onTransition() {
        return LTransition.newEmpty();
    }

    public void onLoad() {
        // 把Screen背景色定爲紅色
        setBackground(GLColor.red);
        // 從指定xml文檔獲取LTextureList實例
        list = new LTextureList("assets/images.xml");
    }

    public void alter(LTimerContext timer) {

    }

    public void draw(GLEx g) {
        if (isOnLoadComplete()) {
            g.drawTexture(list.loadTexture("hero"), 32, 32);
            g.drawTexture(list.loadTexture("enemy"), 32, 64);
        }
    }

    public void touchDown(LTouch e) {
    }

    public void touchDrag(LTouch e) {

    }

    public void touchMove(LTouch e) {

    }

    public void touchUp(LTouch e) {

    }

    public void dispose() {
        if (list != null) {
            list.dispose();
            list = null;
        }
    }

    public static void main(String[] args) {
        GameScene game = new GameScene("LTextureListTest", 480, 320);
        game.setShowFPS(true);
        game.setShowLogo(false);
        game.setScreen(new LTextureListTest());
        game.showScreen();
    }
}

運行上述程序後,我們會得到如下顯示畫面:



此時,如果有一些基本的圖像變動,也不需再修改任何程序代碼,僅微調xml參數即可,比如,當我們需要hero去掉黑底時,只要進行如下設置:
<?xml version="1.0" standalone="yes"?>
<images>
    <ref name="hero">
        <image src="assets/hero_a.png">
          <mask r="0" g="0" b="0" />
        </image>
    </ref>
    <ref name="enemy">
        <image src="assets/enemy_a.png">
        </image>
    </ref>
</images>

這時,我們在image元素中,加入了<mask r="0" g="0" b="0" />一行,即過濾掉0,0,0像素,這時我們再次執行程序,畫面將變爲如下所示。


怎麼樣,大家發現變化了沒有?原來【hero系列】的黑底,是否已經消失不見了呢?可惜,這樣依舊是一組連續的圖片,這樣的“英雄連”我們還無法直接在遊戲中使用,所以,我們要繼續修改xml設置,直到生成的紋理被用戶能夠接受爲止。

<?xml version="1.0" standalone="yes"?>
<images>
    <ref name="hero">
        <image src="assets/hero_a.png" x="0" y="0" w="24" h="24" scale="2">
          <mask r="0" g="0" b="0" />
        </image>
    </ref>
    <ref name="enemy">
        <image src="assets/enemy_a.png">
        </image>
    </ref>
</images>

這次,增加了x="0" y="0" w="24" h="24"這樣一行文字,並且添加有scale="2"。這是幹什麼用的呢?其實在上例講到LTexturePack時,小弟已經做過了類似的事情,不過是告知系統從一整張圖片的x=0,y=0處開始取圖,一直取到w=24,h=24處停止罷了(也就是第一個“士兵”的圖片位置)。不過這裏有處與LTexturePack明顯的差別,既LTexturePack中所取得的小圖是系統從一整張大紋理中“虛擬”出來的,不會消耗任何額外的內存或顯存空間,而LTextureList所取出的小圖,對系統而言可是真實紋理,顯存中實際存在的一張圖像。



當然,LTextureList也可以單純作爲圖像List使用,設定統一的setBlendMode改變圖像的混圖模式,透明度等等,比如:
package org.loon.test;

import org.loon.framework.javase.game.GameScene;
import org.loon.framework.javase.game.core.graphics.Screen;
import org.loon.framework.javase.game.core.graphics.opengl.GL;
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.LTextureList;
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 LTextureListTest extends Screen {

    LTextureList list;

    public LTransition onTransition() {
        return LTransition.newEmpty();
    }

    public void onLoad() {
        // 把Screen背景色定爲紅色
        setBackground(GLColor.red);
        list = new LTextureList();
        list.add("assets/hero_a.png", 32, 32);
        list.add("assets/enemy_a.png", 32, 64);
        list.setBlendMode(GL.MODE_COLOR_MULTIPLY);
    }

    public void alter(LTimerContext timer) {

    }

    public void draw(GLEx g) {
        if (isOnLoadComplete()) {
            list.draw(g);
        }
    }

    public void touchDown(LTouch e) {
    }

    public void touchDrag(LTouch e) {

    }

    public void touchMove(LTouch e) {

    }

    public void touchUp(LTouch e) {

    }

    public void dispose() {
        if (list != null) {
            list.dispose();
            list = null;
        }
    }

    public static void main(String[] args) {
        GameScene game = new GameScene("LTextureListTest", 480, 320);
        game.setShowFPS(true);
        game.setShowLogo(false);
        game.setScreen(new LTextureListTest());
        game.showScreen();
    }
}

不過,這樣就失去預設定與惰性加載的能力了。


說完了LTextureList,但這種程度的篇幅作爲博文來說依舊太短了,所以小弟下面再來談談0.3.2中新增的幾個輔助類。

關於Session類


LGame中的Session類,是爲了在多Screen中傳值與保存多值而設計的,RecordStore類(高仿JavaME)的再簡化版本,它每次set的可以是唯一的鍵值對,也可以在同一鍵下掛多值,在執行dispose函數前,session每次save的數值都將永久保存(JavaSE版保存爲本地文件,Android版保存到手機數據庫)。

下面是一個非常簡單的用例。

package org.loon.test;

import java.util.Calendar;

import org.loon.framework.javase.game.GameScene;
import org.loon.framework.javase.game.core.LSystem;
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.input.LTouch;
import org.loon.framework.javase.game.core.input.LTransition;
import org.loon.framework.javase.game.core.store.Session;
import org.loon.framework.javase.game.core.timer.LTimerContext;

public class SessionTest extends Screen {

    Session session = new Session(SessionTest.class.getName());

    public LTransition onTransition() {
        return LTransition.newEmpty();
    }

    public void onLoad() {

    }

    public void alter(LTimerContext timer) {

    }

    public void draw(GLEx g) {
        g.drawString("save 0 :" + session.get("save", 0), 32, 64);
        g.drawString("save 1 :" + session.get("save", 1), 32, 96);
    }

    public void touchDown(LTouch e) {
    }

    public void touchDrag(LTouch e) {

    }

    public void touchMove(LTouch e) {

    }

    public void touchUp(LTouch e) {

    }

    public void dispose() {
        if (session != null) {
            session.set("save", 0, session.getInt("save", 0) + 1);
            session.set("save", 1, System.currentTimeMillis());
            session.save();
        }
        /**
         * delete session
         *
         * if (session != null)
         * {
         *  session.dispose();
         *  session = null;
         * }
         */
    }

    public static void main(String[] args) {
        GameScene game = new GameScene("SessionTest", 480, 320);
        game.setShowFPS(true);
        game.setShowLogo(false);
        game.setScreen(new SessionTest());
        game.showScreen();
    }
}


關於HttpClient類

HttpClient類是爲了保證LGame應用在跨平臺時接口一致而構建的,處理簡單網頁資源瀏覽的工具類,基本用法如下所示。
    HttpClient webClient = new HttpClient("www.baidu.com");
    webClient.start();
    System.out.println(webClient.doHTML("gb2312"));
    webClient.stop();

除了常規的網頁加載外,HttpClient還可以作爲下載器使用,基本用例如下所示:
package org.loon.test;

import org.loon.framework.javase.game.GameScene;
import org.loon.framework.javase.game.action.sprite.Label;
import org.loon.framework.javase.game.core.graphics.Screen;
import org.loon.framework.javase.game.core.graphics.component.LButton;
import org.loon.framework.javase.game.core.graphics.component.LProgress;
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.input.LTouch;
import org.loon.framework.javase.game.core.input.LTransition;
import org.loon.framework.javase.game.core.timer.LTimerContext;
import org.loon.framework.javase.game.net.HttpClient;
import org.loon.framework.javase.game.net.HttpDownload;
import org.loon.framework.javase.game.net.HttpDownload.HttpDownloadListener;

public class DownloadTest extends Screen {

    public LTransition onTransition() {
        return LTransition.newEmpty();
    }

    public void onLoad() {

        //背景紅色
        setBackground(GLColor.red);

        //構建一個字符標籤
        final Label label = new Label("State : None", 200, 128);
        label.setColor(GLColor.white);
        add(label);

        //構建HttpClient
        final HttpClient webClient = new HttpClient(
                "http://libgdx.googlecode.com/files/libgdx-0.9.1.zip");

        //構建一個進度條
        final LProgress progress = new LProgress(32, 52, 420, 50);
        progress.setProgressTitle(true);
        add(progress);

        //構建一個下載按鈕
        LButton button = new LButton("download", 32, 64, 100, 100) {

            HttpDownload download;

            //設定一個下載標記,避免重複點擊
            boolean downloadFlag = false;

            public void downClick() {

                if (!downloadFlag) {

                    downloadFlag = true;

                    //進度監聽
                    final HttpDownloadListener listener = new HttpDownloadListener() {

                        public void cancel() {
                            label.setLabel("State : Cancel");
                        }

                        public void completed() {
                            label.setLabel("State : Completed");
                            downloadFlag = false;
                            if (download != null) {
                                download.save("c:\\test.zip");
                                webClient.stop();
                            }
                        }

                        public void downloading(float c) {
                            label.setLabel("State : Downloading");
                            progress.percent((int) c);
                        }

                        public void error(Exception ex) {
                            label.setLabel("State : Error");
                        }

                        public void paused() {
                            label.setLabel("State : Paused");
                        }

                    };

                    Runnable runnable = new Runnable() {
                        public void run() {
                            webClient.start();
                            download = webClient.getHttpDownload();
                            download.setListener(listener);
                            download.start();
                        }

                    };
                    callEvent(runnable);
                }
            }

        };

        add(button);
    }

    public void alter(LTimerContext timer) {

    }

    public void draw(GLEx g) {

    }

    public void touchDown(LTouch e) {
    }

    public void touchDrag(LTouch e) {

    }

    public void touchMove(LTouch e) {

    }

    public void touchUp(LTouch e) {

    }

    public void dispose() {

    }

    public static void main(String[] args) {
        GameScene game = new GameScene("DownloadTest", 480, 320);
        game.setShowFPS(true);
        game.setShowLogo(false);
        game.setScreen(new DownloadTest());
        game.showScreen();
    }
}

效果如下所示:


__________________________

另,在LGame的SVN中,有一個小弟開發中的0.3.3測試版(內含jar及源碼,loon-simple.googlecode.com/svn/trunk/),此版本與小弟目前手中的最新版本僅有個別細節處的差異(差的僅爲效率問題修正),因爲小弟正在集中精力搞C#版的完善與C/C++版的開發,0.3.3正式版暫時不會面世(我會將C/C++、C#、Java版都統一到0.3.3結構下,所以會多用點時間),但小弟會將一些最近的修改發往此處存底,如果大家在使用0.3.2時遇到一些問題,也可以換用0.3.3測試版實驗運行,也許相關問題在此版就已經解決了(此LGame-0.3.3-test文件大約每週更新一次,上次更新是三天前)。

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