8. Unity3d Lua框架 - xLua - Resource 资源管理系统:提供资源加载管理

--[[
-- 资源管理系统:提供资源加载管理
-- 注意:
-- 1、只提供异步接口,即使内部使用的是同步操作,对外来说只有异步
-- 2、两套API:使用回调(任何不带"Co"的接口)、使用协程(任何带"Co"的接口)
-- 3、对于串行执行一连串的异步操作,建议使用协程(用同步形式的代码写异步逻辑),回调方式会使代码难读
-- 4、所有lua层脚本别直接使用cs侧的AssetBundleManager,都来这里获取接口
-- 5、理论上做到逻辑层脚本对AB名字是完全透明的,所有资源只有packagePath的概念,这里对路径进行处理
--]]
--[[
-- GameObject缓存池
-- 注意:
-- 1、所有需要预设都从这里加载,不要直接到ResourcesManager去加载,由这里统一做缓存管理
-- 2、缓存分为两部分:从资源层加载的原始GameObject(Asset),从GameObject实例化出来的多个Inst
--]]
--[[
-- 图集管理:为逻辑层透明化图集路径和图集资源加载等底层操作
-- 注意:
-- 1、只提供异步操作,为的是不需要逻辑层取操心图集AB是否已经加载的问题
-- 2、图集管理器不做资源缓存
-- 3、图片名称带后缀
--]]

支付宝捐赠

打赏红包

1 . ResourcesManager.lua

--[[
-- 资源管理系统:提供资源加载管理
-- 注意:
-- 1、只提供异步接口,即使内部使用的是同步操作,对外来说只有异步
-- 2、两套API:使用回调(任何不带"Co"的接口)、使用协程(任何带"Co"的接口)
-- 3、对于串行执行一连串的异步操作,建议使用协程(用同步形式的代码写异步逻辑),回调方式会使代码难读
-- 4、所有lua层脚本别直接使用cs侧的AssetBundleManager,都来这里获取接口
-- 5、理论上做到逻辑层脚本对AB名字是完全透明的,所有资源只有packagePath的概念,这里对路径进行处理
--]]

local ResourcesManager = BaseClass("ResourcesManager", Singleton)
local AssetBundleManager = CS.AssetBundles.AssetBundleManager.Instance
local AssetBundleUtility = CS.AssetBundles.AssetBundleUtility

-- 是否有加载任务正在进行
local function IsProsessRunning(self)
	return AssetBundleManager.IsProsessRunning
end

-- 设置常驻包
-- 注意:
-- 1、公共包(被2个或者2个其它AB包所依赖的包)底层会自动设置常驻包
-- 2、任何情况下不想被卸载的非公共包(如Lua脚本)需要手动设置常驻包
local function SetAssetBundleResident(self, path, resident)
	local assetbundleName = AssetBundleUtility.AssetBundlePathToAssetBundleName(path)
	resident = resident and true or false
	AssetBundleManager:SetAssetBundleResident(assetbundleName, resident)
end

-- 异步加载AssetBundle:回调形式
local function LoadAssetBundleAsync(self, path, callback, ...)
	assert(path ~= nil and type(path) == "string" and #path > 0, "path err : "..path)
	assert(callback ~= nil and type(callback) == "function", "Need to provide a function as callback")
	local args = SafePack(...)
	coroutine.start(function()
		local assetbundle = self:CoLoadAssetBundleAsync(path, nil)
		callback(SafeUnpack(args))
	end)
end

-- 异步加载AssetBundle:协程形式
local function CoLoadAssetBundleAsync(self, path, progress_callback)
	assert(path ~= nil and type(path) == "string" and #path > 0, "path err : "..path)
	local assetbundleName = AssetBundleUtility.AssetBundlePathToAssetBundleName(path)
	local loader = AssetBundleManager:LoadAssetBundleAsync(assetbundleName)
	coroutine.waitforasyncop(loader, progress_callback)
    loader:Dispose()
end

-- 异步加载Asset:回调形式
local function LoadAsync(self, path, res_type, callback, ...)
	assert(path ~= nil and type(path) == "string" and #path > 0, "path err : "..path)
	assert(callback ~= nil and type(callback) == "function", "Need to provide a function as callback")
	local args = SafePack(nil, ...)
	coroutine.start(function()
		local asset = self:CoLoadAsync(path, res_type, nil)
		args[1] = asset
		callback(SafeUnpack(args))
	end)
end

-- 异步加载Asset:协程形式
local function CoLoadAsync(self, path, res_type, progress_callback)
	assert(path ~= nil and type(path) == "string" and #path > 0, "path err : "..path)
	local loader = AssetBundleManager:LoadAssetAsync(path, res_type)
	coroutine.waitforasyncop(loader, progress_callback)
	local asset = loader.asset
    loader:Dispose()
	if IsNull(asset) then
		Logger.LogError("Asset load err : "..path)
	end
	return asset
end

-- 清理资源:切换场景时调用
local function Cleanup(self)
	AssetBundleManager:ClearAssetsCache()
	AssetBundleManager:UnloadAllUnusedResidentAssetBundles()
	
	-- TODO:Lua脚本要重新加载,暂时吧,后面缓缓策略
	local luaAssetbundleName = CS.XLuaManager.Instance.AssetbundleName
	AssetBundleManager:AddAssetbundleAssetsCache(luaAssetbundleName)
end

ResourcesManager.IsProsessRunning = IsProsessRunning
ResourcesManager.SetAssetBundleResident = SetAssetBundleResident
ResourcesManager.LoadAssetBundleAsync = LoadAssetBundleAsync
ResourcesManager.CoLoadAssetBundleAsync = CoLoadAssetBundleAsync
ResourcesManager.LoadAsync = LoadAsync
ResourcesManager.CoLoadAsync = CoLoadAsync
ResourcesManager.Cleanup = Cleanup

return ResourcesManager

2 . GameObjectPool.lua

--[[
-- GameObject缓存池
-- 注意:
-- 1、所有需要预设都从这里加载,不要直接到ResourcesManager去加载,由这里统一做缓存管理
-- 2、缓存分为两部分:从资源层加载的原始GameObject(Asset),从GameObject实例化出来的多个Inst
--]]

local GameObjectPool = BaseClass("GameObjectPool", Singleton)
local __cacheTransRoot = nil
local __goPool = {}
local __instCache = {}

local function __init(self)
	local go = CS.UnityEngine.GameObject.Find("GameObjectCacheRoot")
	if go == nil then
		go = CS.UnityEngine.GameObject("GameObjectCacheRoot")
		CS.UnityEngine.Object.DontDestroyOnLoad(go)
	end
	__cacheTransRoot = go.transform
end

-- 初始化inst
local function InitInst(inst)
	if not IsNull(inst) then
		inst:SetActive(true)
	end
end

-- 检测是否已经被缓存
local function CheckHasCached(self, path)
	assert(path ~= nil and type(path) == "string" and #path > 0, "path err : "..path)
	assert(string.endswith(path, ".prefab", true), "GameObject must be prefab : "..path)
	
	local cachedInst = __instCache[path]
	if cachedInst ~= nil and table.length(cachedInst) > 0 then
		return true
	end
	
	local pooledGo = __goPool[path]
	return not IsNull(pooledGo)
end

-- 缓存并实例化GameObject
local function CacheAndInstGameObject(self, path, go, inst_count)
	assert(not IsNull(go))
	assert(inst_count == nil or type(inst_count) == "number" and inst_count >= 0)
	
	__goPool[path] = go
	if inst_count ~= nil and inst_count > 0 then
		local cachedInst = __instCache[path] or {}
		for i = 1, inst_count do
			local inst = CS.UnityEngine.GameObject.Instantiate(go)
			inst.transform:SetParent(__cacheTransRoot)
			inst:SetActive(false)
			table.insert(cachedInst, inst)
		end
		__instCache[path] = cachedInst
	end
end

-- 尝试从缓存中获取
local function TryGetFromCache(self, path)
	if not self:CheckHasCached(path) then
		return nil
	end
	
	local cachedInst = __instCache[path]
	if cachedInst ~= nil and table.length(cachedInst) > 0 then
		local inst = table.remove(cachedInst)
		assert(not IsNull(inst), "Something wrong, there gameObject instance in cache is null!")
		return inst
	end
	
	local pooledGo = __goPool[path]
	if not IsNull(pooledGo) then
		local inst = CS.UnityEngine.GameObject.Instantiate(pooledGo)
		return inst
	end
	
	return nil
end

-- 预加载:可提供初始实例化个数
local function PreLoadGameObjectAsync(self, path, inst_count, callback, ...)
	assert(inst_count == nil or type(inst_count) == "number" and inst_count >= 0)
	if self:CheckHasCached(path) then
		if callback then
			callback(...)
		end
		return
	end
	
	local args = SafePack(...)
	ResourcesManager:GetInstance():LoadAsync(path, typeof(CS.UnityEngine.GameObject), function(go)
		if not IsNull(go) then
			CacheAndInstGameObject(self, path, go, inst_count)
		end
		
		if callback then
			callback(SafeUnpack(args))
		end
	end)
end

-- 预加载:协程形式
local function CoPreLoadGameObjectAsync(self, path, inst_count, progress_callback)
	if self:CheckHasCached(path) then
		return
	end
	
	local go = ResourcesManager:GetInstance():CoLoadAsync(path, typeof(CS.UnityEngine.GameObject), progress_callback)
	if not IsNull(go) then
		CacheAndInstGameObject(self, path, go, inst_count)
	end
end

-- 异步获取:必要时加载
local function GetGameObjectAsync(self, path, callback, ...)
	local inst = self:TryGetFromCache(path)
	if not IsNull(inst) then
		InitInst(inst)
		callback(not IsNull(inst) and inst or nil, ...)
		return
	end
	
	self:PreLoadGameObjectAsync(path, 1, function(callback, ...)
		local inst = self:TryGetFromCache(path)
		InitInst(inst)
		callback(not IsNull(inst) and inst or nil, ...)
	end, callback, ...)
end

-- 异步获取:协程形式
local function CoGetGameObjectAsync(self, path, progress_callback)
	local inst = self:TryGetFromCache(path)
	if not IsNull(inst) then
		InitInst(inst)
		return inst
	end
	
	self:CoPreLoadGameObjectAsync(path, 1, progress_callback)
	local inst = self:TryGetFromCache(path)
	if not IsNull(inst) then
		InitInst(inst)
	end
	return inst
end

-- 回收
local function RecycleGameObject(self, path, inst)
	assert(not IsNull(inst))
	assert(path ~= nil and type(path) == "string" and #path > 0, "path err : "..path)
	assert(string.endswith(path, ".prefab", true), "GameObject must be prefab : "..path)
	
	inst.transform:SetParent(__cacheTransRoot)
	inst:SetActive(false)
	local cachedInst = __instCache[path] or {}
	table.insert(cachedInst, inst)
	__instCache[path] = cachedInst
end

-- 清理缓存
local function Cleanup(self, includePooledGo)
	for _,cachedInst in pairs(__instCache) do
		for _,inst in pairs(cachedInst) do
			if not IsNull(inst) then
				CS.UnityEngine.GameObject.Destroy(inst)
			end
		end
	end
	__instCache = {}
	
	if includePooledGo then
		__goPool = {}
	end
end

GameObjectPool.__init = __init
GameObjectPool.CheckHasCached = CheckHasCached
GameObjectPool.TryGetFromCache = TryGetFromCache
GameObjectPool.PreLoadGameObjectAsync = PreLoadGameObjectAsync
GameObjectPool.CoPreLoadGameObjectAsync = CoPreLoadGameObjectAsync
GameObjectPool.GetGameObjectAsync = GetGameObjectAsync
GameObjectPool.CoGetGameObjectAsync = CoGetGameObjectAsync
GameObjectPool.RecycleGameObject = RecycleGameObject
GameObjectPool.Cleanup = Cleanup

return GameObjectPool

3 . AtlasManager.lua

--[[
-- 图集管理:为逻辑层透明化图集路径和图集资源加载等底层操作
-- 注意:
-- 1、只提供异步操作,为的是不需要逻辑层取操心图集AB是否已经加载的问题
-- 2、图集管理器不做资源缓存
-- 3、图片名称带后缀
--]]

local AtlasManager = BaseClass("AtlasManager", Singleton)
local sprite_type = typeof(CS.UnityEngine.Sprite)

-- 从图集异步加载图片:回调方式
local function LoadImageAsync(self, atlas_config, image_name, callback, ...)
	local atlas_path = atlas_config.AtlasPath
	local image_path = atlas_path.."/"..image_name
	
	ResourcesManager:GetInstance():LoadAsync(image_path, sprite_type, function(sprite, ...)
		if callback then
			callback(not IsNull(sprite) and sprite or nil, ...)
		end
	end, ...)
end

-- 从图集异步加载图片:协程方式
local function CoLoadImageAsync(self, atlas_config, image_name, progress_callback)
	local sprite = ResourcesManager:GetInstance():CoLoadAsync(path, sprite_type, progress_callback)
	return not IsNull(sprite) and sprite or nil
end

AtlasManager.LoadImageAsync = LoadImageAsync
AtlasManager.CoLoadImageAsync = CoLoadImageAsync

return AtlasManager

支付宝捐赠

打赏红包

你可能感兴趣的:(Lua)