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

第五章:个体集群

虚假的分配与释放

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

[个体集群思想]

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

[个体集群的必要性]

        个体集群的概念,产生自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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值