Stride 的 dotnet new 模板体系:从 CLI 使用到模板包定制开发

  • 游戏开发
  • 图形学
  • VR

【免费下载链接】stride

Stride (formerly Xenko), a free and open-source cross-platform C# game engine.

项目地址: https://gitcode.com/gh_mirrors/st/stride
点击查看 免费下载

Stride(原 Xenko)在仓库中把游戏模板、类型化起步器、功能演示样例与资源包统一封装为 NuGet 包,并接入 dotnet new 模板引擎,使 GameStudio 的"New Project"对话框与命令行 CLI 共用同一条模板流水线。本文以 sources/templates/README.md 为核心脉络,结合模板预处理器的源码实现,系统讲解终端用户如何用 dotnet new 创建游戏、开发者在仓库内如何打包与迭代模板、以及如何为 Stride 新增一个模板样例或资源包。

一、四个模板包:一份生态,两条消费入口

Stride 的 dotnet new 模板由四个 NuGet 包共同组成,每个包对应不同的模板类型与分发渠道:

包包含模板分发方式
Stride.Templates.Gamesstride-game(空白新游戏起步器)、stride-library(共享代码库)随 GameStudio 安装包内置
Stride.Templates.Games.Startersstride-fps、stride-platformer2d、stride-topdownrpg、stride-thirdpersonplatformer、stride-vrsandboxnuget.org(CLI 安装 / 未来模板商店)
Stride.Templates.Samples18 个功能演示(教程、游戏、图形、物理、UI、粒子、输入、音频)nuget.org(CLI 安装 / 未来模板商店)
Stride.Templates.AssetPacks4 个资源包条目模板:stride-pack-buildingblocks、stride-pack-animatedmodels、stride-pack-materials、stride-pack-particlesnuget.org(由 GameStudio 新建游戏对话框 / stride CLI 按需下载)

关键设计是:GameStudio 的"New Project"对话框与 CLI dotnet new 消费的是同一批包,只有一条模板流水线,而不是两套。这得益于引擎侧与 CLI 侧共享的解析逻辑——GameStudio 端由 DotNetNewTemplateBridge.cs 负责,它通过 ContentTemplateResolver.cs 解析四个包(Games 包跟随引擎版本,内容类包跟随内容版本),安装到编辑器隔离的 TemplateEngine profile,并把每个模板注册为 TemplateDotNetNewDescription 交给 TemplateManager。

二、终端用户视角:用 dotnet new 创建 Stride 项目

1. 创建空白游戏

最基础的用法是安装 Games 包并实例化 stride-game:

dotnet new install Stride.Templates.Games
dotnet new stride-game -n MyGame
cd MyGame
dotnet run --project MyGame.Windows

2. 类型化游戏起步器与功能演示

类型化起步器和功能演示需要先安装各自的包:

# 有明确游戏类型的起步器(FPS、平台跳跃、俯视角 RPG、第三人称、VR)
dotnet new install Stride.Templates.Games.Starters
dotnet new stride-fps -n MyShooter
cd MyShooter && dotnet run --project MyShooter.Windows

# 功能演示 / 教程(18 个模板:stride-csharp-beginner / -intermediate、
# stride-jumpyjet、stride-spaceescape、stride-animatedmodel、stride-particles、
# stride-bepuphysics、stride-physics、stride-ui-menu 等)
dotnet new install Stride.Templates.Samples
dotnet new stride-jumpyjet -n MyJumpyJet
cd MyJumpyJet && dotnet run --project MyJumpyJet.Windows

3. 资源包:向已有游戏追加内容

资源包是 item 模板,作用是把现成的资源"投递"到已有的游戏库目录中(与 New Game 对话框中以复选框形式展示的内容一致):

dotnet new install Stride.Templates.AssetPacks
cd MyGame/MyGame.Game
dotnet new stride-pack-buildingblocks   # 将包的 Assets/ + Resources/ 合并进项目

stride CLI 则无需手动安装:它会解析目标 Stride 版本对应的模板包,并在首次需要时自动下载资源包,因此在游戏库目录内执行 stride new stride-pack-buildingblocks 效果相同。

安装完成后,dotnet new -l 可以列出所有可用的 stride-* 短名称;dotnet new <template> --help 则显示每个模板接受的具体参数。

4. 通用参数

模板参数随模板而异,以下是常见参数:

参数取值含义
-n / --name字符串项目名;实例化时会替换内容中所有的 MyTemplate 字面量
--platformshost / windows / linux / macos / ios / android(多平台用 \| 连接)要包含的各平台执行项目;host 自动检测当前操作系统
--HDRtrue / falseHDR 渲染管线(要求 graphicsProfile >= 10.0)
--graphicsProfile9.0 / 10.0 / 11.0着色器功能级别
--orientationDefault / LandscapeLeft / LandscapeRight / Portrait移动端显示方向

这些参数在模板生成器中均有对应的 template.json 符号实现,详见后文源码剖析。

三、本地开发:打包、部署与端到端验证

1. 构建 Games 包并自动部署到本地源

构建 Stride.Templates.Games 会在 bin/packages/ 产出 .nupkg,并自动部署到 %LocalAppData%\stride\nugetdev,这样 GameStudio bridge 在下次启动编辑器时就能直接取用:

dotnet build sources/templates/Stride.Templates.Games/Stride.Templates.Games.csproj

2. 内容类包的"默认不打包"策略

三个内容类包(Starters、Samples、AssetPacks)不会被普通构建打包:GameStudio 与 CLI 使用的是引擎所声明的已发布内容版本(见"示例版本管理"一节)。当你在编辑样例时,可以显式将其打开;打开后包会带上工作树后缀(如 4.4.0-beta7-dev2),从而在本 checkout 中覆盖已发布内容。关闭该标志重新构建即可移除它:

dotnet build sources/templates/Stride.Templates.Samples -p:StridePackContentTemplates=true   # 或在 build/Stride.Local.props 中设置 StridePackContentTemplates
dotnet msbuild build/Stride.Samples.build -t:CleanContentTemplates                            # 不重新构建直接移除开发包

从 Stride.Templates.Common.targets 的实现可以看到,StridePackContentTemplates 默认值在 CI 环境(CI / GITHUB_ACTIONS / TF_BUILD 任一为真)下自动为 true,本地为 false;关闭状态下包仍可被构建(NoTargets 项目本就无内容可编译),并且仍会以精确的已提交版本响应 GameStudio 的 nuspec 依赖查询。这样设计的原因是:内容包体积大、内容极少变化,按自己的节奏发布(release-samples.yml)。

3. 注册到全局 dotnet new 注册表(可选)

希望在每次构建后自动把新构建的 .nupkg 注册到全局 dotnet new 注册表(便于用 CLI 迭代模板内容),可以打开 StrideInstallTemplate:

dotnet build sources/templates/Stride.Templates.Games/Stride.Templates.Games.csproj -p:StrideInstallTemplate=true

该开关默认关闭,因为模板注册表是全局按用户生效的——每次构建都自动安装会覆盖其他 Stride checkout/worktree 的模板并打扰用户的 CLI 工作流。若希望跨构建持久生效,可以在 checkout 根目录放置一个被 gitignore 的 Directory.Build.user.props:

<Project>
  <PropertyGroup>
    <StrideInstallTemplate>true</StrideInstallTemplate>
  </PropertyGroup>
</Project>

4. 端到端冒烟测试

打包 → dotnet new install → 实例化 → dotnet restore 的完整链路由测试项目覆盖:

dotnet test sources/tools/Stride.Templates.Tests

5. CI 发布打包

正式发布版会生成带版本号、经过完整预处理的 .nupkg(含资源裁剪与最终 schema 迁移):

dotnet pack sources/templates/Stride.Templates.Games -p:StridePackageBuild=true

这里 StridePackageBuild=true 会把模板打包模式切换为 preprocessed(完整预处理);本地默认的 minimal 模式会跳过 M6 资源裁剪步骤(--skip-prune)。Common.targets 中还内置了 CI 保护:StridePackageBuild=true 时若打包模式不是 preprocessed 会直接报错,确保绝不发布未经预处理的 nupkg。

四、示例版本管理:devN 与内容版本

仓库内的样例以干净的发布版本提交,但本地通常只有 -devN 开发包存在,因此需要在编辑前后切换引用版本:

  • SamplesToDevEngine:把样例 csproj 中所有 Stride.* 引用改写为本 checkout 开发构建的版本(真实编辑);
  • SamplesToReleaseEngine:改回干净的发布版本,供提交前使用;
  • PrepareSamplesForRelease:将样例升级到当前引擎并打包。
dotnet msbuild build/Stride.Samples.build -t:SamplesToDevEngine                          # 编辑/构建前(例如在 GameStudio 中)
dotnet msbuild build/Stride.Samples.build -t:SamplesToReleaseEngine                      # 提交前
dotnet msbuild build/Stride.Samples.build -t:PrepareSamplesForRelease                    # 升级样例到当前引擎并打包

Stride.Templates.Games 跟随引擎版本;而 Stride.Templates.Samples、.Games.Starters、.AssetPacks 是内容版本:StrideSamplesVersion(定义于 StrideSamplesVersion.props,当前提交值为 4.4.1)是引擎实际引用的已发布内容版本。该文件被 Stride.Templates.Common.targets(打包侧)与 build/Stride.Samples.build(样例发布侧)共同导入,并被 Stride.Assets 以资源形式嵌入——这样一次样例发布只需要重新构建 Stride.Assets,而不必重建所有程序集。样例的修改按普通变更合并,随后由 release-samples.yml 以新的内容版本发布并提交该版本。更完整的版本机制(引擎版本、-devN、样例发布顺序、解析器)见 docs/build/versioning.md。

五、添加一个新的模板样例

1. 编写样例

在 samples/<Category>/<SampleName>/<SampleName>/ 编写样例,遵循标准 Stride 样例布局:.Game/<SampleName>.Game.csproj + .Game/<SampleName>.Game.sdpkg,可选的 .Windows/<SampleName>.Windows.csproj(及其他平台执行目录)、Assets/、Resources/。样例必须能在自身目录下(先执行 SamplesToDevEngine)干净地 dotnet build——预处理器只负责暂存和转换,不修复损坏的输入。

2. 放置 .sdtpl 元数据文件

在 samples/<Category>/<SampleName>/<SampleName>/<SampleName>.sdtpl 放置模板元数据:

!TemplateSample
Id: <new-guid>
Name: "Sample game: MyCoolSample"
Scope: Session
Description: A short blurb for the dialog row.
FullDescription: Multi-line elaboration shown in the dialog detail pane.
Group: Samples/Games
Icon: .sdtpl/icon.png
DefaultOutputName: MyCoolSample
Screenshots:
    - .sdtpl/screenshot_small.jpg
Parameters:
    - HDR
    - graphicsProfile
    - orientation

其中 Parameters 声明该模板选择启用的可选参数(由预处理器发射到 template.json 中);Icon 与 Screenshots 相对于 .sdtpl 文件解析,指向样例目录之外的路径(如 ../../.sdtpl/shared.png)会在打包时被自动复制到每个模板的内容中。

3. 挂接到包中

在对应的 Stride.Templates.*.csproj 中添加 <StrideSampleTemplate> 项:

<StrideSampleTemplate Include="stride-mycoolsample">
  <SamplePath>$(StrideRoot)samples/Category/MyCoolSample/MyCoolSample</SamplePath>
</StrideSampleTemplate>

Include 值即 dotnet new 短名称。按内容类型选包:类型化游戏起步器进 Stride.Templates.Games.Starters,功能演示进 Stride.Templates.Samples。仓库中的实际例子可见 Stride.Templates.Samples.csproj(18 个样例)与 Stride.Templates.Games.Starters.csproj(5 个起步器)。此外,每个包的 csproj 都要为对应的样例目录补充 UpToDateCheckInput 字面通配符,否则 VS 的快速最新检查(CPS 只读取静态展开的输入)会跳过 Build → 不触发 Pack → nupkg 过期。

4. 构建包并检查转换结果

预处理器会完成以下转换(详见下一节源码剖析):GUID 重写(样例内部 GUID → template.json 占位符,引擎原型 Id 保留)、ProjectReference 依赖折叠(共享资源包内联为 Assets//Resources/)、sourceName 重命名(MyCoolSample → MyTemplate → 实例化时的 -n 值)、.sln 合成(带平台条件项目段)、template.json 发射。打包前可检查 obj/template-content/stride-mycoolsample/ 目录确认转换结果。

六、添加一个新的资源包

资源包走的是精简流水线:只暂存 Assets/ + Resources/,打包为 dotnet new item 模板(无 sourceName 重命名、无 GUID 占位、无资源裁剪)。在包根目录放置一个 !TemplateAssetPack 类型的 .sdtpl(可参考 samples/Templates/Packs/PrototypingBlocks):

!TemplateAssetPack
Id: <new-guid>
Name: "My pack"
Description: A short blurb shown next to the checkbox.
Group: AssetPacks

然后在 Stride.Templates.AssetPacks.csproj 中添加 <StrideSampleTemplate> 条目。GameStudio 的 New Game 对话框会把 AssetPacks 包的每个 item 模板都列为一个复选框(.sdtpl 的 Parameters 中包含 assetPacks 的模板会进入该区域;目前是 stride-game),并把用户选中的每个资源包实例化到新生成的游戏库中。现有四个资源包的定义见 Stride.Templates.AssetPacks.csproj。

七、模板生成器源码剖析

1. 两个子命令

Program.cs 是发布流水线工具,提供两个子命令:

  • preprocess-template:把原始模板输入(一个样例目录)转换为 dotnet new 模板引擎可消费的目录。命令行选项包括 --input-path、--output-path、--template-name(dotnet new 短名称)、--source-name(要重命名为 MyTemplate 的原始字面名,缺省时从首个 .sdpkg 的 Name 字段自动检测)、--engine-version(把引擎版本戳进产出的 csproj:$EngineVersion$ 字面量 + 引擎族 Stride.* PackageReference 的具体版本)、--skip-prune(跳过资源裁剪)、--platform-template-path(参考模板,通常是 NewGame,其 MyTemplate.{Linux,macOS,iOS,Android,Windows} 目录会被复制进缺少对应平台执行项目的输出中)。
  • aggregate-sdtpls:把每个被包裹模板的 .sdtpl 拼接为一个多文档 YAML 文件,输出到包根目录的 templates.sdtpls。GameStudio bridge 读取这一个文件即可填充 New-Project 对话框,无需加载每个模板的预处理内容。

2. 预处理流水线

TemplatePreprocessor.cs 的 Run 方法内联记录了完整步骤,核心环节如下:

  1. 解析 .sdtpl:驱动 template.json 元数据(Name、Description、identity 等)与参数选择;若为 !TemplateAssetPack 则走精简的 RunAssetPack 路径。
  2. 暂存(Stage):递归镜像输入 → 输出,并剔除 .sdtpl 元数据文件;bin/obj/.vs 目录不进入模板(避免膨胀与 Windows MAX_PATH 问题)。
  3. 依赖折叠(Dep collapse):把指向暂存目录之外的 ProjectReference(如共享资源包)的 Assets/、Resources/ 内联进暂存树并删除该引用;模板内部引用(如 MyTemplate.Windows → MyTemplate)保留。
  4. 资源裁剪(Prune):以 .sdpkg 的 RootAssets 与固定根类型(GameSettingsAsset、EffectShader、EffectLibrary、EffectCompositorAsset、ScriptSourceFileAsset)为根,对 *.sd* 文件中 <guid>:<location> 形式的资产引用做 BFS 可达性分析,删除不可达资产。对依赖折叠后的样例模板,通常能裁剪掉 80% 以上的内联包资源。该扫描是纯文本的(不加载 PackageSession),因此毫秒级完成且不依赖引擎程序集。
  5. sourceName 重命名:把样例字面名(如 CSharpBeginner)按词边界替换为 MyTemplate,覆盖文件内容、文件名与目录名;App/AppDelegate/Activity 后缀通过前瞻保留(如 CSharpBeginnerApp.cs → MyTemplateApp.cs)。
  6. 平台注入:从 --platform-template-path 复制样例缺失的平台执行项目目录;仅对含有 MyTemplate.Game/ 的"可运行游戏"类模板生效,纯库模板(stride-library)不会获得平台项目。
  7. 合成 .slnx:样例通常不包含解决方案文件,生成器为暂存树内每个 csproj 生成 <Project> 条目,平台执行项目用 #if (XActive) 条件包裹,供 template.json 的 SpecialCustomOperations 在实例化时剥离未选平台;优先级最高的保留平台(Windows > Linux > macOS > Android > iOS)获得 DefaultStartup="true"——因为 .slnx 不像传统 .sln 按文件顺序确定启动项目。
  8. 相机脚本注入:把 BasicCameraController 组件以 !MyTemplate.BasicCameraController,MyTemplate.Game 占位形式注入 MainScene*.sdscene 的 Camera 实体(HDR/LDR 双变体都会处理),组件 Id 使用固定 GUID 以便后续占位符化,保证每次实例化都生成全新 GUID。
  9. 方向占位符注入(仅当模板选择 orientation 参数):把 GameSettings 的 DisplayOrientation、Android 活动的 ScreenOrientation.Landscape、iOS Info.plist 的 UIInterfaceOrientationLandscapeRight 分别改写为 STRIDE_ORIENTATION、STRIDE_ANDROID_ORIENTATION、STRIDE_IOS_ORIENTATION 占位符。源脚手架保留真实可构建的值,只有暂存副本被令牌化。
  10. GUID 扫描与占位:先建立"本地声明的 Id 集合"(.sd* 文件中所有 Id: 行,含子资产 Id),只对集合内的 GUID 做占位(00000000-0000-0000-0000-xxxxxxxxxxxx);引擎原型 Id、相机槽 Id 等外部引用保持原样,避免实例化后引用悬空。三个 Microsoft 预定义项目类型 GUID 也在保留集中。
  11. template.json 发射:为每个 GUID 占位符生成一个 generated/guid 符号;发射 platforms 多选(host 通过 env:OS/env:OSTYPE/env:HOME 绑定与 computed 布尔链解析为各平台 *Active)、HDR/LDR 选择、graphicsProfile 选择、orientation 选择(Android/iOS 用 switch 生成器映射到平台原生枚举),以及 sources/modifiers 按 *Active 条件排除平台目录、按 EffectiveHDR 选择 HDR/LDR 场景变体(LDR 变体通过 rename 恢复为标准文件名)。同时发射 dotnetcli.host.json,把 GameStudio 程序化设置的 updateOnly、skipSolution 参数标记为 UI 不可见。值得注意的还有 compactName 名称形式(去空白、数字前加点转下划线、再走引擎的 safe_namespace),解决带空格项目名导致 .slnx/ProjectReference/sdpkg 路径不一致的问题。
  12. 引擎版本戳印:替换 $EngineVersion$ 字面量,并把引擎族 Stride.* PackageReference 的具体版本改写为打包时的引擎版本(社区前缀如 Stride.Community、Stride.Mono. 等除外),保证打包内容无需任何 Stride 侧改写钩子即可被包括纯 dotnet new 在内的所有消费者直接实例化。
  13. 换行符归一化:.sln/.slnx → CRLF(与 VS/dotnet 编辑行为一致,避免首次保存时的"行尾不一致"提示),其余文本 → LF;二进制文件(.dds/.png/.fbx/.wav 等)不受影响,确保 nupkg 与构建主机无关地字节一致。

3. 共享 MSBuild 逻辑

Stride.Templates.Common.targets 被四个包的 csproj 共同导入,承担版本推导(Games 跟随 StrideNuGetVersion,内容类包跟随 StrideSamplesVersion,-p:PackageVersion= 显式覆盖)、工作树 -devN 后缀追加、preprocess-template 与 aggregate-sdtpls 的 Exec 调用、自动打包部署(Stride.AutoPackDeploy.targets)、CI 保护、以及 StrideInstallTemplate 可选目标。它还会通过一个 Private=false 的 Stride.Core ProjectReference 在 nuspec 中打入"最低引擎版本"标记依赖——CLI 正是读取该元数据来选择兼容模板的。GameStudio 会话侧由 DotNetNewTemplateGenerator.cs 完成实例化分发与各平台执行项目的会话注册。

八、未来工作方向

README 末尾列出了几项明确的演进方向,均围绕当前单条模板流水线的短板:

  • Windows 一键 CLI 注册:在 Linux/macOS 上手动 dotnet new install 是标准路径(没有编辑器可集成);Windows 上 GameStudio 已内置 Stride.Templates.Games.<version>.nupkg,未来可提供一个设置开关替用户执行安装。采用 opt-in 是为了尊重用户意图、避免 4.4 与 4.5 GameStudio 并存时的多版本冲突。GameStudio 自身的 New-Project 对话框不受影响——它安装到隔离的 TemplateEngine profile,而非全局 dotnet new 注册表。
  • 资源包改为引用包:目前资源包是用户持有的拷贝(复制进项目);未来可借助资产 URL 命名空间把资源包变成真正的 NuGet 资产包并作为 PackageReference 引用,资产随版本更新、第三方可发布自己的包。对话框 UX 不变,复选框动作从"复制"改为"引用",item 模板在无需迁移的情况下逐步退出(已创建的游戏保留其复制的资产)。
  • 模板商店 UI:在 New-Project 对话框内浏览 nuget.org 上的 Stride.Templates.Games.Starters / Stride.Templates.Samples,按需安装而无需手动 CLI。
  • templates.sdtpls 的 HTTP Range 拉取:仅加载聚合元数据(Name/Description/Icon/Screenshots),再决定是否下载数 MB 的 nupkg,让商店 UI 无需全量下载即可展示丰富的预览卡片。
  • 游戏开发
  • 图形学
  • VR

【免费下载链接】stride

Stride (formerly Xenko), a free and open-source cross-platform C# game engine.

项目地址: https://gitcode.com/gh_mirrors/st/stride
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值