我是Bitfighter的首席开发者,这是一款主要用C++编写的游戏,但是使用Lua来编写机器人玩家的脚本.我们正在使用Lunar(Luna的一个变种)将这些位粘合在一起.
我现在正在努力解决我们的Lua脚本如何知道他们引用的对象已被C++代码删除.
这是一些示例机器人代码(在Lua中):
if needTarget then -- needTarget => global(?) boolean ship = findClosest(findItems(ShipType)) -- ship => global lightUserData obj end if ship ~= nil then bot:setAngleToPoint(ship:getLoc()) bot:fire() end
请注意,仅在needTarget为true时设置ship,否则使用上一次迭代的值.很可能(甚至,如果机器人一直在做它的工作:-),因为变量是最后一次设置,所以该船将被杀死(并且其对象被C++删除).如果是这样,当我们调用ship:getLoc()时,C++会适合,并且通常会崩溃.
所以问题是如果(程序员)犯了错误,如何最优雅地处理这种情况并限制损害.
我有一些想法.首先,我们可以创建某种Lua函数,C++代码可以在船舶或其他项目死亡时调用:
function itemDied(deaditem) if deaditem == ship then ship = nil needTarget = true end end
其次,我们可以实现某种引用计数智能指针"神奇地"修复问题.但我不知道从哪里开始.
第三,我们可以使用某种类型的死机检测器(不确定它是如何工作的)机器人可以这样调用:
if !isAlive(ship) then needTarget = true ship = nil -- superfluous, but here for clarity in this example end if needTarget then -- needTarget => global(?) boolean ship = findClosest(findItems(ShipType)) -- ship => global lightUserData obj end <...as before...>
第四,我只能保留船舶的ID而不是参考,并使用它来获取每个周期的船舶对象,如下所示:
local ship = getShip(shipID) -- shipID => global ID if ship == nil then needTarget = true end if needTarget then -- needTarget => global(?) boolean ship = findClosest(findItems(ShipType)) -- ship => global lightUserData obj shipID = ship:getID() end <...as before...>
我理想的情况也会智能地抛出错误.如果我在死船上运行getLoc()方法,我想触发错误处理代码,让机器人有机会恢复,或者至少让系统杀死机器人并记录问题,希望能引导我要更加小心我的机器人编码方式.
这些是我的想法.我倾向于#1,但它感觉很笨(并且可能涉及很多来回,因为我们有很多短生命周期对象,比如子弹来应对,其中大多数我们都不会跟踪).忘记实现itemDied()函数可能很容易.#2很有吸引力,因为我喜欢魔法,但不知道它是如何工作的.#3和#4非常容易理解,我可以将我的死亡检测限制在几个游戏周期(很可能只是一艘船)的几个有趣的物体上.
这必须是一个常见问题.您如何看待这些想法,那里有更好的想法吗?
谢谢!
这是我目前最好的解决方案:
在C++中,我的ship对象称为Ship,其生命周期由C++控制.对于每个Ship,我创建一个名为LuaShip的代理对象,其中包含指向Ship的指针,Ship包含指向LuaShip的指针.在Ship的析构函数中,我将LuaShip的Ship指针设置为NULL,我将其用作船舶已被销毁的指示器.
我的Lua代码只引用了LuaShip,因此(理论上,至少,因为这部分仍然无法正常工作)Lua将在相应的Ship对象消失后控制LuaShip的生命周期.因此,即使在Ship对象消失之后,Lua也将始终具有有效句柄,并且我可以为检查Ship为NULL的Ship方法编写代理方法.
所以现在我的任务是更好地了解Luna/Lunar如何管理指针的生命周期,并确保当他们的合作伙伴Ships被删除时,如果仍有一些Lua代码指向它们,我的LuaShips不会被删除.那应该是非常可行的.
事实上,事实证明这是不可行的(至少不是我).似乎工作的是将Ship和LuaShip对象分离一点.现在,当Lua脚本请求LuaShip对象时,我创建一个新对象并将其交给Lua,让Lua在完成后删除它.LuaShip使用智能指针来引用Ship,因此当Ship死亡时,该指针被设置为NULL,LuaShip对象可以检测到它.
在使用之前,由Lua编码员检查船舶是否仍然有效.如果他们不这样做,我可以捕获引用并抛出严厉的错误消息,而不是让整个游戏崩溃(就像之前发生的那样).
现在Lua完全控制了LuaShip的生命周期,C++可以删除Ships而不会造成问题,而且一切似乎都能顺利运行.唯一的缺点是我可能会创建很多LuaShip对象,但实际上并没有那么糟糕.
如果您对此主题感兴趣,请参阅我发布的有关相关概念的邮件列表主题,最后是一些改进上述内容的建议:
http://lua-users.org/lists/lua-l/2009-07/msg00076.html
我认为你的Lua方面没有问题,你不应该在那里解决它.
您的C++代码正在删除仍在引用的对象.无论他们如何被引用,那都很糟糕.
简单的解决方案可能是让月球清理所有物体.它已经知道哪些对象必须保持活动,因为脚本正在使用它们,并且让它为随机C++对象执行GC似乎是可行的(假设C++端的智能指针,当然 - 每个智能指针都添加到Lunars引用计数)