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个函数和变量组成。先前我也说过理论与实现的关联和差距,但能够确定地一点是由理论带动实践开发才是快速提升自身水平之路。理论不正确,方向就会错误,弯路就成不可避免的了。