程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> J2ME >> j2me游戲引擎的基本構成(二)——場景管理器

j2me游戲引擎的基本構成(二)——場景管理器

編輯:J2ME
前言         上一章我們用通俗易懂的電影拍攝過程大致的了解了一下游戲引擎,希望大家可以了解一個引擎都有哪些部分。恰恰就在帖子貼出的第二天一個朋友問我:“游戲框架和游戲引擎有什麼區別?”。當時我就蒙了一下,轉眼我又想到了汽車與引擎的關系,於是回答“框架就相當於汽車,引擎就相當於汽車的引擎”。不知道大家看明白沒有,框架決定了引擎的作用,比如一個J2ME的游戲框架是不可能運行一個PC的游戲引擎,就像小汽車的引擎和客車的引擎不可能通用一樣。還有朋友問我,你講的裡面那些東西每一個都是單獨的類嗎?請大家注意,抽象構成和現實組織有著不可分割的聯系,但同樣有著巨大的差距,就像鑽天猴與“神州5號”飛船的差別一樣。原理是一樣的,可是他們差別同樣巨大。一定要活學活用啊。   第一節  場景管理器(world)         在PC中我們有著海量可以使用的資源,包括各種各樣的資源 ,要說有限制也是機器本身的限制。甚至裡面一個小小的文件都有可能比我們的J2ME游戲本身都大。於是乎在做PC游戲的時候更多考慮的是游戲的效果如何,運行速度如何,而我們也可以天馬行空的使用著各種我們可以使用的特效。於是渲染器成了引擎中的重頭戲,呵呵,誰不喜歡看自己的游戲能玩出好多特別Cool的效果來,反正我想。唉,雖然為PC的游戲興奮了半天可我們畢竟不是做PC游戲的,我們做的是比PC游戲更牛X的J2ME游戲。為什麼說更牛X呢,敢問那個PC游戲可以在僅僅使用100k heap的情況下可以把《古墓麗影》《細胞分裂》這樣的游戲做的爐火純青,玩家愛不釋手?100k heap,這是什麼概念?都學過計算機理論,知道那是多大。
在這種情況下我們引出第一次從天堂的墮落:場景管理器。我們仍然拿拍電影來做分析。第一章那個小片段中的場景是一個狹窄的街道,中間要放好多道具,“導演”不停的要求“劇務”放這放那,那麼如果“劇務”足夠聰明的話他肯定要采取一種比較省時省力的辦法,而不是一趟一趟的往庫房跑。但是本身劇務可以放置道具的地方並不多,於是一次取多少道具以及道具的使用率就決定了“劇務”的勞動量。同理,我們可以把“劇務”可以控制的空間比作heap,“劇務”去庫房拿“道具”比作I/O操作。既要最大程度滿足“導演”的需求,又要保證“時間”不過多地浪費在去“庫房”的路上又不能超出當前所能控制的空間....其實這個真的很難!就像自己的女人要求天天跟她在一起,而自己又因為要掙錢奔波一樣^_^。問題出來了,至於怎麼樣最省力我們仍然需要不斷的探索。一個好辦法就是“劇務”首先掌握“庫房”中“道具”存放位置,等需要的時候一步到位拿出來即可,省去找尋的時間。至於怎麼滿足“導演”的需要那是另一碼事,咱們解決這個問題。我相信大家在學習過程中看代碼是最直接,最有效的方式,為了證明我的想法我找了一個S40版的《細胞分裂》來學習研究一下。(如果大家已經看過我的msn 空間了,下面可以略過)第一步從我們的分析中就可以看出應該是“掌握道具在庫房中的位置”,這個嘛和我們打的文件包是有一定關系的,需要在文件中加入一些附屬的信息,下面是gameloft細胞分裂部分文件操作代碼,代碼中有詳細說明。在我的感覺中gameloft一定采用了什麼方法。我曾經大概猜測了一下,無非就是將文件的一些信息扔到一個包裡然後在包裡記錄一些文件的數據信息,比如便移量什麼的。我的包就是那麼作的,但是缺點很明顯,必須要一次載入所有的資源,也不是必須,如果不那樣就每次根據需要
來搜索資源位置就很費CPU資源了。
首先來看gameloft的第一段代碼:(反編譯後的)
    private static final boolean B(String s1)
    {
        getResourceAsStream = s1;
        try
        {
            InputStream inputstream = I.getClass().getResourceAsStream(s1);
            System.gc();
            //這裡取了兩個byte的數據,並組合成了一個整數,仔細想想的話不難發現這是這個包中資源的個數
            getState = inputstream.read() & 0xff;
            getState += (inputstream.read() & 0xff) << 8;
           //這段話又是什麼意思呢?又+又*的,暈死了。到最後我再告訴大家這是做什麼用的,主要得根據數據結構來說
            hasNextElement = 2 + 4 * getState;
            
            //不用說了,這是分配空間
            getWidth = new int[getState];
            indexOf = new int[getState];
            //難點終於來了
            for(int i1 = 0; i1 < getState; i1++)
            {
                //下面這段話最有意思,明明知道是在拼一個整數,可是要整數干什麼用呢?先留給大家想想
                getWidth[i1] = inputstream.read() & 0xff;
                getWidth[i1] += (inputstream.read() & 0xff) << 8;
                getWidth[i1] += (inputstream.read() & 0xff) << 16;
                getWidth[i1] += (inputstream.read() & 0xff) << 24;
                //這裡嘛好理解,就是indexOf中存的是兩個整數差,*.....難道....接著看
                if(i1 != 0)
                    indexOf[i1 - 1] = getWidth[i1] - getWidth[i1 - 1];
            }
            inputstream.close();
            inputstream = null;
            System.gc();
        }
        catch(Exception exception)
        {
            boolean flag = false;
            return flag;
        }
        return true;
    }
        這段代碼中並沒有反應出系統載入了一個包,而是一些亂七八糟的東西,這段代碼也是一直困擾我的地方,明明是載入東西,可是卻載入了一股子整數。沒辦法,接著看吧,也許整合起來就知道做什麼用的了。

我最敏感的就是byte[]這個詞,於是我發現了這段代碼
    private static final byte[] addRecord(int i1)
    {
        //哦,有戲,一看就知道在作邊界檢測
        if(i1 < 0 || i1 >= getState)
            return null;
        //obj是做什麼的呢?暫時不知道
        Object obj = null;
        
        //聲明byte數組,嘿嘿,找到啦
        byte abyte0[] = null;
        try
        {   //載入資源 getResourceAsStream=剛才的s1
            InputStream inputstream = I.getClass().getResourceAsStream(getResourceAsStream);
            //噢,找到啦,剛才那個什麼getWidth的數組不是存了整數嗎?難道整個文件後面還有東西?
            //不用說了,那個數組記錄的是文件的偏移量而index記錄了大小,這樣一切就都明白了,
            inputstream.skip(getWidth[i1] + hasNextElement);
            abyte0 = new byte[indexOf[i1]];
            inputstream.read(abyte0);
            inputstream.close();
            inputstream = null;
        }
        catch(Exception exception) { }
        System.gc();
        return abyte0;
    }
        從上面的代碼我們不難總結出,首先將所有的“道具”的大小和位置保存下來有多麼大的好處。但是他也有它的限制,如果大家就這樣然後一味的生搬硬套到自己的項目中那你就慘了,如果“道具”比較大比如地圖數據之類的還好說,很長時間調用一回I/O,如果是一些小文件光I/O操作就能搞死手機。所以我們因該想出更好的解決方案,就是讓那個addRecord可以接受一個數組,在不超出heap size的情況下調入多個文件。代碼我就不給出了,有了上面的代碼大家很容易就可以做出來。或許比GameLoft更好也說不定哦
       呵呵,從理論到實踐,我們走出第一步。我並不主張大家就照本宣科的把人家的東西拿來就用,也許有人說,暈,這麼簡單,對!在你做出來之前都是困難的!只要用心,只有想不到沒有做不到!
上面的問題我們已經解決掉了,但是如何調度最快最省時間?這是個人者見人,妖者見妖的問題,一般要根據項目的需求來做,如果是一個RPG游戲,劇情節奏緩慢,就沒有必要把好多資源一起載入,僅把需要的載入即可。而一個劇情發展迅速,對速度要求較高的空戰游戲我們最好還是盡最大程度將“道具”資源載入較好。         從我的敘述中似乎對場景管理器的“管理”方面敘述不夠多,我也注意到了嘿嘿。其實場景管理器除了從庫房調入“道具”並加入自己的“道具”列表,更重要的一個工作就是在合適的時候將道具放回庫房,釋放自己能控制的“空間”並從“道具”列表中刪除。那麼我們怎麼樣才可以實現呢?這個問題也比較撓頭,筆者曾經做過一些探索,但效果不甚理想。我的方案就是對當前“劇務”所能控制的每個道具進行使用頻率計算,使用過就+1,在一定頻率內沒有使用就-1,直到變成0就釋放掉,效果不是很理想。於是回歸原始,每段游戲完畢後就將資源手動回收一次,呵呵。這樣雖然有悖於引擎的宗旨,但的確是效率最好的。         汗!雖然本節講的是“場景管理器”,可更多的敘述了場景資源的調度,其實這麼講是有原因的。在J2ME中所謂“管理”最有可能就是用數組來實現的,所以管理方面顯得就比較薄弱,而資源的調度則可能是N個函數和變量組成。先前我也說過理論與實現的關聯和差距,但可以肯定地一點是由理論帶動實踐開發才是快速提高自身水平之路。理論不正確,方向就會錯誤,彎路就成不可避免的了。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved