[游戏开发][Unity]Assetbundle打包篇(2)打包资源配置篇

这篇具有很好参考价值的文章主要介绍了[游戏开发][Unity]Assetbundle打包篇(2)打包资源配置篇。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

打包与资源加载框架目录

正文

可视化配置的方式有很多种,Json、XML、以及Unity内置的ScriptableObject序列化

配置文件里要有哪些内容呢,很显然,最重要的就是目标文件路径,其次是权重类型,权重类型有:必要打包型、被引用打包型、忽略类型。为何会有忽略类型呢,是因为我们设置目标文件路径是个文件夹,同文件夹内可能有不想被打包的文件夹,因此,我们需要另开一条配置,把该子文件夹也设置进去,并且权重类型设置为:忽略类型即可。被引用打包型比较好理解,虽然该资源在目标文件路径中,如果最终统计该资源被引用的次数等于0,说明该资源不需要进包。

我选择的是使用 ScriptableObject,其可视化内容可以参考下图。

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

我安装了Odin Inspector插件所以Inspector面板是可以直接操作的,有需要自行安装

Unity2020以后不用装Odin,已经可以在Unity上编辑了。

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档
unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

CollectionSetting顾名思义就是收集配置,请注意还有一些比较特殊的配置


PackRule设置

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

PackRule看三个变量就知道是对文件夹是否收集的操作

  1. Collect必须收集该文件夹

  1. Ignore必须忽略该文件夹

  1. Passive 该文件夹被引用时收集


LabelRule

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

这里要重点强调一下这个规则的作用,我们测试项目中,所有的资源都在一个根目录下Assets/Works/Resource,看我时如何运用LabelRule规则对根目录下的子文件夹实现控制,如下图所示

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

首先,根目录Assets/Works/Resource设置为LabelByFilePath,意思是说,该目录下的每一个子文件夹内文件,都要单独打成一个AB包,那疑问就来了,每个文件一个AB包,肯定太碎了。

所以就有了第二步操作,第二个操作就是设置根目录的子文件夹类型Assets/Works/Resource/Sprite/BuffIcon 的LabelRule为LabelByFolderPath,意思是以文件夹路径打包。子路径的LabelRule可以覆盖根路径的LabelRule

还有一个重点提示的内容就是,有些文件我们想以文件夹打包,但是!部分子文件太大了,比如好几十M,因此还有个特殊选项LabelByFolderPathExceptBig,意思是,该文件夹还是打成一个整包,但是过大的文件,会根据自己的路径生成一个单独的AB包。

具体代码请看下面的第三个方法


EncryptRule加密规则

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档
unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

加密规则的作用是,AB包打出来后,使用我们规定的加密方式,对AB包数据进行处理,从而让外部的人无法查看包内容。这些加密方式都是自己定的,没有特定的规则

Quick模式:往AB包二进制数据前插一段数据,等将来读取的时候再把插入部分删掉。插入部分你可以自己定义,就算塞入一个数,也可以起到加密作用,只不过别人还是有办法破解,我的Quick模式是用该AB包的Hash生成了一个二进制key插进去,加载AB包时用Hash再算出来这个key,把这个key删掉读包。


BundlePos设置

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

buildin模式启动时要热更进APP,ingame模式比较特殊,边玩边下载。


CollectionSettingData脚本是用来管理使用CollectionSetting配置的脚本,该CollectionSettingData提供了4个基本的方法供搜集资源时使用。

下面是CollectionSettingData的全部代码,部分代码在下面单独讲解


using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;


public static class CollectionSettingData
{
public static CollectionSetting Setting;
private static long bigSizeMB = 1024;

static CollectionSettingData()
{
    // 加载配置文件
    Setting = AssetDatabase.LoadAssetAtPath<CollectionSetting>(EditorDefine.CollectorSettingFilePath);
    if (Setting == null)
    {
        Debug.LogWarning($"Create new CollectionSetting.asset : {EditorDefine.CollectorSettingFilePath}");
        Setting = ScriptableObject.CreateInstance<CollectionSetting>();
        EditorTools.CreateFileDirectory(EditorDefine.CollectorSettingFilePath);
        AssetDatabase.CreateAsset(Setting, EditorDefine.CollectorSettingFilePath);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }
    else
    {
        Debug.Log("Load CollectionSetting.asset ok");
    }
}

/// <summary>
/// 存储文件
/// </summary>
public static void SaveFile()
{
    if (Setting != null)
    {
        EditorUtility.SetDirty(Setting);
        AssetDatabase.SaveAssets();
    }
}

/// <summary>
/// 添加元素
/// </summary>
public static void AddElement(string folderPath)
{
    if (IsContainsElement(folderPath) == false)
    {
        CollectionSetting.Wrapper element = new CollectionSetting.Wrapper();
        element.FolderPath = folderPath;
        Setting.Elements.Add(element);
        SaveFile();
    }
}

/// <summary>
/// 移除元素
/// </summary>
public static void RemoveElement(string folderPath)
{
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        if (Setting.Elements[i].FolderPath == folderPath)
        {
            Setting.Elements.RemoveAt(i);
            break;
        }
    }
    SaveFile();
}

public static void AddI18NDir(string folderPath)
{
    if(!Setting.I18nDirectories.Contains(folderPath))
    {
        Setting.I18nDirectories.Add(folderPath);
        SaveFile();
    }
}
public static void RemoveI18NDir(string folderPath)
{
    for (int i = 0; i < Setting.I18nDirectories.Count; i++)
    {
        if (Setting.I18nDirectories[i] == folderPath)
        {
            Setting.I18nDirectories.RemoveAt(i);
            break;
        }
    }
    SaveFile();
}

public static void AddInGameDir(string folderPath)
{
    if (!Setting.InGames.Contains(folderPath))
    {
        Setting.InGames.Add(folderPath);
        SaveFile();
    }
}
public static void RemoveInGameDir(string folderPath)
{
    for (int i = 0; i < Setting.InGames.Count; i++)
    {
        if (Setting.InGames[i] == folderPath)
        {
            Setting.InGames.RemoveAt(i);
            break;
        }
    }
    SaveFile();
}

/// <summary>
/// 编辑元素
/// </summary>
public static void ModifyElement(string folderPath, CollectionSetting.EFolderPackRule packRule, CollectionSetting.EBundleLabelRule labelRule, EEncryptMethod encryptMethod, EAssetDeliveryMode deliveryMode, EBundlePos bundlePos)
{
    // 注意:这里强制修改忽略文件夹的命名规则为None
    if (packRule == CollectionSetting.EFolderPackRule.Ignore)
        labelRule = CollectionSetting.EBundleLabelRule.None;
    else if (labelRule == CollectionSetting.EBundleLabelRule.None)
        labelRule = CollectionSetting.EBundleLabelRule.LabelByFilePath;

    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        if (Setting.Elements[i].FolderPath == folderPath)
        {
            Setting.Elements[i].PackRule = packRule;
            Setting.Elements[i].LabelRule = labelRule;
            //Setting.Elements[i].EncryptRule = encryptMethod;
            //Setting.Elements[i].DeliveryMode = deliveryMode;
            Setting.Elements[i].BundlePos = bundlePos;
            break;
        }
    }
    SaveFile();
}

/// <summary>
/// 是否包含元素
/// </summary>
public static bool IsContainsElement(string folderPath)
{
    for (int i = 0; i < Setting.Elements.Count; i++)
        if (Setting.Elements[i].FolderPath == folderPath) return true;

    return false;
}

public static CollectionSetting.Wrapper GetElement(string folderPath)
{
    for (int i = 0; i < Setting.Elements.Count; i++)
        if (Setting.Elements[i].FolderPath == folderPath) return Setting.Elements[i];

    return null;
}

/// <summary>
/// 获取所有的打包路径
/// </summary>
public static List<string> GetAllCollectPath()
{
    List<string> result = new List<string>();
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = Setting.Elements[i];
        if (wrapper.PackRule == CollectionSetting.EFolderPackRule.Collect)
            result.Add(wrapper.FolderPath);
    }
    return result;
}

/// <summary>
/// 是否收集该资源
/// </summary>
public static bool IsCollectAsset(string assetPath)
{
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = Setting.Elements[i];
        if (wrapper.PackRule == CollectionSetting.EFolderPackRule.Collect && assetPath.StartsWith(wrapper.FolderPath))
            return true;
    }

    return false;
}

/// <summary>
/// 是否忽略该资源
/// </summary>
public static bool IsIgnoreAsset(string assetPath)
{
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = Setting.Elements[i];
        if (wrapper.PackRule == CollectionSetting.EFolderPackRule.Ignore && assetPath.StartsWith(wrapper.FolderPath))
            return true;
    }

    //XML
    //List<string> ignoreList = ConfigParser.GetIgnoreResList();
    //foreach (string name in ignoreList)
    //    if (assetPath.Contains(name)) return true;

    return false;
}


private static bool IsSubPath(string folderA, string assetPath)
{
    if(assetPath.StartsWith(folderA))
        return assetPath.Replace(folderA, "").StartsWith("/");

    return false;
}

public static EEncryptMethod GetEncryptRule(string assetPath)
{
    // 注意:一个资源有可能被多个规则覆盖
    List<CollectionSetting.Wrapper> filterWrappers = new List<CollectionSetting.Wrapper>();
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = Setting.Elements[i];
        if (IsSubPath(wrapper.FolderPath, assetPath))
            filterWrappers.Add(wrapper);
    }

    // 我们使用路径最深层的规则
    CollectionSetting.Wrapper findWrapper = null;
    for (int i = 0; i < filterWrappers.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = filterWrappers[i];
        if (findWrapper == null)
        {
            findWrapper = wrapper;
            continue;
        }
        if (wrapper.FolderPath.Length > findWrapper.FolderPath.Length)
            findWrapper = wrapper;
    }

    // 如果没有找到命名规则
    if (findWrapper == null) return EEncryptMethod.None;

    return findWrapper.EncryptRule;
}
/// <summary>
/// 获取资源的打包标签
/// </summary>
public static string GetAssetBundleLabel(string assetPath)
{
    // 注意:一个资源有可能被多个规则覆盖
    List<CollectionSetting.Wrapper> filterWrappers = new List<CollectionSetting.Wrapper>();
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = Setting.Elements[i];
        if (IsSubPath(wrapper.FolderPath, assetPath))
            filterWrappers.Add(wrapper);
    }

    // 我们使用路径最深层的规则
    CollectionSetting.Wrapper findWrapper = null;
    for (int i = 0; i < filterWrappers.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = filterWrappers[i];
        if (findWrapper == null)
        {
            findWrapper = wrapper;
            continue;
        }
        if (wrapper.FolderPath.Length > findWrapper.FolderPath.Length)
            findWrapper = wrapper;
    }

    // 如果没有找到命名规则
    if (findWrapper == null) return assetPath.Remove(assetPath.LastIndexOf("."));
    
    string labelName = "";
    // 根据规则设置获取标签名称
    if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.None)
    {
        // 注意:如果依赖资源来自于忽略文件夹,那么会触发这个异常
        throw new Exception($"CollectionSetting has depend asset in ignore folder : {findWrapper.FolderPath}, asset : {assetPath}");
        // MotionLog.Log(ELogLevel.Log, $"CollectionSetting has depend asset in ignore folder : {findWrapper.FolderPath}, asset : {assetPath}");
        // labelName = assetPath.Remove(assetPath.LastIndexOf("."));
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFileName)
    {
        labelName = Path.GetFileNameWithoutExtension(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "test"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFilePath)
    {
        labelName = assetPath.Remove(assetPath.LastIndexOf(".")); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config\test"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFolderName)
    {
        string temp = Path.GetDirectoryName(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config"
        labelName = Path.GetFileName(temp); // "C:\Demo\Assets\Config" --> "Config"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFolderPath)
    {
        labelName = Path.GetDirectoryName(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFolderPathExceptBig)
    {
        long sizeMB = EditorTools.GetFileSize(assetPath) / 1024;
        if (sizeMB < bigSizeMB)
            labelName = Path.GetDirectoryName(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config"
        else
            labelName = assetPath.Remove(assetPath.LastIndexOf(".")); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config\test"
    }
    else if(findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByRootFolderPath)
    {
        labelName = findWrapper.FolderPath;
    }
    else
    {
        throw new NotImplementedException($"{findWrapper.LabelRule}");
    }

    ApplyReplaceRules(ref labelName);

    return labelName.Replace("\\", "/");
}

/// <summary>
/// 获取资源的打包位置
/// </summary>
public static EBundlePos GetAssetBundlePos(string assetPath)
{
    // if (assetPath.Contains("Assets/WorksArt/Model/Live2D")
    //      || assetPath.Contains("Assets/Works/Resource/Audio/live2D")
    //      || assetPath.Contains("Assets/Works/Resource/Model/Live2D"))
    // {
    //     return EBundlePos.ingame;
    // }
    List<string> buildInList = ConfigParser.GetBuildInResList();

    foreach(string path in Setting.InGames)
    {
        if (assetPath.Contains(path))
        {
            bool match = false;
            foreach (string name in buildInList)
            {
                if (assetPath.Contains(name))
                {
                    match = true;
                    break;
                }
            }
            if (match)
                break;
            else
                return EBundlePos.ingame;
        }
    }

    // 注意:一个资源有可能被多个规则覆盖
    List<CollectionSetting.Wrapper> filterWrappers = new List<CollectionSetting.Wrapper>();
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = Setting.Elements[i];
        if(IsSubPath(wrapper.FolderPath, assetPath))
        {
            filterWrappers.Add(wrapper);
        }
    }

    // 我们使用路径最深层的规则
    CollectionSetting.Wrapper findWrapper = null;
    for (int i = 0; i < filterWrappers.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = filterWrappers[i];
        if (findWrapper == null)
        {
            findWrapper = wrapper;
            continue;
        }
        if (wrapper.FolderPath.Length > findWrapper.FolderPath.Length)
            findWrapper = wrapper;
    }

    // 如果没有找到命名规则
    if (findWrapper == null)
    {
        return EBundlePos.buildin;
    }
    
    return findWrapper.BundlePos;
}

public static void ApplyReplaceRules(ref string name)
{
    if (name.Contains("TempLuaCode"))
    {
        name = name.Replace("TempLuaCode", "Lua");
    }
    if (name.Contains("Resource_min"))
    {
        name = name.Replace("Resource_min", "Resource");
    }
}

}

2:获取所有资源配置信息,是一个很重要的接口,代码在上面也有


    public static List<string> GetAllCollectPath()
        {
            List<string> result = new List<string>();
            for (int i = 0; i < Setting.Elements.Count; i++)
            {
                CollectionSetting.Wrapper wrapper = Setting.Elements[i];
                if (wrapper.PackRule == CollectionSetting.EFolderPackRule.Collect)
                    result.Add(wrapper.FolderPath);
            }
            return result;
        }

3:获取该资源目录的AssetbundleLabel,不知道AssetbundleLabel的小伙伴,看下图

unity 打包策略,开发心得,项目管理,unity,游戏引擎,Powered by 金山文档

/// <summary>
/// 获取资源的打包标签
/// </summary>
public static string GetAssetBundleLabel(string assetPath)
{
    // 注意:一个资源有可能被多个规则覆盖
    List<CollectionSetting.Wrapper> filterWrappers = new List<CollectionSetting.Wrapper>();
    for (int i = 0; i < Setting.Elements.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = Setting.Elements[i];
        if (IsSubPath(wrapper.FolderPath, assetPath))
            filterWrappers.Add(wrapper);
    }

    // 我们使用路径最深层的规则
    CollectionSetting.Wrapper findWrapper = null;
    for (int i = 0; i < filterWrappers.Count; i++)
    {
        CollectionSetting.Wrapper wrapper = filterWrappers[i];
        if (findWrapper == null)
        {
            findWrapper = wrapper;
            continue;
        }
        if (wrapper.FolderPath.Length > findWrapper.FolderPath.Length)
            findWrapper = wrapper;
    }

    // 如果没有找到命名规则
    if (findWrapper == null) return assetPath.Remove(assetPath.LastIndexOf("."));
    
    string labelName = "";
    // 根据规则设置获取标签名称
    if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.None)
    {
        // 注意:如果依赖资源来自于忽略文件夹,那么会触发这个异常
        throw new Exception($"CollectionSetting has depend asset in ignore folder : {findWrapper.FolderPath}, asset : {assetPath}");
        // MotionLog.Log(ELogLevel.Log, $"CollectionSetting has depend asset in ignore folder : {findWrapper.FolderPath}, asset : {assetPath}");
        // labelName = assetPath.Remove(assetPath.LastIndexOf("."));
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFileName)
    {
        labelName = Path.GetFileNameWithoutExtension(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "test"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFilePath)
    {
        labelName = assetPath.Remove(assetPath.LastIndexOf(".")); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config\test"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFolderName)
    {
        string temp = Path.GetDirectoryName(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config"
        labelName = Path.GetFileName(temp); // "C:\Demo\Assets\Config" --> "Config"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFolderPath)
    {
        labelName = Path.GetDirectoryName(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config"
    }
    else if (findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByFolderPathExceptBig)
    {
        long sizeMB = EditorTools.GetFileSize(assetPath) / 1024;
        if (sizeMB < bigSizeMB)
            labelName = Path.GetDirectoryName(assetPath); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config"
        else
            labelName = assetPath.Remove(assetPath.LastIndexOf(".")); // "C:\Demo\Assets\Config\test.txt" --> "C:\Demo\Assets\Config\test"
    }
    else if(findWrapper.LabelRule == CollectionSetting.EBundleLabelRule.LabelByRootFolderPath)
    {
        labelName = findWrapper.FolderPath;
    }
    else
    {
        throw new NotImplementedException($"{findWrapper.LabelRule}");
    }

    ApplyReplaceRules(ref labelName);

    return labelName.Replace("\\", "/");
}

4:获取该资源的BundlePos,BundlePos是什么,前文已经介绍了文章来源地址https://www.toymoban.com/news/detail-703361.html


public static EBundlePos GetAssetBundlePos(string assetPath)
{
// if (assetPath.Contains("Assets/WorksArt/Model/Live2D")
//      || assetPath.Contains("Assets/Works/Resource/Audio/live2D")
//      || assetPath.Contains("Assets/Works/Resource/Model/Live2D"))
// {
//     return EBundlePos.ingame;
// }
List<string> buildInList = ConfigParser.GetBuildInResList();

foreach(string path in Setting.InGames)
{
    if (assetPath.Contains(path))
    {
        bool match = false;
        foreach (string name in buildInList)
        {
            if (assetPath.Contains(name))
            {
                match = true;
                break;
            }
        }
        if (match)
            break;
        else
            return EBundlePos.ingame;
    }
}

// 注意:一个资源有可能被多个规则覆盖
List<CollectionSetting.Wrapper> filterWrappers = new List<CollectionSetting.Wrapper>();
for (int i = 0; i < Setting.Elements.Count; i++)
{
    CollectionSetting.Wrapper wrapper = Setting.Elements[i];
    if(IsSubPath(wrapper.FolderPath, assetPath))
    {
        filterWrappers.Add(wrapper);
    }
}

// 我们使用路径最深层的规则
CollectionSetting.Wrapper findWrapper = null;
for (int i = 0; i < filterWrappers.Count; i++)
{
    CollectionSetting.Wrapper wrapper = filterWrappers[i];
    if (findWrapper == null)
    {
        findWrapper = wrapper;
        continue;
    }
    if (wrapper.FolderPath.Length > findWrapper.FolderPath.Length)
        findWrapper = wrapper;
}

// 如果没有找到命名规则
if (findWrapper == null)
{
    return EBundlePos.buildin;
}

return findWrapper.BundlePos;
}

到了这里,关于[游戏开发][Unity]Assetbundle打包篇(2)打包资源配置篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包