Stride Community Toolkit 预览版入门:纯代码基础





5.00/5 (5投票s)
探索 Stride Community Toolkit 的纯代码功能,该功能旨在帮助 C#/.NET 开发者使用强大的开源 (FOSS) C# 游戏引擎 Stride 轻松创建沉浸式 2D/3D 游戏和可视化效果。
介绍🌱
欢迎使用 Stride Community Toolkit 的预览版。这是一个为 Stride C# 游戏引擎提供的扩展和助手集合,作为社区驱动的开源项目开发。该工具包使开发者能够使用 Stride 的纯代码方法创建 2D/3D 游戏和可视化效果,而无需使用 Game Studio。
在这篇简短的文章中,我们将探讨 纯代码 功能,我发现它对于 C#/.NET 开发者来说特别有用。我们将使用一个 .NET 8 控制台应用程序,通过添加几个 NuGet 包来创建一个简单的项目。如果您喜欢这篇文章,请务必查看文末链接的更全面的文章,以进行深入了解。
目录
- 引言
- 背景
- 必备组件
- 使用代码
- 步骤 1:创建新的 C# .NET 8 控制台应用程序
- 步骤 2:添加代码
- 步骤 3:构建游戏
- 步骤 4:运行游戏
- 步骤 5:理解代码
- 步骤 6:轮到你了
- 参考文献
- 结论
背景🌄
几年前我偶然发现了 Stride,它引起了我的注意,因为 Stride 使用 .NET 6 和 C# 10,而其他 C#/.NET 游戏引擎则没有。我开始探索它,但不想通过 Game Studio 来学习 Stride。相反,我想运行一个控制台应用程序,并逐步添加功能和逻辑,一步一步地学习,“.NET 的方式”。
当时,纯代码方法不像现在这样可用,所以我开始深入研究引擎,与社区联系,并贡献了一些想法和 PR。快进到今天,有了 Stride Community Toolkit,我可以通过添加常规的 NuGet 包从一个简单的控制台中运行 Stride 游戏引擎。
先决条件🏠
要遵循本教程,需要 C# 和 .NET 的基本知识。
这些先决条件已在干净的 Windows 11 安装上进行了测试。
- 安装 Microsoft Visual C++ 2015-2022 可再发行版 (25MB),并在出现提示时重启系统。
- 安装 .NET 8 SDK x64 (200MB)。
- 安装您选择的 IDE。我将使用 Visual Studio 2022,但您也可以使用 Visual Studio Code、Rider 或任何其他支持 .NET 开发的 IDE。
使用代码
您可以将代码复制并粘贴到您的 Program.cs
文件中,然后运行应用程序以查看结果。
步骤 1:创建新的 C# .NET 8 控制台应用程序
- 在您的 IDE 中创建一个新的 C# .NET 8 控制台应用程序。
- 添加以下 NuGet 包📦
dotnet add package Stride.CommunityToolkit.Windows --prerelease
步骤 2:添加代码
将以下代码粘贴到您的 Program.cs
文件中:💻
using Stride.CommunityToolkit.Engine;
using Stride.CommunityToolkit.Rendering.ProceduralModels;
using Stride.Core.Mathematics;
using Stride.Engine;
using Stride.Games;
using Stride.Input;
using Stride.Physics;
float movementSpeed = 1f;
float force = 3f;
Entity? sphere1 = null;
Entity? sphere2 = null;
// Create an instance of the game
using var game = new Game();
// Start the game loop and provide the Start and Update methods as callbacks
// This method initializes the game, begins running the game loop,
// and starts processing events.
game.Run(start: Start, update: Update);
// Define the Start method to set up the scene
void Start(Scene rootScene)
{
// Add the default graphics compositor to handle rendering
game.AddGraphicsCompositor();
// Add a 3D camera and a controller for basic camera movement
game.Add3DCamera().Add3DCameraController();
// Add a directional light to illuminate the scene
game.AddDirectionalLight();
// Add a 3D ground plane to catch the cone
game.Add3DGround();
// Add a ground gizmo to visualize axis directions
game.AddGroundGizmo(position: new Vector3(-5, 0.1f, -5), showAxisName: true);
// Create a 3D primitive cone and store it in an entity
var entity = game.Create3DPrimitive(PrimitiveModelType.Cone);
// Reposition the cone 8 units above the origin in the scene
entity.Transform.Position = new Vector3(0, 8, 0);
// Add the entity to the root scene so it becomes part of the scene graph
entity.Scene = rootScene;
// Create a sphere with material, disable its collider, and add it to the scene
// The sphere is hanging in the default position Vector(0,0,0) in the air,
// well intersecting the ground plane as it is not aware of the ground
sphere1 = game.Create3DPrimitive(PrimitiveModelType.Sphere, new()
{
Material = game.CreateMaterial(Color.Gold),
IncludeCollider = false // No collider for simple movement
});
sphere1.Scene = rootScene;
// This was added
// Create a second sphere with a collider for physics-based interaction
sphere2 = game.Create3DPrimitive(PrimitiveModelType.Sphere, new()
{
Material = game.CreateMaterial(Color.Orange)
});
sphere2.Transform.Position = new Vector3(-3, 5, 0); // Reposition the sphere above the ground
sphere2.Scene = rootScene;
}
// Define the Update method, called every frame to update the game state
void Update(Scene scene, GameTime time)
{
// Calculate the time elapsed since the last frame for consistent movement
var deltaTime = (float)time.Elapsed.TotalSeconds;
// Handle non-physical movement for sphere1
if (sphere1 != null)
{
// Move the first sphere along the negative X-axis when the Z key is held down
if (game.Input.IsKeyDown(Keys.Z))
{
sphere1.Transform.Position -= new Vector3(movementSpeed * deltaTime, 0, 0);
}
// Move the first sphere along the positive X-axis when the X key is held down
else if (game.Input.IsKeyDown(Keys.X))
{
sphere1.Transform.Position += new Vector3(movementSpeed * deltaTime, 0, 0);
}
}
// Handle physics-based movement for sphere2
if (sphere2 != null)
{
// Retrieve the RigidbodyComponent, which handles physics interactions
var rigidBody = sphere2.Get<RigidbodyComponent>();
// We use KeyPressed instead of KeyDown to apply impulses only once per key press.
// This means the player needs to press and release the key to apply an impulse,
// preventing multiple impulses from being applied while the key is held down.
// Apply an impulse to the left when the C key is pressed (and released)
if (game.Input.IsKeyPressed(Keys.C))
{
rigidBody.ApplyImpulse(new Vector3(-force, 0, 0));
}
// Apply an impulse to the right when the V key is pressed (and released)
else if (game.Input.IsKeyPressed(Keys.V))
{
rigidBody.ApplyImpulse(new Vector3(force, 0, 0));
}
}
}
步骤 3:构建游戏
从命令行构建项目,或使用您的 IDE 来构建它
dotnet build
步骤 4:运行游戏
从您的 IDE 运行应用程序。您应该会看到下面的内容。
步骤 5:理解代码
让我们仔细看看代码。
有了这段代码,我们就可以运行游戏了,但当循环运行时,目前还看不到任何东西。
using Stride.Engine;
// Create an instance of the game
using var game = new Game();
// Start the game loop
// This method initializes the game, begins running the game loop,
// and starts processing events.
game.Run();
我们需要向游戏循环添加基本组件,以便在 3D 空间中显示内容。这些都是包装了基本功能的扩展,但如果这些扩展太有限,您可以实现自己的版本来进一步自定义它们。
我们将向 game.Run()
添加一个 Start()
回调方法,并使用它来使用必要的组件设置场景。
using Stride.CommunityToolkit.Engine; using Stride.Core.Mathematics; using Stride.Engine; // Create an instance of the game using var game = new Game(); // Start the game loop and provide the Start method as a callback // This method initializes the game, begins running the game loop, // and starts processing events. game.Run(start: Start); // Define the Start method to set up the scene void Start(Scene rootScene) { // Add the default graphics compositor to handle rendering game.AddGraphicsCompositor(); // Add a 3D camera and a controller for basic camera movement game.Add3DCamera().Add3DCameraController(); // Add a directional light to illuminate the scene game.AddDirectionalLight(); // Add a 3D ground plane to catch the capsule game.Add3DGround(); // Add a ground gizmo to visualize axis directions game.AddGroundGizmo(position: new Vector3(-5, 0.1f, -5), showAxisName: true); }

rootScene
,否则它们将不会出现在场景中。Start()
方法中初始化并添加 3D 原始对象。// Create a 3D primitive cone and store it in an entity
var entity = game.Create3DPrimitive(PrimitiveModelType.Cone);
// Reposition the cone 8 units above the origin in the scene
entity.Transform.Position = new Vector3(0, 8, 0);
// Add the entity to the root scene so it becomes part of the scene graph
entity.Scene = rootScene;
// Create a sphere with material, disable its collider, and add it to the scene
// The sphere is hanging in the default position Vector(0,0,0) in the air,
// well intersecting the ground plane as it is not aware of the ground
var sphere1 = game.Create3DPrimitive(PrimitiveModelType.Sphere, new()
{
Material = game.CreateMaterial(Color.Gold),
IncludeCollider = false // No collider for simple movement
});
sphere1.Scene = rootScene;
// This was added
// Create a second sphere with a collider for physics-based interaction
var sphere2 = game.Create3DPrimitive(PrimitiveModelType.Sphere, new()
{
Material = game.CreateMaterial(Color.Orange)
});
sphere2.Transform.Position = new Vector3(-3, 5, 0); // Reposition the sphere above the ground
sphere2.Scene = rootScene;
我们添加了三个 3D 原始对象,其中一些带有碰撞体,一些不带。
现在,让我们深入探讨键盘交互和运动。
键盘交互非常简单。 Update()
方法在游戏循环中被调用,我们使用 Input.IsKeyDown()
和 Input.IsKeyPressed()
方法来检测玩家输入,以根据按下的键触发操作。
// Define the Update method, called every frame to update the game state void Update(Scene scene, GameTime time) { // Calculate the time elapsed since the last frame for consistent movement var deltaTime = (float)time.Elapsed.TotalSeconds; // Handle non-physical movement for sphere1 if (sphere1 != null) { // Move the first sphere along the negative X-axis when the Z key is held down if (game.Input.IsKeyDown(Keys.Z)) { sphere1.Transform.Position -= new Vector3(movementSpeed * deltaTime, 0, 0); } // Move the first sphere along the positive X-axis when the X key is held down else if (game.Input.IsKeyDown(Keys.X)) { sphere1.Transform.Position += new Vector3(movementSpeed * deltaTime, 0, 0); } } // Handle physics-based movement for sphere2 if (sphere2 != null) { // Retrieve the RigidbodyComponent, which handles physics interactions var rigidBody = sphere2.Get<RigidbodyComponent>(); // We use KeyPressed instead of KeyDown to apply impulses only once per key press. // This means the player needs to press and release the key to apply an impulse, // preventing multiple impulses from being applied while the key is held down. // Apply an impulse to the left when the C key is pressed (and released) if (game.Input.IsKeyPressed(Keys.C)) { rigidBody.ApplyImpulse(new Vector3(-force, 0, 0)); } // Apply an impulse to the right when the V key is pressed (and released) else if (game.Input.IsKeyPressed(Keys.V)) { rigidBody.ApplyImpulse(new Vector3(force, 0, 0)); } } }
运动说明
我们有两种移动方式:一种用于没有碰撞体的原始对象,另一种用于带有碰撞体的原始对象。
-
没有碰撞体的原始对象:我们通过直接修改它们的
Transform.Position
来移动它们。例如,sphere1
可以在场景中自由移动,但由于它没有碰撞体,它不会与其他实体交互——它只会漂浮在虚空中。 -
带有碰撞体的原始对象:必须使用
RigidbodyComponent
来移动带有碰撞体的原始对象。这就是为什么我们引用RigidbodyComponent
并应用一个冲量来使对象移动,模拟现实世界的物理。
代码本身就相当直观,我已经添加了注释来指导您完成。希望您玩得开心!
步骤 6:轮到你了
我希望您能看到,仅用几行代码就可以实现纯 C# 和 .NET 的游戏开发学习。
编码不仅能增强创造力和解决问题的能力,还能通过可视化将创造力提升到新的水平,无论是游戏、模拟还是仅仅在虚拟空间中试验 3D 模型。
让我们看看您将如何发展 🙂。
参考文献
我引起您的注意了吗?本教程的完整、全面的版本可在 Suchong:Stride Community Toolkit 预览 - C# 中的纯代码功能基础。
或者,如果您对 F# 感兴趣,本文的精简版可在 Suchong:Stride Community Toolkit 预览 - F# 中的纯代码功能基础。是的,您也可以在 F# 中运行它👀!
您问 Visual Basic?👀 嗯,技术上来说,您可以,但这真的不是我的菜。不过,如果您好奇,这里有一个非常简单的例子:Visual Basic 中的带刚体的胶囊体。
结论
在本文中,您学习了如何开始使用 Stride Community Toolkit,重点是其纯代码功能,用于完全通过 C# 和 .NET 开发游戏项目。我们通过设置一个简单的 .NET 8 控制台应用程序、向场景添加实体和运动以及使用键盘实现用户交互进行了讲解。通过采用这种代码优先的方法,您已经看到了在不依赖图形化 Game Studio 的情况下,游戏开发可以多么强大和灵活。
无论您是构建游戏、模拟还是试验 3D 模型,Stride 引擎及其工具包都为开发者开辟了新的可能性。现在轮到您将这些基础技能应用到您自己的创意项目中了。继续试验,继续学习,并不断突破您的创造极限!🚀
历史
- 2024/09/23 初始发布