--[[
-- 资源管理系统:提供资源加载管理
-- 注意:
-- 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