在编辑器下不打包加载AB包资源

前言

        最近我的项目是在使用AB包来做资源加载。因为某些原因,策划频繁修改AB包的资源。比如某些贴图或是模型进行改动,或是UI位置上的修改。而且修改后,策划就要看效果。我不得不等一段AB包构建的时间(当然可以运行过程中进行更改,但如果有些东西更改太多,我会记不下。毕竟运行中的修改不能作用于我们的资源的)。所以我想能不能不打包也正常加载AB包资源呢?当然这个方法只能在编辑器中使用,毕竟正常情况下AB包本身就是要构建的。

代码展示

        这里我只提供了如何不构建AB包进行加载的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#if UNITY_EDITOR
using AssetBundleBrowser.AssetBundleDataSource;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using AssetBundleDataSource = AssetBundleBrowser.AssetBundleDataSource;

namespace Test
{
[InitializeOnLoad]
public class ABLoadInEditor
{
private static HashSet<string> packetHash = new HashSet<string>();
private static ABDataSource curData;

static ABLoadInEditor()
{
EditorApplication.playModeStateChanged += EnterPlay;
}

private static void EnterPlay(PlayModeStateChange playModeStateChange)
{
if (playModeStateChange == PlayModeStateChange.EnteredPlayMode)
{
var m_DataSourceList = new List<AssetBundleDataSource.ABDataSource>();

foreach (var info in BuildCustomABDataSourceList())
{
m_DataSourceList.AddRange(info.GetMethod("CreateDataSources").Invoke(null, null) as List<AssetBundleDataSource.ABDataSource>);
}

if (m_DataSourceList.Count > 1)
{
// 载入所有包信息
curData = m_DataSourceList[0];
}
else curData = AssetBundleBrowser.AssetBundleModel.Model.DataSource;
packetHash = new HashSet<string>();
foreach (var i in curData.GetAllAssetBundleNames())
{
packetHash.Add(i);
}
}
}

private static List<Type> BuildCustomABDataSourceList()
{
var properList = new List<Type>();
properList.Add(null); //empty spot for "default"
var x = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in x)
{
try
{
var list = new List<Type>(
assembly
.GetTypes()
.Where(t => t != typeof(ABDataSource))
.Where(t => typeof(ABDataSource).IsAssignableFrom(t)));


for (int count = 0; count < list.Count; count++)
{
if (list[count].Name == "AssetDatabaseABDataSource")
properList[0] = list[count];
else if (list[count] != null)
properList.Add(list[count]);
}
}
catch (System.Exception)
{
//assembly which raises exception on the GetTypes() call - ignore it
}
}


return properList;
}

public static T LoadRes<T>(string packetName, string resName) where T : UnityEngine.Object
{
if (packetHash.Contains(packetName))
{
foreach (var i in curData.GetAssetPathsFromAssetBundle(packetName))
{
// 全匹配或者文件名匹配
if (resName.Equals(i) || Path.GetFileNameWithoutExtension(i).Equals(resName))
{
var obj = AssetDatabase.LoadAssetAtPath<T>(i);
if (obj != null)
return obj;
}
}
}
else Debug.LogError($"AB包中不存在{packetName}这个包名");
return null;
}
}
}

#endif

代码说明

        整体的思路就是从编辑器中获取到AB包的打包信息,然后根据这些信息使用AssetDatabase加载。因为思路是获取编辑器中的AB包信息,所以在正式的打包的时候会报找不到类型的错误。所以我这边使用了UNITY_EDITOR宏来做处理,在正常使用的过程中你也需要使用UNITY_EDITOR来加载这个类。

        在加载资源中,你可能会有疑惑为什么我这里要做全匹配或者文件名匹配。这是因为在一个包体中同名且同类型的资源,ab包只会加载第一个用此名称的资源。如果你要完整的显示,那你只能用全名,比如Assets/AB/Cube/Cube.prefab

上述代码的缺点

        其实你可以很明显的感受到,上述代码很不符合实际的项目环境。因为上面的代码全部都包裹在UNITY_EDITOR的判断下。你也需要自己为其做一套资源优化,当然打包后走的就是AB包的资源优化了。

        如果你项目小,其实这些都不算什么大问题。如果你觉得全部代码全部都包裹在UNITY_EDITOR的判断下很离谱,那么你可以通过得到的包信息去写一个自定义的数据结构,然后再本地加载。虽然这样仍然要花费你一些时间,但是会比AB包打包的时间短。

碎碎念

        上面的代码,其实我现在项目已经不用了。我自己项目就是自定义一个数据结构去加载的,但是之前文章已经写了一半。我懒得改了,毕竟改了代码还要加说明。反正想法也就那样,大家自己写一下就好了。