GameFrameWork+删除指定的(分包)ab资源(玩法/场景)

GameFrameWork+删除指定的(分包)ab资源

这个要结合我之前的GameFrameWork中的分包功能的实现一起看。如果没看,先去看这篇

前言

项目的前提是已经完成了GF框架的分包下载的功能,因为玩法过多,可能有十几个玩法,如果用户要删除的话,如何在不改变整体流程的同时可以完成删除,也就是新的玩法可能是预制的,也可能是全部走下载,或者部分走下载,总之在这个或者多个玩法被删除的情况下,还能通过下载下来进入该玩法。

在这里插入图片描述

拆解需求

1.删除的话,首先要找到路径,找到路径了才可以删除;

2.删除了下次点进来的时候,还能再下载到本地。

实现

仔细看了下GF现在的框架是不存在这样的功能,需要先看下他现在的流程,删除的功能实现比较简单,只需要确定路径,删除就可以了,问题主要在于如何下次还能重新删除。

在这里插入图片描述

1.0版本

除了通用的共有资源我放在了一个ab里面,其他的把每个玩法拆成了单独的ab,所以我删除的时候直接根据他的持久化路径+ab包的名字就可以了,核心代码如下

string path = Application.persistentDataPath + "/"+abName+".dat";
Debug.Log("当前的路径是-----------------"+path);
if (File.Exists(path))
{
    File.Delete(path);
    Debug.Log("删除文件--"+path);
}

2.0版本

结果:这样做的话删除了无法在运行的时候下载,只能退出重进才进,因为他每次进来的时候有个校验流程,这个时候他才会发现这个资源是需要下载的,不然你这个资源组如果已经在本地下载好了,你即使这样删除他也认为这个资源组ab无需下载,因为在ProcedureResourcesCheck中我们下载的时候是根据资源组是否已经准备好了,这个逻辑如下:

1.启动的时候他会获取所有的资源组;

foreach (UpdatableVersionList.Resource resource in resources)
{
	...
	defaultResourceGroup.AddResource(resourceName, resource.Length, resource.CompressedLength);
//在这个循环里面获取所有的资源组;
}
                      

2.当我们指定下载资源组的时候他会改变对应资源组的状态,由!Ready → Ready

GameEntry.Resource.UpdateResources(drScene.AssetName, OnUpdateResourcesComplete);
//这是加载分包的逻辑
											↓
m_ResourceUpdater.UpdateResources(resourceGroup);if (string.IsNullOrEmpty(resourceGroup.Name))
{
  foreach (KeyValuePair<ResourceName, UpdateInfo> updateInfo in m_UpdateCandidateInfo)
  {
      m_UpdateWaitingInfo.Add(updateInfo.Value);
  }
}
else
{
  resourceGroup.InternalGetResourceNames(m_CachedResourceNames);
  foreach (ResourceName resourceName in m_CachedResourceNames)
  {
      UpdateInfo updateInfo = null;
      if (!m_UpdateCandidateInfo.TryGetValue(resourceName, out updateInfo))
      {
          continue;
      }

      m_UpdateWaitingInfo.Add(updateInfo);
  }
//这一段是不管资源组是不是为空,都会加到m_UpdateWaitingInfo数组中
//然后我们会看到update有这样的逻辑...
while (m_ApplyWaitingInfo.Count > 0)
{
    ApplyInfo applyInfo = m_ApplyWaitingInfo.Dequeue();
    if (ApplyResource(applyInfo))
    {
        return;
    }
...

if (m_UpdateWaitingInfo.Count > 0)
{
    int freeCount = m_DownloadManager.FreeAgentCount - m_DownloadManager.WaitingTaskCount;
    if (freeCount > 0)
    {
        for (int i = 0, count = 0; i < m_UpdateWaitingInfo.Count && count < freeCount; i++)
        {
            if (DownloadResource(m_UpdateWaitingInfo[i]))
            {
...
//大概意思就是先看看资源组有没有下好,如果下好了,就直接应用,如果没有,那就说明要下载,那么就去下载private bool DownloadResource(UpdateInfo updateInfo)
{
    if (updateInfo.Downloading)
    {
        return false;
    }

    updateInfo.Downloading = true;
    string resourceFullNameWithCrc32 = updateInfo.ResourceName.Variant != null ? Utility.Text.Format("{0}.{1}.{2:x8}.{3}", updateInfo.ResourceName.Name, updateInfo.ResourceName.Variant, updateInfo.HashCode, DefaultExtension) : Utility.Text.Format("{0}.{1:x8}.{2}", updateInfo.ResourceName.Name, updateInfo.HashCode, DefaultExtension);
    Debug.Log("资源组"+updateInfo.ResourceName+"下载地址是----"+updateInfo.ResourcePath);
    m_DownloadManager.AddDownload(updateInfo.ResourcePath, Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_UpdatePrefixUri, resourceFullNameWithCrc32)), updateInfo);
    return true;
}//然后她在ApplyResource()和OnDownloadSuccess之后都有个改变状态的过程,标记资源下载完成,后面会修改到这里
m_ResourceManager.m_ResourceInfos[updateInfo.ResourceName].MarkReady()
//所以我们在修改的时候需要修改这个状态

3.改写方法

/// <summary>
/// 标记资源准备完毕。
/// </summary>
public void MarkReady(bool isReady = true)
{
    // m_Ready = true;
    m_Ready = isReady;
}

综上,修改如下:

在这里插入图片描述

1.当我想修改的时候,发现这个类是个内部的密封类,本着修改最小的原则,我准备获取这个ResourceManager,然后通过他来修改状态变化;

在这里插入图片描述

2.通过ResourceComponenetExtension来获取这个ResruourceManager,从而修改ready的状态;

public static class ResourceComponentExtension
{
        public static void ChangeResourceGroupReadyStateByResourceName(this ResourceComponent resource, string resourceName,
            bool isReady = false)
        {
            var resourceManager = resource.GetResourceManager();
            resourceManager.ChangeResourceGroupReadyState(resourceName,isReady);
}

3.同时需要在IResourceManager中添加对应的接口:

/// <summary>
/// 改变当前资源组的状态
/// </summary>
/// <param name="resourceGroupName"></param>
/// <param name="isReady"></param>
public void ChangeResourceGroupReadyState(string resourceGroupName, bool isReady);

4.ResourceManager中的具体实现:

public void ChangeResourceGroupReadyState(string resourceGroupName,bool isReady = false)
{
    foreach (var item in m_ResourceInfos)
    {
        if (item.Key.Name == resourceGroupName)
        {
            m_ResourceInfos[item.Key].MarkReady(isReady);
            m_ReadWriteResourceInfos.Remove(item.Key);
            OnCheckerResourceNeedUpdate(item.Key,item.Value.FileSystemName,item.Value.LoadType,item.Value.Length,item.Value.HashCode,item.Value.CompressedLength,m_oldCompressedHashCodeDic[item.Key]);
            Debug.Log("当前的资源组==="+resourceGroupName+"-----state-----is---"+isReady);
            break;
        }
    }
}
//m_ReadWriteResourceInfos注意这里
这个在ResourceManager.ResourceUpdater下面的ApplyResource()DownloadSuccess()里面都把需要更新的资源组加到了这个列表里面,我们在删除的同事,要把他们对应的从列表里面拿出来,这样下次就会重新加,或者你自己加个判断,因为其他也用到了这个列表我选择移除
//m_ResourceManager.m_ReadWriteResourceInfos.Add(updateInfo.ResourceName, new ReadWriteResourceInfo(updateInfo.FileSystemName, updateInfo.LoadType, updateInfo.Length, updateInfo.HashCode));

//OnCheckerResourceNeedUpdate()为什么使用这个方法,因为正常更新的时候就是需要这个方法,这个就是吧这个资源组加到更新列表中
//m_UpdateCandidateInfo 他有一个候选更新的列表

/// <summary>
  /// 增加资源更新。
  /// </summary>
  /// <param name="resourceName">资源名称。</param>
  /// <param name="fileSystemName">资源所在的文件系统名称。</param>
  /// <param name="loadType">资源加载方式。</param>
  /// <param name="length">资源大小。</param>
  /// <param name="hashCode">资源哈希值。</param>
  /// <param name="compressedLength">压缩后大小。</param>
  /// <param name="compressedHashCode">压缩后哈希值。</param>
  /// <param name="resourcePath">资源路径。</param>
  public void AddResourceUpdate(ResourceName resourceName, string fileSystemName, LoadType loadType, int length, int hashCode, int compressedLength, int compressedHashCode, string resourcePath)
  {
      m_UpdateCandidateInfo.Add(resourceName, new UpdateInfo(resourceName, fileSystemName, loadType, length, hashCode, compressedLength, compressedHashCode, resourcePath));
  }

在这里插入图片描述

在这里插入图片描述

然后从上面俩张图就能看出来,第一张是把他加到可更新的列表里面,告诉内存他还没有下载下来,然后等更新的时候传入这个要更新的资源组,他就会去这个列表里面取他的对应的信息来下载;

5.最后,要说下我加的一个字典,因为我们使用了这个标记方法

OnCheckerResourceNeedUpdate(item.Key,item.Value.FileSystemName,item.Value.LoadType,item.Value.Length,item.Value.HashCode,item.Value.CompressedLength,m_oldCompressedHashCodeDic[item.Key]);
//m_oldCompressedHashCodeDic这个是用来记录之前所有的资源组的compressedHashCode的值的因为这个循环的item是resource

在这里插入图片描述
在这里插入图片描述

可以看到RerouceInfo里面没有这个字段,所以我需要记录下,然后每次进来都会要走一遍Check;

在这里插入图片描述
在这里插入图片描述

6.测试:逻辑部分已经结束测试逻辑如下

public void deleteSpecifyAbByName(string abName)
{
   
    bool flag =  GetCurResourceGroupIsLocalComplete(abName);
    string path1 = Application.persistentDataPath + "/"+abName+".dat";
    Debug.Log("资源组当前路径是----------------------"+path1);
    if (flag)
    {
        Debug.Log("本地有该资源组");
        ChangeResourceGroupStatus(abName);   
        string path = Application.persistentDataPath + "/"+abName+".dat";
        Debug.Log("当前的路径是-----------------"+path);
        if (File.Exists(path))
        {
            File.Delete(path);
            Debug.Log("删除文件--"+path);
        }
    }
    else
    {
        Debug.Log("本地没有该资源组");
    }
}
//判定本地资源组是否已经资源完整
  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;
  }

//可以直接在Update中添加按键事件 这个根据自己的需求来,或者是按钮点击,就会看到自己的持久化路径下面的资源组已经删除
//必须不知道资源组在哪,我加了打印

7.PC端和安卓端删除均已验证,不管删除场景包不包括当前场景,均显示正常。。

3.0版本

上面基本已经满足了业务需要,但是还存在一种情况,不过我觉得已经没有必要了,就是玩家在玩的同时,自己删除了app的缓存数据,或者去了对应的文件夹删了对应的ab包,这时候会有问题,为什么?

因为,我们手动删除的时候给对应的资源组做了标记,并且也将他们移除了对应的m_ReadWriteResourceInfos列表,但是你如果物理删除的话,这些标记就没有变化,就会可能崩溃或者黑屏,引发不可控的问题,但是如果你只要删掉进程重新进,又会正常,因为这时候会检测本地的文件,所以都会正常。所以,我觉得问题不大,毕竟这是非常规操作。但是,如果你非要改,有没有办法?

我试了一半,如果非要规避这样的操作,那么就需要在分包逻辑里面不仅判断资源组是否完全,还要判断本地是否有该文件,然后你需要吧通用的ab和当前的ab都判定一遍,如果没有需要把他们标记为!Ready,移除对应的列表,并且要加载通过的ab和当前要加入的ab,我觉得没有必要,所以就不加了。但我本地已经验证过pc端的逻辑,暂未发现异常。

最后,所以2.0版本基本已经可以实现删除指定的玩法(分包资源),不管你是单个删除还是批量删除。

ps:欢迎大家进q群交流游戏开发的问题(632313288)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值