写在最前面,逻辑实现可以最后看,可以先看下面的配置,ab配置和配置表使用这些
逻辑实现
思考:分包加载的话主要是利用了ReourceGroup的概念,如果是其他的框架,就是指定ab包来加载的,简单来说就是对ab包进行分组;
1.一般来说,可以将ab包根据模块划分,比如音频,模型,字体这种;
2.或者是按照独立功能来分一个玩法一个ab包;
我选择,相互结合的方式,将通用的资源提取出来放在一个共用文件夹中,分组名叫Base(Common),这都随便,然后因为我们不同的场景过多,属于不同的模块,把他当成关卡来处理,每一关的都打包到一起,这样就是有多少个关卡就会有多少个分组+通用分组。
PS:有个特殊 情况,因为Unity机制的问题,Scene场景不能和其他资源放一起,所以我把所有的Scene放在一个ab里面,因为Scene主要是一份关联信息表,除非没有拆分的资源,否则他不会打进这个ab,如果不拆分的话,那么相关的资源也会打进这个ab包,这个ab就会很大,一般来说这个scene不会很大,哪怕你有几十个场景。
逻辑实现
核心的修改在于框架里面的这个类ProcedureResourcesUpdate:
可以看到在他的OnEnter里面有这样一段代码
StartUpdateResources(null);
我们在回顾下之前的流程:
如果判断你有资源需要更新那么他就会进入这个下载资源的逻辑,刚开始这个参数是null
跟踪进去能看到这里有俩个同名函数,一个带参数,一个没有参数,如果不传入参数,他就会下载默认的资源组,因为我们一般很少会指定资源组,默认是通过名字/路径来加载使用。我们利用的就是下面的那个方法,传入指定资源组,这样在刚开始的时候我们就让他下载比如通用资源组,然后在进入其他玩法的时候指定资源组下就行了。
逻辑修改如下
StartUpdateResources(“Base”);//Base是我默认的资源组的名字,其他的资源组我为了方便就让他跟玩法的名字保持一致了,这个随便
这样修改之后我们刚开始进入的时候只会下载通用资源了,可以通过HFS和下载的路径来观察是不是只下了我们指定的资源组(可以参考我下面的ab包的分组,使用了其他的插件工具。)
改完这里先测试以为没问题,后来发现校验出了问题,就是procedureResourceCheck那里,他的逻辑如下:
private void OnCheckResourcesComplete(int movedCount, int removedCount, int updateCount, long updateTotalLength, long updateTotalCompressedLength)
{
m_CheckResourcesComplete = true;
m_NeedUpdateResources = updateCount > 0;
m_UpdateResourceCount = updateCount;
m_UpdateResourceTotalCompressedLength = updateTotalCompressedLength;
Log.Info("Check resources complete, '{0}' resources need to update, compressed length is '{1}', uncompressed length is '{2}'.", updateCount.ToString(), updateTotalCompressedLength.ToString(), updateTotalLength.ToString());
}
可以看到他是会验证所有的资源组,我们只需要他验证我们指定的那个默认资源组,代码如下
private void OnCheckResourcesComplete(int movedCount, int removedCount, int updateCount, long updateTotalLength, long updateTotalCompressedLength)
{
IResourceGroup resourceGroup = GameEntry.Resource.GetResourceGroup("Base");
if (resourceGroup == null)
{
Log.Error("has no resource group '{Base}',", "Base");
return;
}
m_CheckResourcesComplete = true;
m_NeedUpdateResources = !resourceGroup.Ready;
m_UpdateResourceCount = resourceGroup.TotalCount - resourceGroup.ReadyCount;
m_UpdateResourceTotalCompressedLength = updateTotalCompressedLength;
Log.Info("Check resources complete, '{0}' resources need to update, unzip length is '{1}'.", m_UpdateResourceCount.ToString(), (resourceGroup.TotalLength - resourceGroup.ReadyLength).ToString());
}
这样他就只会验证默认资源了,然后选择进入到ProcedureResourceUpdate的流程,还是正常的逻辑流程,正常来说第一次之后都是正常的逻辑流程,这个每个人不一样,我这里是加载热更流程,然后切换场景ProcedureChangeScene,我加了一个ProcedureGame的流程,因为是热更工程,这里的状态机需要自己手动创建,流程就是GameHotFixEntry→ProcedurePreload→ProcedureChangeScene <→ProcedureGame
因为场景太多,我也不想每个场景一个流程这样太复杂了,所以所有的玩法只有一个状态,然后根据不同的场景id,我来加载对应的玩法,指定资源组下载.然后又发现了新的问题,因为资源组下载走的是更新,编辑器读取的是编辑器路径下的资源不能直接使用资源组的逻辑,我就让编辑器模式下直接查找资源了,这样编辑器运行不至于报错,否则汇报ResrouceGroup相关的错误,主要修改了ProcedureChangeScene下面的OnEnter里面的逻辑,上面的订阅和关闭逻辑自己看着修改:
#if UNITY_EDITOR
{
sceneAssetIsCompleted = true;
GameEntry.Scene.LoadScene(AssetUtility.GetSceneAsset(drScene.AssetName), Constant.AssetPriority.SceneAsset, this);
}
#else
{
sceneAssetIsCompleted = GetCurResourceGroupIsLocalComplete(drScene.AssetName);
if (!sceneAssetIsCompleted)
{
//下载呗
GameEntry.Resource.UpdateResources(drScene.AssetName, OnUpdateResourcesComplete);
}
else
{
GameEntry.Scene.LoadScene(AssetUtility.GetSceneAsset(drScene.AssetName), Constant.AssetPriority.SceneAsset, this);
}
}
#endif
我这里封装了一个方法,判定这个资源组是不是下载了,如果已经下载了到本地了,那么就直接加载了走LoadScene,否则就在下载成功之后在加载LoadScene,代码如下:
//判定bending资源组是否已经资源完整
private bool GetCurResourceGroupIsLocalComplete(string resourceGourpName)
{
bool flag = false;
List<IResourceGroup> results = new List<IResourceGroup>();
GameEntry.Resource.GetAllResourceGroups(results);
for (int i = 0; i < results.Count; i++)
{
if (results[i].Name == resourceGourpName)
{
if (results[i].Ready)
{
flag = true;
break;
}
}
}
return flag;
}
//根据场景的id获取当前的场景信息
private DRScene GetDrSceneBySceneId(int sceneId)
{
IDataTable<DRScene> dtScene = GameEntry.DataTable.GetDataTable<DRScene>();
DRScene drScene = dtScene.GetDataRow(sceneId);
if (drScene == null)
{
Log.Warning("Can not load scene '{0}' from data table.", sceneId.ToString());
return null;
}
return drScene;
}
//这个是下载成功之后的监听
private void OnUpdateResourcesComplete(IResourceGroup resourceGroup, bool result)
{
if (result)
{
// SetDownFinishState();
DRScene drScene = GetDrSceneBySceneId(m_cuSceneId);
GameEntry.Scene.LoadScene(AssetUtility.GetSceneAsset(drScene.AssetName), Constant.AssetPriority.SceneAsset, this);
Log.Info("OnUpdateResourcesComplete -加载场景---Scene---"+m_cuSceneId+"Update resources complete with no errors.");
}
else
{
Log.Error("OnUpdateResourcesComplete Scene---"+m_cuSceneId+"Update resources complete with errors.");
}
}
最后我在ProcedureGame里面加了事件监听,用的也是GF的Event模块
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
{
base.OnEnter(procedureOwner);
m_ProcedureOwner = procedureOwner;
Game.GameEntry.Event.Subscribe(PlayerEventArgs.EventId,OnPlayerEvent);
}
public virtual void OnPlayerEvent(object sender, GameEventArgs e)
{
var args = e as PlayerEventArgs;
switch (args.EventType)
{
case PlayerEventType.Event_ReplaceScene:
{
int sceneId = (int)args.EventData;
ReplaceScene(sceneId);
}
break;
case PlayerEventType.Event_GetCurSceneId:
{
int sceneId = (int)args.EventData;
ReplaceScene(sceneId);
}
break;
}
}
private void ReplaceScene(int sceneId)
{
DRScene drScene = GetDrSceneBySceneId(sceneId);
Debug.Log("当前场景是==="+drScene.Id+"--名称是--"+drScene.AssetName);
m_ProcedureOwner.SetData<VarInt32>("NextSceneId", sceneId);
ChangeState<ProcedureChangeScene>(m_ProcedureOwner);
}
这些完成基本就实现了GF框架上的分包逻辑实现,也就是我们说的按需加载。也实现了包体缩减的修改。这里只说实现的逻辑,具体ab这里可能一下子说不了很清楚。
当然如果我们想整包加载怎么办,需要吧代码改回去吗?
当然不用,这个时候我们修改ab包的资源分组就好,比如所有的ab分组改成Base,这样就是整包下载了。
AB包处理
GF本身有自己的ab插件,不过需要自己去配置表里面改,我觉得不是很方便就用了大佬的插件。用法参考我之前的那个GameFrameWork打包文章就好了,这里就看下截图,看看资源组的命名就好了。
依赖的处理
之前查资料准备自己写一个依赖查找的工具,后来发现GF自带一个查依赖的工具,我就使用这个工具+自己检查,做了一个大概的分类,这个我已场景为单位,然后把每个场景用到的资源放到自己的ab包文件夹里面,零散的和共用的都先放到公用的文件夹了,这个文件多的时候,左下角可以输入名字过滤,比如点击一个场景过去会看到下面的图:
配置表的处理
加载场景的使用目前主要用Scene.txt和DefaultScene.txt,这个就是吧自己的场景和信息添加进去,代碼调用的时候注意自己的路径要对应的修改(配置参考starforce就可以了。)
工具使用
1.GF自带工具的Resource Analyzer,查看依赖,帮助ab包分包,移动的话需要自己手动
2.插件ResourceRuleEditor ,进行ab包资源组分组
3.插件DataTableGenerator,datatable插件生成。
4.ResourceBuilder进行ab包的打包。
ps:欢迎大家进q群交流游戏开发的问题(632313288)