3. Unity3d Lua框架 - xLua - TimerManager

-- 定时器管理:负责定时器获取、回收、缓存、调度等管理
-- 注意:
-- 1、任何需要定时更新的函数从这里注册,游戏逻辑层最好使用不带"Co"的接口
-- 2、带有"Co"的接口都是用于协程,它的调度会比普通更新后一步---次序依从Unity函数调用次序:https://docs.unity3d.com/Manual/ExecutionOrder.html
-- 3、UI界面倒计时刷新等不需要每帧去更新的逻辑最好用定时器,少用Updatable,定时器能很好避免频繁的无用调用
-- 4、定时器并非精确定时,误差范围和帧率相关
-- 5、循环定时器不会累积误差,这点和Updater的Update函数自己去控制时间刷新是一致的,很好用
-- 6、定时器是weak表,使用临时对象时不会持有引用
-- 7、慎用临时函数、临时闭包,所有临时对象要外部自行维护引用以保障生命周期,否则会被GC掉===>很重要***
-- 8、考虑到定时器可能会被频繁构造、回收,这里已经做了缓存池优化

支付宝捐赠

打赏红包

  1. TimerManager.lua
--[[
-- 定时器管理:负责定时器获取、回收、缓存、调度等管理
-- 注意:
-- 1、任何需要定时更新的函数从这里注册,游戏逻辑层最好使用不带"Co"的接口
-- 2、带有"Co"的接口都是用于协程,它的调度会比普通更新后一步---次序依从Unity函数调用次序:https://docs.unity3d.com/Manual/ExecutionOrder.html
-- 3、UI界面倒计时刷新等不需要每帧去更新的逻辑最好用定时器,少用Updatable,定时器能很好避免频繁的无用调用
-- 4、定时器并非精确定时,误差范围和帧率相关
-- 5、循环定时器不会累积误差,这点和Updater的Update函数自己去控制时间刷新是一致的,很好用
-- 6、定时器是weak表,使用临时对象时不会持有引用
-- 7、慎用临时函数、临时闭包,所有临时对象要外部自行维护引用以保障生命周期,否则会被GC掉===>很重要***
-- 8、考虑到定时器可能会被频繁构造、回收,这里已经做了缓存池优化
--]]

local Messenger = require "Framework.Common.Messenger"
local TimerManager = BaseClass("TimerManager", Singleton)
local UpdateMsgName = "Update"

-- 构造函数
local function __init(self)
	-- 成员变量
	-- handle
	self.__update_handle = nil
	self.__lateupdate_handle = nil
	self.__fixedupdate_handle = nil
	self.__coupdate_handle = nil
	self.__colateupdate_handle = nil
	self.__cofixedupdate_handle = nil
	-- 定时器列表
	self.__update_timer = {}
	self.__lateupdate_timer = {}
	self.__fixedupdate_timer = {}
	self.__coupdate_timer = {}
	self.__colateupdate_timer = {}
	self.__cofixedupdate_timer = {}
	-- 定时器缓存
	self.__pool = {}
	-- 待添加的定时器列表
	self.__update_toadd = {}
	self.__lateupdate_toadd = {}
	self.__fixedupdate_toadd = {}
	self.__coupdate_toadd = {}
	self.__colateupdate_toadd = {}
	self.__cofixedupdate_toadd = {}
end

-- 延后回收定时器,必须全部更新完毕再回收,否则会有问题
local function DelayRecycle(self, timers)
	for timer,_ in pairs(timers) do
		if timer:IsOver() then
			timer:Stop()
			table.insert(self.__pool, timer)
			timers[timer] = nil
		end
	end
end

-- Update回调
local function UpdateHandle(self)
	-- 更新定时器
	for timer,_ in pairs(self.__update_toadd) do
		self.__update_timer[timer] = true
		self.__update_toadd[timer] = nil
	end
	for timer,_ in pairs(self.__update_timer) do
		timer:Update(false)
	end
	DelayRecycle(self, self.__update_timer)
end

-- LateUpdate回调
local function LateUpdateHandle(self)
	-- 更新定时器
	for timer,_ in pairs(self.__lateupdate_toadd) do
		self.__lateupdate_timer[timer] = true
		self.__lateupdate_toadd[timer] = nil
	end
	for timer,_ in pairs(self.__lateupdate_timer) do
		timer:Update(false)
	end
	DelayRecycle(self, self.__lateupdate_timer)
end

-- FixedUpdate回调
local function FixedUpdateHandle(self)
	-- 更新定时器
	for timer,_ in pairs(self.__fixedupdate_toadd) do
		self.__fixedupdate_timer[timer] = true
		self.__fixedupdate_toadd[timer] = nil
	end
	for timer,_ in pairs(self.__fixedupdate_timer) do
		timer:Update(true)
	end
	DelayRecycle(self, self.__fixedupdate_timer)
end

-- CoUpdate回调
local function CoUpdateHandle(self)
	-- 更新定时器
	for timer,_ in pairs(self.__coupdate_toadd) do
		self.__coupdate_timer[timer] = true
		self.__coupdate_toadd[timer] = nil
	end
	for timer,_ in pairs(self.__coupdate_timer) do
		timer:Update(false)
	end
	DelayRecycle(self, self.__coupdate_timer)
end

-- CoLateUpdate回调
local function CoLateUpdateHandle(self)
	-- 更新定时器
	for timer,_ in pairs(self.__colateupdate_toadd) do
		self.__colateupdate_timer[timer] = true
		self.__colateupdate_toadd[timer] = nil
	end
	for timer,_ in pairs(self.__colateupdate_timer) do
		timer:Update(false)
	end
	DelayRecycle(self, self.__colateupdate_timer)
end

-- CoFixedUpdate回调
local function CoFixedUpdateHandle(self)
	-- 更新定时器
	for timer,_ in pairs(self.__cofixedupdate_toadd) do
		self.__cofixedupdate_timer[timer] = true
		self.__cofixedupdate_toadd[timer] = nil
	end
	for timer,_ in pairs(self.__cofixedupdate_timer) do
		timer:Update(true)
	end
	DelayRecycle(self, self.__cofixedupdate_timer)
end

-- 启动
local function Startup(self)
	self:Dispose()
	self.__update_handle = UpdateBeat:CreateListener(UpdateHandle, TimerManager:GetInstance())
	self.__lateupdate_handle = LateUpdateBeat:CreateListener(LateUpdateHandle, TimerManager:GetInstance())
	self.__fixedupdate_handle = FixedUpdateBeat:CreateListener(FixedUpdateHandle, TimerManager:GetInstance())
	self.__coupdate_handle = CoUpdateBeat:CreateListener(CoUpdateHandle, TimerManager:GetInstance())
	self.__colateupdate_handle = CoLateUpdateBeat:CreateListener(CoLateUpdateHandle, TimerManager:GetInstance())
	self.__cofixedupdate_handle = CoFixedUpdateBeat:CreateListener(CoFixedUpdateHandle, TimerManager:GetInstance())
	UpdateBeat:AddListener(self.__update_handle)
	LateUpdateBeat:AddListener(self.__lateupdate_handle)
	FixedUpdateBeat:AddListener(self.__fixedupdate_handle)
	CoUpdateBeat:AddListener(self.__coupdate_handle)
	CoLateUpdateBeat:AddListener(self.__colateupdate_handle)
	CoFixedUpdateBeat:AddListener(self.__cofixedupdate_handle)
end

-- 释放
local function Dispose(self)
	if self.__update_handle ~= nil then
		UpdateBeat:RemoveListener(self.__update_handle)
		self.__update_handle = nil
	end
	if self.__lateupdate_handle ~= nil then
		LateUpdateBeat:RemoveListener(self.__lateupdate_handle)
		self.__lateupdate_handle = nil
	end
	if self.__fixedupdate_handle ~= nil then
		FixedUpdateBeat:RemoveListener(self.__fixedupdate_handle)
		self.__fixedupdate_handle = nil
	end
	if self.__coupdate_handle ~= nil then
		CoUpdateBeat:RemoveListener(self.__coupdate_handle)
		self.__coupdate_handle = nil
	end
	if self.__colateupdate_handle ~= nil then
		CoLateUpdateBeat:RemoveListener(self.__colateupdate_handle)
		self.__colateupdate_handle = nil
	end
	if self.__cofixedupdate_handle ~= nil then
		CoFixedUpdateBeat:RemoveListener(self.__cofixedupdate_handle)
		self.__cofixedupdate_handle = nil
	end
end

-- 清理:可用在场景切换前,不清理关系也不大,只是缓存池不会下降
local function Cleanup(self)
	self.__update_timer = {}
	self.__lateupdate_timer = {}
	self.__fixedupdate_timer = {}
	self.__coupdate_timer = {}
	self.__colateupdate_timer = {}
	self.__cofixedupdate_timer = {}
	self.__pool = {}
	self.__update_toadd = {}
	self.__lateupdate_toadd = {}
	self.__fixedupdate_toadd = {}
	self.__coupdate_toadd = {}
	self.__colateupdate_toadd = {}
	self.__cofixedupdate_toadd = {}
end

-- 获取定时器
local function InnerGetTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	local timer = nil
	if table.length(self.__pool) > 0 then
		timer = table.remove(self.__pool)
		if delay and func then
			timer:Init(delay, func, obj, one_shot, use_frame, unscaled)
		end
	else
		timer = Timer.New(delay, func, obj, one_shot, use_frame, unscaled)
	end
	return timer
end

-- 获取Update定时器
local function GetTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	assert(not self.__update_timer[timer] and not self.__update_toadd[timer])
	local timer = InnerGetTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	self.__update_toadd[timer] = true
	return timer
end

-- 获取LateUpdate定时器
local function GetLateTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	assert(not self.__lateupdate_timer[timer] and not self.__lateupdate_toadd[timer])
	local timer = InnerGetTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	self.__lateupdate_toadd[timer] = true
	return timer
end

-- 获取FixedUpdate定时器
local function GetFixedTimer(self, delay, func, obj, one_shot, use_frame)
	assert(not self.__fixedupdate_timer[timer] and not self.__fixedupdate_toadd[timer])
	local timer = InnerGetTimer(self, delay, func, obj, one_shot, use_frame, false)
	self.__fixedupdate_toadd[timer] = true
	return timer
end

-- 获取CoUpdate定时器
local function GetCoTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	assert(not self.__coupdate_timer[timer] and not self.__coupdate_toadd[timer])
	local timer = InnerGetTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	self.__coupdate_toadd[timer] = true
	return timer
end

-- 获取CoLateUpdate定时器
local function GetCoLateTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	assert(not self.__colateupdate_timer[timer] and not self.__colateupdate_toadd[timer])
	local timer = InnerGetTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	self.__colateupdate_toadd[timer] = true
	return timer
end

-- 获取CoFixedUpdate定时器
local function GetCoFixedTimer(self, delay, func, obj, one_shot, use_frame)
	assert(not self.__cofixedupdate_timer[timer] and not self.__cofixedupdate_toadd[timer])
	local timer = InnerGetTimer(self, delay, func, obj, one_shot, use_frame, unscaled)
	self.__cofixedupdate_toadd[timer] = true
	return timer
end

-- 析构函数
local function __delete(self)
	self:Cleanup()
	self.__update_handle = nil
	self.__lateupdate_handle = nil
	self.__fixedupdate_handle = nil
	self.__coupdate_handle = nil
	self.__colateupdate_handle = nil
	self.__cofixedupdate_handle = nil
	self.__update_timer = nil
	self.__lateupdate_timer = nil
	self.__fixedupdate_timer = nil
	self.__coupdate_timer = nil
	self.__colateupdate_timer = nil
	self.__cofixedupdate_timer = nil
	self.__pool = nil
	self.__update_toadd = nil
	self.__lateupdate_toadd = nil
	self.__fixedupdate_toadd = nil
	self.__coupdate_toadd = nil
	self.__colateupdate_toadd = nil
	self.__cofixedupdate_toadd = nil
end

TimerManager.__init = __init
TimerManager.Startup = Startup
TimerManager.Cleanup = Cleanup
TimerManager.Dispose = Dispose
TimerManager.GetTimer = GetTimer
TimerManager.GetLateTimer = GetLateTimer
TimerManager.GetFixedTimer = GetFixedTimer
TimerManager.GetCoTimer = GetCoTimer
TimerManager.GetCoLateTimer = GetCoLateTimer
TimerManager.GetCoFixedTimer = GetCoFixedTimer
TimerManager.__delete = __delete
return TimerManager;
  1. Timer.lua
--[[
-- 定时器
-- 注意:
-- 1、定时器需要暂停使用pause、恢复使用resume
-- 2、定时器使用stop停止,一旦停止逻辑层脚本就应该将引用置空,因为它随后会被管理类回收,引用已经不再正确
--]]

local Timer = BaseClass("Timer")

-- 构造函数
local function __init(self, delay, func, obj, one_shot, use_frame, unscaled)
	-- 成员变量
	-- weak表,保证定时器不影响目标对象的回收
	self.target = setmetatable({}, {__mode = "v"})
	if delay and func then
		self:Init(delay, func, obj, one_shot, use_frame, unscaled)
	end
end

-- Init
local function Init(self, delay, func, obj, one_shot, use_frame, unscaled)
	assert(type(delay) == "number" and delay >= 0)
	assert(func ~= nil)
	-- 时长,秒或者帧
	self.delay = delay
	-- 回调函数
	self.target.func = func
	-- 回传对象,一般作为回调函数第一个self参数
	self.target.obj = obj
	-- 是否是一次性计时
	self.one_shot = one_shot
	-- 是否是帧定时器,否则为秒定时器
	self.use_frame = use_frame
	-- 使用deltaTime计时,还是采用unscaledDeltaTime计时
	self.unscaled = unscaled
	-- 是否已经启用
	self.started = false
	-- 倒计时
	self.left = delay
	-- 是否已经结束
	self.over = false
	-- 传入对象是否为空
	self.obj_not_nil = obj and true or false
	-- 启动定时器时的帧数
	self.start_frame_count = Time.frameCount
end

-- Update
local function Update(self, is_fixed)
	if not self.started or self.over then
		return
	end
	
	local timeup = false
	if self.use_frame then
		-- TODO:这里有个经常会落后一帧的问题,一般出现在协程当中--当协程启用另外的协程时
		-- 协程不做精确定时,一般用于异步等待或者分帧操作,所以这里暂时没有什么影响,后面看是否需要修改
		timeup = (Time.frameCount >= self.start_frame_count + self.delay)
	else
		local delta = nil
		if is_fixed then
			delta = Time.fixedDeltaTime
		else
			delta = not self.unscaled and Time.deltaTime or Time.unscaledDeltaTime
		end
		self.left = self.left - delta
		timeup = (self.left <= 0)
	end
	
	if timeup then
		if self.target.func ~= nil then
			-- 说明:这里一定要先改状态,后回调
			-- 如果回调手动删除定时器又马上再次获取,则可能得到的是同一个定时器,再修改状态就不对了
			-- added by Circle @ 2018-01-09:TimerManager已经被重构,不存在以上问题,但是这里的代码不再做调整
			if not self.one_shot then
				if not self.use_frame then
					-- 说明:必须把上次计时“欠下”的时间考虑进来,否则会有误差
					self.left = self.delay + self.left
				end
				self.start_frame_count = Time.frameCount
			else
				self.over = true
			end
			-- 说明:运行在保护模式,有错误也只是停掉定时器,不要让客户端挂掉
			local status, err
			if self.obj_not_nil then
				status, err = pcall(self.target.func, self.target.obj)
			else
				status, err = pcall(self.target.func)
			end
			if not status then
				self.over = true
				Logger.LogError(err)
			end
		else
			self.over = true
		end
	end
end

-- 启动计时
local function Start(self)
	if self.over then
		Logger.LogError("You can't start a overed timer, try add a new one!")
	end
	if not self.started then
		self.left = self.delay
		self.started = true
		self.start_frame_count = Time.frameCount
	end
end

-- 暂停计时
local function Pause(self)
	self.started = false
end

-- 恢复计时
local function Resume(self)
	self.started = true
end

-- 停止计时
local function Stop(self)
	self.left = 0
	self.one_shot = false
	self.target.func = nil
	self.target.obj = nil
	self.use_frame = false
	self.unscaled = false
	self.started = false
	self.over = true
end

-- 复位:如果计时器是启动的,并不会停止,只是刷新倒计时
local function Reset(self)
	self.left = self.delay
	self.start_frame_count = Time.frameCount
end

-- 是否已经完成计时
local function IsOver(self)
	if self.target.func == nil then
		return true
	end
	if self.obj_not_nil and self.target.func == nil then
		return true
	end
	return self.over
end

Timer.__init = __init
Timer.Init = Init
Timer.Update = Update
Timer.Start = Start
Timer.Pause = Pause
Timer.Resume = Resume
Timer.Stop = Stop
Timer.Reset = Reset
Timer.IsOver = IsOver
return Timer;
  1. Coroutine.lua
--[[
-- 协程模块:对Lua协程conroutine进行扩展,使其具有Unity侧协程的特性
-- 注意:
-- 1、主线程使用coroutine.start启动协程,协程启动以后,首次挂起时主线程继续往下执行,这里和Unity侧表现是一致的
-- 2、协程里可以再次使用coroutine.start启动协程,和在Unity侧协程中使用StartCoroutine表现一致
-- 3、协程里启动子级协程并等待其执行完毕,在Unity侧是yield return StartCoroutine,但是在Lua不需要另外启动协程,直接调用函数即可
-- 4、如果lua侧协程不使用本脚本的扩展函数,则无法实现分帧;lua侧启动协程以后不管协程函数调用栈多深,不管使用多少次本脚本扩展函数,都运行在一个协程
-- 5、使用coroutine.waitforframes(1)来等待一帧,千万不要用coroutine.yield,否则整个协程将永远不被唤醒===>***很重要,除非你在其它地方手动唤醒它
-- 6、子级协同在lua侧等同于普通函数调用,和普通函数一样可在退出函数时带任意返回值,而Unity侧协同不能获取子级协同退出时的返回值
-- 7、理论上任何协同都可以用回调方式去做,但是对于异步操作,回调方式也需要每帧去检测异步操作是否完成,消耗相差不多,而使用协同可以简单很多,清晰很多
-- 8、协程所有等待时间的操作,如coroutine.waitforseconds误差由帧率决定,循环等待时有累积误差,所以最好只是用于分帧,或者等待异步操作
-- 9、yieldstart、yieldreturn、yieldbreak实际上是用lua不对称协程实现对称协同,使用方式和Unity侧协同类似,注意点看相关函数头说明
-- TODO:
-- 1、CS侧做可视化调试器,方便单步查看各个协程运行状态
--]]

-- 协程内部使用定时器实现,定时器是weak表,所以这里必须缓存Action,否则会被GC回收
local action_map = {}
-- action缓存池
local action_pool = {}
-- 用于子级协程yieldreturn时寻找父级协程
local yield_map = {}
-- 协程数据缓存池
local yield_pool = {}
-- 协程缓存池
local co_pool = {}

-- 回收协程
local function __RecycleCoroutine(co)
	if not coroutine.status(co) == "suspended" then
		error("Try to recycle coroutine not suspended : "..coroutine.status(co))
	end
	
	table.insert(co_pool, co)
end

-- 可复用协程
local function __Coroutine(func, ...)
	local args = SafePack(...)
	while func do
		local ret = SafePack(func(SafeUnpack(args)))
		__RecycleCoroutine(coroutine.running())
		args = SafePack(coroutine.yield(SafeUnpack(ret)))
		func = args[1]
		table.remove(args, 1)
	end
end

-- 获取协程
local function __GetCoroutine()
	local co = nil
	if table.length(co_pool) > 0 then
		co = table.remove(co_pool)
	else
		co = coroutine.create(__Coroutine)
	end
	return co
end

-- 回收Action
local function __RecycleAction(action)
	action.co = false
	action.timer = false
	action.func = false
	action.args = false
	action.result = false
	table.insert(action_pool, action)
end

-- 获取Action
local function __GetAction(co, timer, func, args, result)
	local action = nil
	if table.length(action_pool) > 0 then
		action = table.remove(action_pool)
	else
		action = {false, false, false, false, false}
	end
	action.co = co and co or false
	action.timer = timer and timer or false
	action.func = func and func or false
	action.args = args and args or false
	action.result = result and result or false
	return action
end

-- 协程运行在保护模式下,不会抛出异常,所以这里要捕获一下异常
-- 但是可能会遇到调用协程时对象已经被销毁的情况,这种情况应该被当做正常情况
-- 所以这里并不继续抛出异常,而只是输出一下错误日志,不要让客户端当掉
-- 注意:Logger中实际上在调试模式会抛出异常
local function __PResume(co, func, ...)
	local resume_ret = nil
	if func ~= nil then
		resume_ret = SafePack(coroutine.resume(co, func, ...))
	else
		resume_ret = SafePack(coroutine.resume(co, ...))
	end
	local flag, msg = resume_ret[1], resume_ret[2]
	if not flag then
		Logger.LogError(msg.."\n"..debug.traceback(co))
	elseif resume_ret.n > 1 then
		table.remove(resume_ret, 1)
	else
		resume_ret = nil
	end
	return flag, resume_ret
end

-- 启动一个协程:等待协程第一次让出控制权时主函数继续往下执行,这点表现和Unity协程一致
-- 等同于Unity侧的StartCoroutine
-- @func:协程函数体
-- @...:传入协程的可变参数
local function start(func, ...)
	local co = __GetCoroutine()
	__PResume(co, func, ...)
	return co
end

-- 启动一个协程并等待
-- 注意:
-- 1、这里会真正启动一个子级协程,比起在协程中直接函数调用开销稍微大点,但是灵活度很高
-- 2、最大好处就是可以在子级协程中使用yieldreturn返回值给父级协程执行一次某个回调,用于交付数据给父级协程
-- 3、如果子级协程没有结束,父级协程会在执行完回调之后等待一帧再次启动子级协程
-- 4、具体运用参考场景管理(ScenceManager)部分控制加载界面进度条的代码,十分清晰简洁
-- 5、如果不需要callback,即不需要子级协程交付数据,别使用yieldstart,直接使用普通函数调用方式即可
-- 6、回调callback函数体一般处理的是父级协程的逻辑,但是跑在子级协程内,这点要注意,直到yieldbreak父级协程都是挂起的
-- @func:子级协程函数体
-- @callback:子级协程在yieldreturn转移控制权给父级协程时,父级协程跑的回调,这个回调会填入子级协程yieldreturn时的参数
-- @...:传递给子级协程的可变参数
local function yieldstart(func, callback, ...)
	local co = coroutine.running() or error ('coroutine.yieldstart must be run in coroutine')
	local map = nil
	if table.length(yield_pool) > 0 then
		map = table.remove(yield_pool)
		map.parent = co
		map.callback = callback
		map.waiting = false
		map.over = false
	else
		map = {parent = co, callback = callback, waiting = false, over = false}
	end
	
	local child = __GetCoroutine()
	yield_map[child] = map
	local flag, resume_ret = __PResume(child, func, ...)
	if not flag then
		table.insert(yield_pool, map)
		yield_map[child] = nil
		return nil
	elseif map.over then
		table.insert(yield_pool, map)
		yield_map[child] = nil
		if resume_ret == nil then
			return nil
		else
			return SafeUnpack(resume_ret)
		end
	else
		map.waiting = true
		local yield_ret = SafePack(coroutine.yield())
		table.insert(yield_pool, map)
		yield_map[child] = nil
		return SafeUnpack(yield_ret)
	end
end

-- 子级协程将控制权转移给父级协程,并交付数据给父级协程回调,配合yieldstart使用
-- 注意:
-- 1、与Unity侧协程yield return不同,对子级协程来说yieldreturn一定会等待一帧再往下执行
local function yieldreturn(...)
	local co = coroutine.running() or error ("coroutine.yieldreturn must be run in coroutine")
	local map = yield_map[co]
	local parent = map.parent
	-- 没有父级协程,啥都不做
	if not map or not parent then
		return
	end
	
	local callback = map.callback
	assert(callback ~= nil, "If you don't need callback, use normal function call instead!!!")
	callback(co, ...)
	
	-- 子级协程等待一帧再继续往下执行
 	return coroutine.waitforframes(1)
end

-- 子级协程在异步回调中交付数据给父级协程回调,配合yieldstart使用
-- 注意:
-- 1、子级协程异步回调并没有运行在子级协程当中,不能使用yieldreturn,实际上不能使用任何协程相关接口,除了start
-- 2、yieldcallback需要传递当前的子级协程,这个可以从异步回调的首个参数获取
-- 3、不会等待一帧,实际上协程中的回调是每帧执行一次的
local function yieldcallback(co, ...)
	assert(co ~= nil and type(co) == "thread")
	local map = yield_map[co]
	-- 没有父级协程,啥都不做
	if not map or not map.parent then
		return
	end
	
	local callback = map.callback
	assert(callback ~= nil, "If you don't need callback, use normal function call instead!!!")
	callback(co, ...)
end

-- 退出子级协程,将控制权转移给父级协程,并交付数据作为yieldstart返回值,配合yieldstart使用
-- 注意:
-- 1、一定要使用return coroutine.yieldbreak退出===>很重要***
-- 2、不使用coroutine.yieldbreak无法唤醒父级协程
-- 3、不使用return,可能无法正确退出子级协程
local function yieldbreak(...)
	local co = coroutine.running() or error ("coroutine.yieldbreak must be run in coroutine")
	local map = yield_map[co]
	-- 没有父级协程
	if not map then
		return ...
	end
	
	map.over = true
	assert(map.parent ~= nil, "What's the fuck!!!")
	if not map.waiting then
		return ...
	else
		__PResume(map.parent, nil, ...)
	end
end

local function __Action(action, abort, ...)
	assert(action.timer)
	if not action.func then
		abort = true
	end
	
	if not abort and action.func then
		if action.args and action.args.n > 0 then
			abort = (action.func(SafeUnpack(action.args)) == action.result)
		else
			abort = (action.func() == action.result)
		end
	end
	
	if abort then
		action.timer:Stop()
		action_map[action.co] = nil
		__PResume(action.co, ...)
		__RecycleAction(action)
	end
end

-- 等待下次FixedUpdate,并在FixedUpdate执行完毕后resume
-- 等同于Unity侧的yield return new WaitForFixedUpdate
local function waitforfixedupdate()
	local co = coroutine.running() or error ("coroutine.waitforfixedupdate must be run in coroutine")
	local timer = TimerManager:GetInstance():GetCoFixedTimer()
	local action = __GetAction(co, timer)
	
	timer:Init(0, __Action, action, true, true)
 	timer:Start()
	action_map[co] = action
 	return coroutine.yield()
end

-- 等待帧数,并在Update执行完毕后resume
local function waitforframes(frames)
	assert(type(frames) == "number" and frames >= 1 and math.floor(frames) == frames)
	local co = coroutine.running() or error ("coroutine.waitforframes must be run in coroutine")
	local timer = TimerManager:GetInstance():GetCoTimer()
	local action = __GetAction(co, timer)
	
	timer:Init(frames, __Action, action, true, true)
 	timer:Start()
	action_map[co] = action
 	return coroutine.yield()
end

-- 等待秒数,并在Update执行完毕后resume
-- 等同于Unity侧的yield return new WaitForSeconds
local function waitforseconds(seconds)
	assert(type(seconds) == "number" and seconds >= 0)
	local co = coroutine.running() or error ("coroutine.waitforsenconds must be run in coroutine")
	local timer = TimerManager:GetInstance():GetCoTimer()
	local action = __GetAction(co, timer)
	
	timer:Init(seconds, __Action, action, true)
 	timer:Start()
	action_map[co] = action
 	return coroutine.yield()
end

local function __AsyncOpCheck(co, async_operation, callback)
	if callback ~= nil then
		callback(co, async_operation.progress)
	end
	return async_operation.isDone
end

-- 等待异步操作完成,并在Update执行完毕resume
-- 等同于Unity侧的yield return AsyncOperation
-- 注意:yield return WWW也是这种情况之一
-- @async_operation:异步句柄---或者任何带有isDone、progress成员属性的异步对象
-- @callback:每帧回调,传入参数为异步操作进度progress
local function waitforasyncop(async_operation, callback)
	assert(async_operation)
	local co = coroutine.running() or error ("coroutine.waitforasyncop must be run in coroutine")
	local timer = TimerManager:GetInstance():GetCoTimer()
	local action = __GetAction(co, timer, __AsyncOpCheck, SafePack(co, async_operation, callback), true)
	
	timer:Init(1, __Action, action, false, true)
 	timer:Start()
	action_map[co] = action
 	return coroutine.yield()
end

-- 等待条件为真,并在Update执行完毕resume
-- 等同于Unity侧的yield return new WaitUntil
local function waituntil(func, ...)
	assert(func)
	local co = coroutine.running() or error ("coroutine.waituntil must be run in coroutine")
	local timer = TimerManager:GetInstance():GetCoTimer()
	local action = __GetAction(co, timer, func, SafePack(...), true)
	
	timer:Init(1, __Action, action, false, true)
 	timer:Start()
	action_map[co] = action
 	return coroutine.yield()
end

-- 等待条件为假,并在Update执行完毕resume
-- 等同于Unity侧的yield return new WaitWhile
local function waitwhile(func, ...)
	assert(func)
	local co = coroutine.running() or error ("coroutine.waitwhile must be run in coroutine")
	local timer = TimerManager:GetInstance():GetCoTimer()
	local action = __GetAction(co, timer, func, SafePack(...), false)
	
	timer:Init(1, __Action, action, false, true)
 	timer:Start()
	action_map[co] = action
 	return coroutine.yield()
end

-- 等待本帧结束,并在进入下一帧之前resume
-- 等同于Unity侧的yield return new WaitForEndOfFrame
local function waitforendofframe()
	local co = coroutine.running() or error ("coroutine.waitforendofframe must be run in coroutine")
	local timer = TimerManager:GetInstance():GetCoLateTimer()
	local action = __GetAction(co, timer)
	
	timer:Init(0, __Action, action, true, true)
 	timer:Start()
	action_map[co] = action
 	return coroutine.yield()
end

-- 终止协程等待操作(所有waitXXX接口)
local function stopwaiting(co, ...)
	local action = action_map[co]
	if action then
		__Action(action, true, ...)
	end
end

coroutine.start = start
coroutine.yieldstart = yieldstart
coroutine.yieldreturn = yieldreturn
coroutine.yieldcallback = yieldcallback
coroutine.yieldbreak = yieldbreak
coroutine.waitforfixedupdate = waitforfixedupdate
coroutine.waitforframes = waitforframes
coroutine.waitforseconds = waitforseconds
coroutine.waitforasyncop = waitforasyncop
coroutine.waituntil = waituntil
coroutine.waitwhile = waitwhile
coroutine.waitforendofframe = waitforendofframe
coroutine.stopwaiting = stopwaiting

-- 调试用:查看内部状态
if Config.Debug then
	return{
		action_map = action_map,
		action_pool = action_pool,
		yield_map = yield_map,
		yield_pool = yield_pool,
		co_pool = co_pool,
	}
end

支付宝捐赠

打赏红包

你可能感兴趣的:(Lua)