【即看即用】在gms2里的对象池实现脚本
gamemaker吧
全部回复
仅看楼主
level 7
a10a010 楼主
对象池定义:
对象池模式, 或者称为对象池服务, 其意图为: 通过循环使用对象, 减少资源在初始化和释放时的昂贵损耗(这里的"昂贵"可能是时间效益(如性能), 也可能是空间效益(如并行处理), 在大多情况下, 指性能)
简单的说, 在需要时,从池中提取,不用时,放回池中,等待下一个请求
(这个图只是用来占位置的)
最近研究到对象池,就在gms2里也写了一个。实际写出来代码也不多,就是不能用创建事件和销毁事件比较难受,得自己单独写出来。。实际上也就是把这两个事件的东西抄一份出来的麻烦程度
2020年05月05日 08点05分 1
level 7
a10a010 楼主
使用这个对象池的第一步,在整局游戏开始的时候创建一个字典数据结构,用来存储和查询各个对象的对象池索引
//对象池
global.ObjectPool_map=ds_map_create()
2020年05月05日 08点05分 2
level 7
a10a010 楼主
第二步,创建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
2020年05月05日 08点05分 3
level 7
a10a010 楼主
第三步,创建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)
2020年05月05日 08点05分 4
level 7
a10a010 楼主
第四步,在你的实例(比如子弹)里改动一下
我在实例里放了一个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)
}
2020年05月05日 08点05分 5
level 7
a10a010 楼主
第五步,你可以一下子创建五百个子弹而不会卡顿了
2020年05月05日 08点05分 6
level 15
说得好[真棒]
几点补充建议:
1. 对象池仅适用于需要在短时间内快速创建销毁的物体,例如子弹;对于不经常创建销毁的物体,例如怪物,使用对象池需要斟酌,否则没有显著时间性能优化反而会浪费空间性能。
2. 我记得event_perform函数可以直接调用指定事件,包括Create和Destroy,不知道gms2里还有没有;另外,即使没有,也建议用用户事件来代替创建和销毁事件,不建议在步事件如此判断,降低代码可读性。
2020年05月05日 10点05分 7
没研究过事件,如果是这样那的确是直接调用事件的好
2020年05月05日 10点05分
感谢补充
2020年05月05日 10点05分
level 7
a10a010 楼主
感谢@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)
2020年05月07日 08点05分 8
1