首先,我不考虑资产经理。用松散定义的术语(例如“ manager”)来思考您的体系结构倾向于使您在脑海中扫清许多细节,因此,解决方案变得更加困难。
关注您的特定需求,这似乎与创建一种资源加载机制有关,该机制抽象了基础的原始存储并支持受支持的类型集的可扩展性。您的问题中没有真正涉及的内容,例如,缓存已加载的资源-很好,因为按照单一职责原则,您可能应该将资产缓存构建为单独的实体并在其他位置聚合两个接口, 作为适当的。
为了解决您的特定问题,您应该设计加载器,使其不进行任何资产本身的加载,而是将这种责任委托给为加载特定类型的资产而定制的接口。例如:
interface ITypeLoader {
object Load (Stream assetStream);
}
您可以创建实现此接口的新类,并定制每个新类以从流中加载特定类型的数据。通过使用流,可以针对与存储无关的公共接口编写类型加载器,而不必进行硬编码就可以从磁盘或数据库中加载。这样甚至可以让您从网络流中加载资产(当您的游戏在控制台上运行,而编辑工具在联网的PC上运行时,这对于实现资产的热重装非常有用)。
您的主要资产加载者需要能够注册和跟踪这些类型特定的加载者:
class AssetLoader {
public void RegisterType (string key, ITypeLoader loader) {
loaders[key] = loader;
}
Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}
此处使用的“键”可以是您喜欢的任何东西,它不必是字符串,但是很容易入手。密钥将决定您希望用户如何识别特定资产,并将用于查找适当的加载程序。因为您想隐藏实现可能正在使用文件系统或数据库的事实,所以您不能让用户通过文件系统路径或类似方式引用资产。
用户应仅使用最少的信息来引用资产。在某些情况下,仅一个文件名就足够了,但是我发现使用类型/名称对通常是可取的,因此一切都非常明确。因此,用户可能将您的动画XML文件之一的命名实例称为"AnimationXml","PlayerWalkCycle"
。
在这里,AnimationXml
将是您注册的密钥AnimationXmlLoader
,它实现了IAssetLoader
。显然,可以PlayerWalkCycle
识别特定资产。给定类型名称和资源名称后,资产加载程序可以查询其持久性存储以获取该资产的原始字节。由于我们将在此处获得最大的通用性,因此您可以通过在创建加载器时为加载器传递一种存储访问方式来实现此目的,从而允许您用以后可以提供流的任何内容替换存储介质:
interface IAssetStreamProvider {
Stream GetStream (string type, string name);
}
class AssetLoader {
public AssetLoader (IAssetStreamProvider streamProvider) {
provider = streamProvider;
}
object LoadAsset (string type, string name) {
var loader = loaders[type];
var stream = provider.GetStream(type, name);
return loader.Load(stream);
}
public void RegisterType (string type, ITypeLoader loader) {
loaders[type] = loader;
}
IAssetStreamProvider provider;
Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}
一个非常简单的流提供程序只需在指定的资产根目录中查找名为的子目录,然后将命名type
文件的原始字节加载name
到流中并返回它。
简而言之,这里的系统是:
- 有一个类知道如何从某种后端存储(磁盘,数据库,网络流等)读取原始字节。
- 有些类知道如何将原始字节流转换为特定类型的资源并将其返回。
- 您实际的“资产加载器”仅包含上述内容,并且知道如何将流提供程序的输出通过管道传输到特定类型的加载器中,从而产生具体的资产。通过公开配置流提供程序和特定于类型的加载程序的方法,您可以拥有一个可由客户端(或您自己)扩展的系统,而无需修改实际的资产加载程序代码。
一些注意事项和最后说明:
上面的代码基本上是C#,但是应该毫不费力地转换为几乎任何语言。为方便起见,我省略了很多东西,例如错误检查或正确使用IDisposable
以及其他可能不适用于其他语言的习惯用法。这些留给读者作为家庭作业。
同样,我object
如上所述返回具体资产,但是如果愿意,您可以使用泛型或模板或任何其他方式生成更具体的对象类型(应该使用,这很不错)。
如上所述,这里根本不处理缓存。但是,您可以轻松添加缓存,并且具有相同的通用性和可配置性。试试看!
有很多方法可以做到这一点,当然也没有一种方法或共识,这就是为什么您找不到一个的原因。我试图提供足够的代码来传达具体的要点,而不会将此答案变成痛苦的长代码墙。它已经很长了。如果您有任何疑问,请随时发表评论或在聊天中找到我。