Lua 协程在游戏开发中的使用

最近发现协程在游戏开发中是个可以让程序很简捷的写法.对于以前大量的使用回调,要程序难以跟踪的问题,终于可以迎刃而解.下面简述几种,就算当抛砖引玉(大神可别吐槽呀:unamused:).


简单封装的协程类
local Coroutine = class(“Coroutine”)

local mCoroutineId = 0
local g_CoroutineList = {}

local function addCoroutine(co)
mCoroutineId = mCoroutineId + 1
co.mCoroutineId = mCoroutineId
g_CoroutineList[mCoroutineId] = co
end

local function removeCoroutine(co)
g_CoroutineList[co.mCoroutineId] = nil
end

function killAllCoroutine()
for _, co in pairs(g_CoroutineList) do
co:cleanup()
end
mCoroutineId = 0
g_CoroutineList = {}
end

function createCoroutine(func, finishFunc, finishParam)
local ret = Coroutine.new()
ret:init(func)
ret.mFinishFunc = finishFunc
ret.mFinishParam = finishParam
return ret
end

– 开启协程
function StartCoroutine(target, name, args)
local func = target[name]
if func == nil then
assert("start coroutine can’t find function name by "…name)
return false
end
local co = nil
local function excuteFunc()
if not device:isWindows() then
func(target, co, args)
else
xpcall(function() func(target, co, args) end, G__TRACKBACK)
end
end
co = createCoroutine(excuteFunc)
co.mTarget = target
co:run(args)
end

– 构造函数
function Coroutine:ctor()
addCoroutine(self)
self.mUpdateFunc = nil
self.mWaitForFrames = 0
self.mWaitForSeconds = 0
self.mId = 0
self.coroutine = nil
self.mFinishFunc = nil
self.mFinishParam = nil
self.mSchedulerId = nil

self.mbDumpProc = false
self.mTimer = 0

end

function Coroutine:init(func)
self.mSchedulerId = cc.Director:getInstance():getScheduler():scheduleScriptFunc(function(dt) self:update(dt) end, 0, false)

local co = coroutine.create(func)
self.coroutine = co
return true

end

function Coroutine:run()
self:resume(“start run”)
end

– 清除协程
function Coroutine:cleanup()
if self.mFinishFunc then
self.mFinishFunc(self.mFinishParam)
end
if self.mSchedulerId then
cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.mSchedulerId)
self.mSchedulerId = nil
end

end

function Coroutine:update(dt)
self.mTimer = self.mTimer + dt
if self.mTarget and type(self.mTarget) == “userdata” and tolua.isnull(self.mTarget) then
self:cleanup()
return
end
if self.timeout then
self.timeout = self.timeout - dt
if self.timeout < 0 then
self.timeout = nil
self:resume(“timeout”)
return
end
end
if self.mUpdateFunc then self.mUpdateFunc(dt) end
end

– 暂停
function Coroutine:pause(name, timeout)
if self.mbDumpProc then print(“coroutine pause with name”, name) end
self.timeout = timeout
coroutine.yield()
end

– 设置更新函数
function Coroutine:setUpdateFunc(handler)
self.mUpdateFunc = handler
end

– 恢复
function Coroutine:resume(name)
self.mUpdateFunc = nil
if self.mbDumpProc then print(“resume”, name) end
local success, yieldType, yieldParam = coroutine.resume(self.coroutine, self)
if not success then
print(“resume failed”, success, yieldType, yieldParam)
self:cleanup()
return
end

local status = coroutine.status(self.coroutine)
--print("resume", name, status)
-- 判断协程是否结束
if status == "dead" then
	self:cleanup()
end

end

return Coroutine

1.在新手引导制作中的使用
StartCoroutine(self, “playLogic”)

function Guider13:playLogic(co)
WaitForButtonEvent(co, “UIBattleEnterNode”, “btn_copy”, 3)
WaitForButtonEvent(co, “CopyMain”, “Button_elite”, 1)
WaitForNPC(co, “通关百将图可以获得英雄碎片”,cc.p(215, 200))
end

其中WaitForButtonEvent方法,是封装好了的,引导玩家点击按钮的接口

5赞