创建自定义资产
Stride 支持创建自定义资产类型,这些资产可以在场景中被引用,也可以引用其他资产。
为此,你必须在游戏的 .csproj 中添加对 Stride.Core.Assets 包的引用:
下面是在默认游戏项目中的样子:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Stride.Engine" Version="4.4.0" />
<PackageReference Include="Stride.Video" Version="4.4.0" />
<PackageReference Include="Stride.Physics" Version="4.4.0" />
<PackageReference Include="Stride.Navigation" Version="4.4.0" />
<PackageReference Include="Stride.Particles" Version="4.4.0" />
<PackageReference Include="Stride.UI" Version="4.4.0" />
<PackageReference Include="Stride.AssetCompiler" Version="4.4.0" IncludeAssets="build;buildTransitive" />
<PackageReference Include="Stride.Core.Assets" Version="4.4.0" />
</ItemGroup>
</Project>
Warning
请确保 TargetFramework 以及所有包的版本与你当前的 Stride 版本一致。上例以 4.4.0 版本作为参考。
在同一个项目中,创建一个新的 C# 文件,并将其内容替换为以下代码:
using System.Collections.Generic;
using System.Threading.Tasks;
using Stride.Core;
using Stride.Core.Assets;
using Stride.Core.Assets.Compiler;
using Stride.Core.BuildEngine;
using Stride.Core.Serialization;
using Stride.Core.Serialization.Contents;
using Stride.Engine;
using Stride.Engine.Design;
namespace YOUR_GAME_NAMESPACE;
/// <summary>
/// 资产的运行时表示,这是你在脚本中实际使用的类
/// </summary>
[DataContract]
[ContentSerializer(typeof(DataContentSerializerWithReuse<YOUR_CLASS>))]
// 当与 ReferenceSerializer<> 一起指定 DataSerializerGlobal 时,需要 ReferenceSerializer 特性,
// 它们两者一起向序列化器说明这个类是一个运行时资产
[ReferenceSerializer, DataSerializerGlobal(typeof(ReferenceSerializer<YOUR_CLASS>), Profile = "Content")]
// 下面这一行确保在实例化预制体时,资产的引用会被复用,而不是为所有新实例克隆
[DataSerializerGlobal(typeof(CloneSerializer<YOUR_CLASS>), Profile = "Clone")]
public class YOUR_CLASS
{
// 将这里替换为你希望该资产在运行时保存的内容
public List<Prefab> PrefabCollection { get; set; } = new();
}
/// <summary>
/// <see cref="YOUR_CLASS"/> 的设计时和文件表示,你可以在这里添加不会包含在构建中的内容
/// </summary>
[AssetDescription(FileExtension, AllowArchetype = false)]
[AssetContentType(typeof(YOUR_CLASS))]
[AssetFormatVersion(nameof(YOUR_GAME_NAMESPACE), CurrentVersion, "1.0.0.0")]
public sealed class YOUR_CLASS_ASSET : Asset
{
private const string CurrentVersion = "1.0.0.0";
public const string FileExtension = ".blks";
// 将这里替换为你希望该资产在 Game Studio 中拥有的内容
public List<Prefab> PrefabCollection { get; set; } = new();
}
/// <summary> 在构建游戏时将 <see cref="YOUR_CLASS_ASSET"/> 转换为 <see cref="YOUR_CLASS"/> 的编译器 </summary>
[AssetCompiler(typeof(YOUR_CLASS_ASSET), typeof(AssetCompilationContext))]
public sealed class YOUR_CLASS_COMPILER : AssetCompilerBase
{
protected override void Prepare(AssetCompilerContext context, AssetItem assetItem, string targetUrlInStorage, AssetCompilerResult result)
{
var asset = (YOUR_CLASS_ASSET)assetItem.Asset;
// 你可以有多个构建步骤,每个步骤运行一个 AssetCommand
result.BuildSteps = new AssetBuildStep(assetItem);
result.BuildSteps.Add(new DESIGN_TO_RUNTIME_COMMAND(targetUrlInStorage, asset, assetItem.Package));
}
public override IEnumerable<ObjectUrl> GetInputFiles(AssetItem assetItem)
{
// 产出必须在此资产构建之前先构建的资产,
// 适用于需要读取某个资产的编译版本来构建此资产的情况。
// 注意,通过此方法创建循环引用会在构建时导致死锁
// (例如:A 是 B 的输入,同时 B 也是 A 的输入)
// 以下仅供参考,在本例中没有用处
var asset = (YOUR_CLASS_ASSET)assetItem.Asset;
foreach (var block in asset.PrefabCollection)
{
var url = AttachedReferenceManager.GetUrl(block);
if (!string.IsNullOrEmpty(url))
{
yield return new ObjectUrl(UrlType.Content, url);
}
}
}
/// <summary>
/// 将设计时资产转换为运行时资产的 <see cref="AssetCommand"/>。
/// </summary>
public class DESIGN_TO_RUNTIME_COMMAND(string url, YOUR_CLASS_ASSET parameters, IAssetFinder assetFinder)
: AssetCommand<YOUR_CLASS_ASSET>(url, parameters, assetFinder)
{
protected override Task<ResultStatus> DoCommandOverride(ICommandContext commandContext)
{
var assetManager = new ContentManager(MicrothreadLocalDatabases.ProviderService);
var runtimeObject = new YOUR_CLASS{ PrefabCollection = Parameters.PrefabCollection };
assetManager.Save(Url, runtimeObject);
commandContext.Logger.Info($"Saving {nameof(YOUR_CLASS)}: {runtimeObject.PrefabCollection}");
return Task.FromResult(ResultStatus.Successful);
}
}
}
Warning
对运行时资产的任何更改都会破坏之前构建的资产数据库。更改类之后,请务必清理解决方案,或手动删除 Stride 为资产生成的构建产物(YOUR_PROJECT.Windows/obj/stride 和 bin/db),以确保下次构建时资产数据库被完整重建。
这样就完成了对该资产类型的支持。你可以在 Assets 目录中创建一个 *.blks 文件并手动填写内容,不过还是通过编辑器来完成更好……
在编辑器中为添加资产菜单添加一个分类
在你的游戏目录中创建一个名为 Templates 的新目录,该目录用于存放你的模板。
在该目录中,以你的新资产命名创建一个带 .sdtpl 扩展名的新文件。

打开该文件并粘贴以下内容
!TemplateAssetFactory
Id: 21CC3354-9F0B-4D1F-8242-62D56454B27C
AssetTypeName: YOUR_CLASS_ASSET
Name: THE NAME IN THE EDITOR
Scope: Asset
Description: A DESCRIPTIVE DESCRIPTION OF YOUR ASSET
Group: WHERE THIS WOULD BE CLASSIFIED UNDER IN THE EDITOR
DefaultOutputName: THE DEFAULT FILE NAME
适当地编辑各个字段,
Id必须唯一!如果你需要,可以使用网上的一些服务来生成,搜索generate guid onlineAssetTypeName必须与继承自Asset的类名一致(可以省略命名空间)
现在你需要编辑 *.sdpkg 以包含这个新模板,只需在 TemplateFolders: 下方添加以下几行即可
TemplateFolders:
- Path: !dir Templates
Group: Assets
Files:
- !file Templates/YOUR_TEMPLATE.sdtpl
下面是将它包含进默认游戏 *.sdpkg 后的样子:
!Package
SerializedVersion: {Assets: 3.1.0.0}
Meta:
Name: MyGame21
Version: 1.0.0
Authors: []
Owners: []
Dependencies: null
AssetFolders:
- Path: !dir Assets
- Path: !dir Effects
ResourceFolders:
- !dir Resources
OutputGroupDirectories: {}
ExplicitFolders: []
Bundles: []
TemplateFolders:
- Path: !dir Templates
Group: Assets
Files:
- !file Templates/YOUR_TEMPLATE.sdtpl
RootAssets: []
至此就大功告成了,祝你玩得开心!
