9. Unity3d Lua框架 - xLua - SceneManager 场景管理系统:调度和控制场景异步加载以及进度管理,展示loading界面和更新进度条数据,GC、卸载未使用资源等

--[[
-- 场景管理系统:调度和控制场景异步加载以及进度管理,展示loading界面和更新进度条数据,GC、卸载未使用资源等
-- 注意:
-- 1、资源预加载放各个场景类中自行控制
-- 2、场景loading的UI窗口这里统一管理,由于这个窗口很简单,更新进度数据时直接写Model层
--]]

--[[
-- 场景基类,各场景类从这里继承:提供统一的场景加载和初始化步骤,负责资源预加载
--]]

支付宝捐赠

打赏红包

  1. SceneManager.lua
--[[
-- 场景管理系统:调度和控制场景异步加载以及进度管理,展示loading界面和更新进度条数据,GC、卸载未使用资源等
-- 注意:
-- 1、资源预加载放各个场景类中自行控制
-- 2、场景loading的UI窗口这里统一管理,由于这个窗口很简单,更新进度数据时直接写Model层
--]]

local SceneManager = BaseClass("SceneManager", Singleton)

-- 构造函数
local function __init(self)
	-- 成员变量
	-- 当前场景
	self.current_scene = nil
	-- 是否忙
	self.busing = false
	-- 场景对象
	self.scenes = {}
end

-- 切换场景:内部使用协程
local function CoInnerSwitchScene(self, scene_config)
	-- 打开loading界面
	local uimgr_instance = UIManager:GetInstance()
	uimgr_instance:OpenWindow(UIWindowNames.UILoading)
	local window = uimgr_instance:GetWindow(UIWindowNames.UILoading)
	local model = window.Model
	model.value = 0
	coroutine.waitforframes(1)
	-- 等待资源管理器加载任务结束,否则很多Unity版本在切场景时会有异常,甚至在真机上crash
	coroutine.waitwhile(function()
		return ResourcesManager:GetInstance():IsProsessRunning()
	end)
	-- 清理旧场景
	if self.current_scene then
		self.current_scene:OnLeave()
	end
	model.value = model.value + 0.01
	coroutine.waitforframes(1)
	-- 清理UI
	uimgr_instance:DestroyWindowExceptLayer(UILayers.TopLayer)
	model.value = model.value + 0.01
	coroutine.waitforframes(1)
	-- 清理资源缓存
	GameObjectPool:GetInstance():Cleanup(true)
	model.value = model.value + 0.01
	coroutine.waitforframes(1)
	ResourcesManager:GetInstance():Cleanup()
	model.value = model.value + 0.01
	coroutine.waitforframes(1)
	-- 同步加载loading场景
	local scene_mgr = CS.UnityEngine.SceneManagement.SceneManager
	local resources = CS.UnityEngine.Resources
	scene_mgr.LoadScene(SceneConfig.LoadingScene.Level)
	model.value = model.value + 0.01
	coroutine.waitforframes(1)
	-- GC:交替重复2次,清干净一点
	collectgarbage("collect")
	CS.System.GC.Collect()
	collectgarbage("collect")
	CS.System.GC.Collect()
	local cur_progress = model.value
	coroutine.waitforasyncop(resources.UnloadUnusedAssets(), function(co, progress)
		assert(progress <= 1.0, "What's the funck!!!")
		model.value = cur_progress + 0.1 * progress
	end)
	model.value = cur_progress + 0.1
	coroutine.waitforframes(1)
	-- 初始化目标场景
	local logic_scene = self.scenes[scene_config.Name]
	if logic_scene == nil then
		logic_scene = scene_config.Type.New(scene_config)
		self.scenes[scene_config.Name] = logic_scene
	end
	assert(logic_scene ~= nil)
	logic_scene:OnEnter()
	model.value = model.value + 0.02
	coroutine.waitforframes(1)
	-- 异步加载目标场景
	cur_progress = model.value
	coroutine.waitforasyncop(scene_mgr.LoadSceneAsync(scene_config.Level), function(co, progress)
		assert(progress <= 1.0, "What's the funck!!!")
		model.value = cur_progress + 0.15 * progress
	end)
	model.value = cur_progress + 0.15
	coroutine.waitforframes(1)
	-- 准备工作:预加载资源等
	-- 说明:现在的做法是不热更场景(都是空场景),所以主要的加载时间会放在场景资源的prefab上,这里给65%的进度时间
	cur_progress = model.value
	coroutine.yieldstart(logic_scene.CoOnPrepare, function(co, progress)
		assert(progress <= 1.0, "Progress should be normalized value!!!")
		model.value = cur_progress + 0.65 * progress
	end, logic_scene)
	model.value = cur_progress + 0.65
	coroutine.waitforframes(1)
	logic_scene:OnComplete()
	model.value = 1.0
	coroutine.waitforframes(3)
	-- 加载完成,关闭loading界面
	uimgr_instance:DestroyWindow(UIWindowNames.UILoading)
	self.current_scene = logic_scene
	self.busing = false
end

-- 切换场景
local function SwitchScene(self, scene_config)
	assert(scene_config ~= LaunchScene and scene_config ~= LoadingScene)
	assert(scene_config.Type ~= nil)
	if self.busing then 
		return
	end
	if self.current_scene and self.current_scene.scene_config.Name == scene_config.Name then
		return
	end
	
	self.busing = true
	coroutine.start(CoInnerSwitchScene, self, scene_config)
end

-- 析构函数
local function __delete(self)
	for _, scene in pairs(self.scenes) do
		scene:Delete()
	end
end

SceneManager.__init = __init
SceneManager.SwitchScene = SwitchScene
SceneManager.__delete = __delete

return SceneManager;
  1. BaseScene.lua
--[[
-- 场景基类,各场景类从这里继承:提供统一的场景加载和初始化步骤,负责资源预加载
--]]

local BaseScene = BaseClass("BaseScene")

-- 构造函数,别重写,初始化放OnInit
local function __init(self, scene_config)
	-- 场景配置
	self.scene_config = scene_config
	-- 预加载资源:资源路径、资源类型
	self.preload_resources = {}
	-- 预加载GameObject:资源路径、实例化个数
	self.preload_prefab = {}
	self:OnCreate()
end

-- 析构函数,别重写,资源释放放OnDispose
local function __delete(self)
	self:OnDestroy()
end

-- 创建:初始化一些需要全局保存的状态
local function OnCreate(self)
end

-- 添加预加载资源
-- 注意:只有prefab类型才需要填inst_count,用于指定初始实例化个数
local function AddPreloadResource(self, path, res_type, inst_count)
	assert(res_type ~= nil)
	assert(type(path) == "string" and #path > 0)
	if res_type == typeof(CS.UnityEngine.GameObject) then
		self.preload_prefab[path] = inst_count
	else
		self.preload_resources[path] = res_type
	end
end

-- 加载前的初始化
local function OnEnter(self)
end

-- 场景加载结束:后续资源准备(预加载等)
-- 注意:这里使用协程,子类别重写了,需要加载的资源添加到列表就可以了
local function CoOnPrepare(self)
	local res_count = table.count(self.preload_resources)
	local prefab_count = table.count(self.preload_prefab)
	local total_count = res_count + prefab_count
	if total_count <= 0 then
		return coroutine.yieldbreak()
	end
	
	-- 进度条切片,已加载数目
	-- 注意:这里的进度被归一化,所有预加载资源占场景加载百分比由SceneManager决定
	local progress_slice = 1.0 / total_count
	local finish_count = 0
	local prefab_type = typeof(CS.UnityEngine.GameObject)
	
	local function ProgressCallback(co, progress)
		assert(progress <= 1.0, "What's the fuck!!!")
		return coroutine.yieldcallback(co, (finish_count + progress) * progress_slice)
	end

	for res_path,res_type in pairs(self.preload_resources) do
		ResourcesManager:GetInstance():CoLoadAsync(res_path, res_type, ProgressCallback)
		finish_count = finish_count + 1
		coroutine.yieldreturn(finish_count * progress_slice)
	end
	for res_path,inst_count in pairs(self.preload_prefab) do
		GameObjectPool:GetInstance():CoPreLoadGameObjectAsync(res_path, inst_count, ProgressCallback)
		finish_count = finish_count + 1
		coroutine.yieldreturn(finish_count * progress_slice)
	end
	return coroutine.yieldbreak()
end

-- 场景加载完毕
local function OnComplete(self)
end

-- 离开场景:清理场景资源
local function OnLeave(self)
end

-- 销毁:释放全局保存的状态
local function OnDestroy(self)
	self.scene_config = nil
	self.preload_resources = nil
end

BaseScene.__init = __init
BaseScene.__delete = __delete
BaseScene.OnCreate = OnCreate
BaseScene.AddPreloadResource = AddPreloadResource
BaseScene.OnEnter = OnEnter
BaseScene.CoOnPrepare = CoOnPrepare
BaseScene.OnComplete = OnComplete
BaseScene.OnLeave = OnLeave
BaseScene.OnDestroy = OnDestroy

return BaseScene

支付宝捐赠

打赏红包

你可能感兴趣的:(Lua)