[GEiv]第五章:个体集群 虚假的分配与释放

来源:互联网 发布:c语言流程图生成器 编辑:程序博客网 时间:2024/04/25 21:29

第五章:个体集群

虚假的分配与释放

        这篇文章主要对“个体集群”思想和对应于引擎的工具类进行介绍。

[个体集群思想]

        个体集群:将大量的、具有相似性质的事物进行某种行为上的抽象和统一管理,例如射击游戏中的弹幕,或是一个粒子特效中使用的上千粒子。

[个体集群的必要性]

        个体集群的概念,产生自Java堆分配的特点,了解Java的您一定明白,相比于可用free方法精确释放堆区内存的C语言不同,Java剥夺了程序员精确控制堆区释放的权利,由此使程序员从控制堆区的烦琐事务中解放出来,同时也就意味着,在要求性能的场合下,糟糕的设计结构会让程序寸步难行。

        以粒子特效为例,譬如这里有一个由1000个粒子共同完成的特效,当收到开始特效的信息后,从堆区分配1000个粒子在时间上是来不及的,同理,在粒子特效完成后,将1000个粒子丢给GC也是不负责任的。

        您可能会这样解决这个问题,将粒子抽象为一个类,在一个容器中,保存1000个这样的类实例,在特效开始后,依次遍历它们,并调用开始的方法。这的确是一个解决思路,而且可以解决的相当不错,但是,如果粒子的种类非常多,怎么办?抽象父类进行继承吗?要是这个游戏是一个射击游戏,包含有弹幕的元素,那么是要再抽象一个弹幕类呢,还是勉强地继承粒子类呢……

        总之,以实现“提前分配”为目的,进行类的抽象是相当不明智的,我们需要的抽象,是对所有具备“分配”、“调用”、“释放”这一系列行为的对象进行概况,而且,介于我们使用的JavaSE平台,集群管理中对于“分配”与“释放”的支持显然是在逻辑上模拟的,并非真实地分配释放内存,这也是映射了本文的副标题——“虚假的分配与释放”。

[个体]

        [interface engineextend.crowdcontroller.Individual]

        接口Individual是对个体集群中的个体提供的抽象,我们来看一下这个接口的结构:

public interface Individual{     publicstatic final int SRC_OUTER = 0;     publicstatic final int SRC_INNER = 1;     publicstatic final int SRC_HIDE_ONLY = 2;         publicstatic final int SRC_EXTRA_0 = 3;     publicstatic final int SRC_EXTRA_1 = 4;     publicstatic final int SRC_EXTRA_2 = 5;         publicboolean isAvalible();     publicvoid getUse(Object[] ARGS,float... FARGS);     publicvoid doStp(int clock);     publicvoid finish(int src);     publicvoid destroy();}


        首先介绍其中的方法:

        public boolean isAvalible();它告诉集群管理器(见下),我是否处在一个可被分配的状态。

        public void getUse(Object[] ARGS,float... FARGS);getUse方法对一个可分配状态的个体进行初始化,并将这个个体分配出去。(也就是调整状态到不可再分配)

        public void doStp(int clock);在绘制每一个帧的逻辑中,集群管理器会对其管理的个体进行遍历,对于已分配的个体调用其doStp方法,可以理解为是一个会被自动调用的Serial方法。

        public void finish(int src);finish方法将一个处于已分配状态的个体再度设置到可分配状态。src可以区分该消息的来源,在一些场合下,根据src的不同进行不同的执行逻辑。

        public void destroy();destroy方法用于永久销毁个体实例,调用此方法后,该类的实例允许被GC回收。

[集群管理器]

[class engineextend.crowdcontroller. CrowdControllerimplements SerialTask]

        CrowdController类是与Individual接口共同完成Geiv下的集群管理功能,有必要对CrowdController进行一些讲解:

框架图:

        

        构造器:UESI是引擎的句柄,后面的布尔值表示是否使用系统默认的帧逻辑。(有时候,我们使用Serial构造Serial级联,这样可以有选择地暂停或终止一部分Serial,这在实现游戏暂停时很有用。)

        countAvailible:返回当前管理的集群中可分配的个数。

        countUnAvailible:返回当前管理的集群中已分配个数。

        getUnAvailible:获得一个已分配的个体。

        getAvailible:获得一个可分配的个体。

        addIndividual:向集群中增加一个个体。

        destroyAllInd:销毁集群中所有个体。

        finishAllInd (int src):以给定的来源,将集群中的所有个体释放(finish)

        finishAllInd:以Individual.SRC_OUTER为来源,将集群中的所有个体释放。

        Serial:该类实现了Serial接口,在每一次绘制结束后,会对群体中所有出于已分配状态的个体,执行其doStp方法。

[例子:发射子弹]您也可以去GitHub页面中的Sample\Sample-CrowdController文件夹内找到这个例子。

        编写程序,使用方向键控制小方块,使用A键发射小矩形子弹。

//Main.java:这是我们的程序入口public class Main{       public static void main(String[] args) {              UESI UES = new R();              new ShootRect(UES);       }}


//ShootRect.java:这个类是我们控制的方块,他可以根据键盘移动和发射子弹。见注释详解。public class ShootRect implements SerialTask{//它实现了Serial,以主动扫描的输入方式响应键盘。       UESI UES;       Obj rect;//这个图元表示被控制的方形。       CrowdController cc;//集群管理器用来管理它的子弹群体。       public ShootRect(UESI UES){              this.UES= UES;              rect = UES.creatObj(UESI.BGIndex);              rect.addGLRect("FFFFFF",0,0,40,40);              rect.setPosition(CANExPos.POS_CENTER);              cc= new CrowdController(UES,true);//构造集群管理器                           for(inti = 0;i < 32;i++){                     cc.addIndividual(newBullet(UES));//将32枚子弹装入集群管理器。装入的数量取决于子弹在屏幕上出现的最大数量。              }              UES.addSerialTask(this);//将Serial实现加入绘制任务队列的末尾。                           rect.show();//显示rect       }       @Override       public void Serial(int clock) {              if(UES.getKeyStatus(KeyEvent.VK_LEFT)){                     rect.setDx(rect.getDx()- 3.0f);//左键按下时,想左移位3.0个像素。下同              }              if(UES.getKeyStatus(KeyEvent.VK_RIGHT)){                     rect.setDx(rect.getDx()+ 3.0f);              }              if(UES.getKeyStatus(KeyEvent.VK_UP)){                     rect.setDy(rect.getDy()- 3.0f);              }              if(UES.getKeyStatus(KeyEvent.VK_DOWN)){                     rect.setDy(rect.getDy()+ 3.0f);              }              if(UES.getKeyStatus(KeyEvent.VK_A)){//这里响应发射键                     if(clock%10== 0){//使用时序,即一秒发射6发                         cc.getAvailible().getUse(null,rect.getCentralX(),rect.getCentralY());//将一个待分配的子弹分配除去,使用参数为发射体方块的中心点。(这里请参考下面Bullet的实现)                     }              }       }}


//Bullet.java:子弹类,它实现Individual接口public class Bullet implements Individual {       Obj bullet;//这里是子弹的图元       public Bullet(UESI UES) {              bullet= UES.creatObj(UESI.BGIndex);              bullet.addGLRect("FFFFFF",0, 0, 5, 12);              bullet.setGLFill(true);       }        @Override       public boolean isAvalible() {              return!bullet.isPrintable();//如果子弹已经投射在屏幕上,意味着其已经分配       }        @Override       public void getUse(Object[] ARGS, float... FARGS) {              bullet.setCentralX(FARGS[0]);//设置子弹的中心位为float参数的第一个。              bullet.setCentralY(FARGS[1]);              bullet.show();//这里,子弹显示在屏幕上,也就意味着,isPrintable方法将返回true,上面的isAvalible返回false。       }        @Override       public void doStp(int clock) {              if(bullet.getDy() > 0) {//如果纵坐标大于0                     bullet.setDy(bullet.getDy()- 5.5f);//则向上移动              }else {                     finish(0);//否则,结束分配状态。              }       }        @Override       public void finish(int src) {              bullet.hide();//将子弹的图元隐藏,因此isAvalible将返回true。       }        @Override       public void destroy() {              bullet.destroy();//销毁图元       } }


演示效果:


[总结]

        本文介绍了集群管理思想和其在该引擎下的对应工具类,我们使用Individual和Crowdcontroller两个类,将那些频繁分配、释放的对象进行抽象,使我们在需要“分配”这样的对象时,无需实际地分配堆区内存;正是因为这种设计结构,原本需要在Serial逻辑中new的对象现在仅仅是需要一个信号就可以使用了,也使原本17ms的绘制间隔变得绰绰有余。

        在本章结束后,后面的章节不再具有逻辑上的前后关联性,可以依据个人喜好参阅。

0 0
原创粉丝点击