gamemaker吧 关注:13,692贴子:95,916
  • 9回复贴,共1

【即看即用】在gms2里的对象池实现脚本

只看楼主收藏回复

对象池定义:
对象池模式, 或者称为对象池服务, 其意图为: 通过循环使用对象, 减少资源在初始化和释放时的昂贵损耗(这里的"昂贵"可能是时间效益(如性能), 也可能是空间效益(如并行处理), 在大多情况下, 指性能)
简单的说, 在需要时,从池中提取,不用时,放回池中,等待下一个请求

(这个图只是用来占位置的)
最近研究到对象池,就在gms2里也写了一个。实际写出来代码也不多,就是不能用创建事件和销毁事件比较难受,得自己单独写出来。。实际上也就是把这两个事件的东西抄一份出来的麻烦程度


IP属地:上海1楼2020-05-05 16:23回复
    使用这个对象池的第一步,在整局游戏开始的时候创建一个字典数据结构,用来存储和查询各个对象的对象池索引
    //对象池
    global.ObjectPool_map=ds_map_create()


    IP属地:上海2楼2020-05-05 16:25
    回复
      2026-03-15 01:26:44
      广告
      不感兴趣
      开通SVIP免广告
      第二步,创建ObjectPool_checkOut脚本,用来代替instance_create_depth函数分的功能,具体实现可以直接看我下面一堆的注释
      //instance_create_depth一样的参数输入
      var tx=argument0,ty=argument1,tdepth=argument2,tobj=argument3,tid
      //以Object_index为索引在字典里找对应的对象池
      var ObjectPool = ds_map_find_value(global.ObjectPool_map, tobj)
      //如果没找到,就新建一个对象池堆栈
      if is_undefined(ObjectPool){
      ObjectPool=ds_stack_create()
      global.ObjectPool_map[? tobj]=ObjectPool
      }
      //如果该对象池里没货了,就新建一个实例
      if (ds_stack_empty(ObjectPool)){
      tid=instance_create_depth(tx,ty,tdepth,tobj)
      }
      else{//否则从对象池堆栈里弹出一个空闲实例
      tid=ds_stack_pop(ObjectPool)
      if !instance_exists(tid) //如果这个实例没了(比如转移场景会清除所有冻结实例),就新建一个
      tid=instance_create_depth(tx,ty,tdepth,tobj)
      else{//否则解冻它并手动初始化值
      instance_activate_object(tid)
      tid.x=tx//初始化赋值
      tid.y=ty
      tid.depth=tdepth
      }
      }
      //给实例一个初始化信号
      tid.key_start=1;
      //返回可用实例id
      return tid


      IP属地:上海3楼2020-05-05 16:29
      回复
        第三步,创建ObjectPool_checkIn,用来代替instance_destroy函数的功能
        //输入参数为用完了的实例id
        var tid=argument0
        //找到对象池
        var ObjectPool=global.ObjectPool_map[? tid.object_index]
        //冻结实例之后把它放到对象池里
        instance_deactivate_object(tid)
        ds_stack_push(ObjectPool,tid)


        IP属地:上海4楼2020-05-05 16:30
        回复
          第四步,在你的实例(比如子弹)里改动一下
          我在实例里放了一个key_start变量来写初始化和销毁的功能,其中key_start=1为初始化状态,key_start=2为销毁状态,key_start=0为正常状态
          ——————————————实例的步事件——————————————
          //对象池实例初始化,把创建事件里的代码抄过来,以及其他image_alpha等变量也需要注意一下
          if (key_start==1)
          {
          **你的初始化代码**
          key_start=0
          }
          **你的正常步事件代码,不过需要把使用instance_destroy的地方都改成key_start=2**
          //对象池对象销毁,在前面将key_start置为2就会触发,可以把销毁事件里的代码抄过来
          if key_start==2{
          **你的销毁代码**
          ObjectPool_checkIn(id)
          }


          IP属地:上海5楼2020-05-05 16:38
          回复
            第五步,你可以一下子创建五百个子弹而不会卡顿了


            IP属地:上海6楼2020-05-05 16:39
            回复
              说得好
              几点补充建议:
              1. 对象池仅适用于需要在短时间内快速创建销毁的物体,例如子弹;对于不经常创建销毁的物体,例如怪物,使用对象池需要斟酌,否则没有显著时间性能优化反而会浪费空间性能。
              2. 我记得event_perform函数可以直接调用指定事件,包括Create和Destroy,不知道gms2里还有没有;另外,即使没有,也建议用用户事件来代替创建和销毁事件,不建议在步事件如此判断,降低代码可读性。


              IP属地:上海来自Android客户端7楼2020-05-05 18:14
              收起回复
                感谢@sunyubokkkkk 的建议,这里更新一下调用了创建事件和销毁事件的新代码,现在在步事件中不需要用那个key_start自己写创建和销毁的代码了
                另外为ObjectPool_checkIn脚本添加了对零参输入的兼容,不输入参数会跟instance_destroy一样直接销毁实例自己
                ————————————ObjectPool_checkOut————————————
                //instance_create_depth一样的参数输入
                var tx=argument0,ty=argument1,tdepth=argument2,tobj=argument3,tid
                //以Object_index为索引在字典里找对应的对象池
                var ObjectPool = ds_map_find_value(global.ObjectPool_map, tobj)
                //如果没找到,就新建一个对象池堆栈
                if is_undefined(ObjectPool){
                ObjectPool=ds_stack_create()
                global.ObjectPool_map[? tobj]=ObjectPool
                }
                //如果该对象池里没货了,就新建一个实例
                if (ds_stack_empty(ObjectPool)){
                tid=instance_create_depth(tx,ty,tdepth,tobj)
                }
                else{//否则从对象池堆栈里弹出一个空闲实例
                tid=ds_stack_pop(ObjectPool)
                if !instance_exists(tid) //如果这个实例没了(比如转移场景会清除所有冻结实例),就新建一个
                tid=instance_create_depth(tx,ty,tdepth,tobj)
                else{//否则解冻它并手动初始化值
                instance_activate_object(tid)
                tid.x=tx//初始化赋值
                tid.y=ty
                tid.depth=tdepth
                }
                }
                //调用实例的创建事件
                with(tid)event_perform(ev_create,0)
                //返回可用实例id
                return tid
                ————————————ObjectPool_checkIn————————————
                //输入参数为用完了的实例id
                var tid=id
                if argument_count==1 tid=argument[0]
                //调用实例的销毁事件
                with(tid)event_perform(ev_destroy,0)
                //找到对象池
                var ObjectPool=global.ObjectPool_map[? tid.object_index]
                //冻结实例之后把它放到对象池里
                instance_deactivate_object(tid)
                ds_stack_push(ObjectPool,tid)


                IP属地:上海8楼2020-05-07 16:04
                回复