Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / game

Getting Started with Stride Community Toolkit Preview: Code-Only Basics

5.00/5 (5 votes)
24 Sep 2024MIT5 min read 5K  
Explore the Stride Community Toolkit's code-only feature, designed to help C#/.NET developers easily create immersive 2D/3D games and visualizations using Stride, a powerful open-source (FOSS) C# game engine.
This article serves as a quick-start guide for C#/.NET developers looking to explore game development with the Stride Community Toolkit. Using a code-only approach, the article walks you through creating a simple project in a .NET 8 Console App by adding basic entities, implementing keyboard interaction, and applying physics, all without needing the Stride Game Studio. By the end, you'll understand how to build a basic game project with Stride's powerful engine and tools, all while working within a familiar C#/.NET environment.

Introduction🌱

Welcome to the preview of the Stride Community Toolkit a collection of extensions and helpers for the Stride C# game engine, developed as a community-driven, open-source project. This toolkit enables developers to create 2D/3D games and visualizations using Stride's code-only approach, bypassing the need for the Game Studio.

In this short post, we’ll explore the code-only feature, which I found particularly useful as a C#/.NET developer. We'll use a .NET 8 Console App to create a simple project by adding a few NuGet packages. If you enjoy this post, be sure to check out the more comprehensive article linked at the end for a deeper dive.

Table of contents:

  • Introduction
  • Background
  • Prerequisites
  • Using the code
  • Step 1: Create a New C# .NET 8 Console App
  • Step 2: Add Code
  • Step 3: Build the Game
  • Step 4: Run the Game
  • Step 5: Understanding the Code
  • Step 6: Your Turn
  • References
  • Conclusion

Background 🌄

I came across Stride a few years ago, and it caught my attention because Stride was using .NET 6 and C# 10, while other C#/.NET game engines weren't. I began exploring it but didn't want to follow the usual route through the Game Studio to learn Stride. Instead, I wanted to run a console application and gradually add features and logic, learning step by step, the ".NET way."

At that time, the code-only approach wasn't as available as it is now, so I started digging into the engine, connecting with the community, and contributing some ideas and PRs. Fast forward to today, with the Stride Community Toolkit, I can run the Stride Game Engine from a simple console by adding regular NuGet packages.

Prerequisites 🏠

Basic knowledge of C# and .NET is required to follow along with this tutorial.

These prerequisites were tested on a clean Windows 11 installation.

  1. Install the Microsoft Visual C++ 2015-2022 Redistributable (25MB) and restart your system if prompted.
  2. Install the .NET 8 SDK x64 (200MB).
  3. Install the IDE of your choice. I will be using Visual Studio 2022, but you can also use Visual Studio Code, Rider, or any other IDE that supports .NET development.

Using the code

You can copy and paste the code into your Program.cs file and run the application to see the results.

Step 1: Create a New C# .NET 8 Console App

  1. Create a new C# .NET 8 Console App in your IDE.
  2. Add the following NuGet package 📦
BAT
dotnet add package Stride.CommunityToolkit.Windows --prerelease

Step 2: Add Code

Paste the following code into your Program.cs file: 💻

C#
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(080);
 
    // 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, 50);  // 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, 00);
        }
        // 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, 00);
        }
    }
 
    // 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, 00));
        }
        // 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, 00));
        }
    }
}

Step 3: Build the Game

Build the project from the command line or use your IDE to build it:

BAT
dotnet build

Step 4: Run the Game

Run the application from your IDE. You should see this below.

Image 1

Step 5: Understanding the Code

Let’s take a closer look at the code.

With just this code, we can run the game, but while the loop is running, there’s nothing to see yet.

C#
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();

Image 2

We need to add basic components to the game loop so that something appears in the 3D space. These are all extensions that wrap basic functionality, but you can further customize them by implementing your own versions if the extensions are too limiting.

We’ll add a Start() callback method to game.Run() and use it to set up the scene with the necessary components.

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);
}
Great! Now we have an empty space with a ground plane. 🤷‍♂️
 
Image 3
 
Next, let's add some 3D primitives, which will be wrapped in entities. Remember to assign the rootScene to each entity, or they won’t appear in the scene.
 
This part of the code initializes and adds 3D primitives in the Start() method.
C#
// 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(080);
 
// 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, 50);  // Reposition the sphere above the ground
sphere2.Scene = rootScene;    

Image 4

We’ve added three 3D primitives, some with colliders and some without.

Now, let's dive into keyboard interaction and motion.

The keyboard interaction is straightforward. The Update() method is called within the game loop, and we detect player input using the Input.IsKeyDown() and Input.IsKeyPressed() methods to trigger actions based on the keys being pressed.

// 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, 00);
        }
        // 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, 00);
        }
    }
 
    // 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, 00));
        }
        // 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, 00));
        }
    }
}

Motion Explanation

We have two types of movement: one for primitives without a collider and one for primitives with a collider.

  • Primitives Without a Collider: We move these by directly modifying their Transform.Position. For example, sphere1 can move freely in the scene, but since it lacks a collider, it won’t interact with other entities—it will just float in the void.

  • Primitives With a Collider: Primitives with colliders must be moved using the RigidbodyComponent. That’s why we reference the RigidbodyComponent and apply an impulse to make the object move, simulating real-world physics.

The code is fairly self-explanatory, and I've added comments to guide you through it. I hope you've had fun exploring these features!

Step 6: Your Turn

I hope you can see that learning game development with pure C# and .NET is achievable with just a few lines of code.

Coding not only enhances creativity and problem-solving but also takes that creativity to the next level through visualization, whether it's in games, simulations, or simply experimenting with 3D models in a virtual space.

Let's see where you take it 🙂.

References

Did I catch your attention? The full, comprehensive version of this tutorial is available here: Stride Community Toolkit Preview - Code-Only Feature Basics in C#.

Image 5

Alternatively, if you're interested in F#, the condensed version of this article can be found here: Stride Community Toolkit Preview - Code-Only Feature Basics in F#. Yes, you can run this in F# 👀 too!

Visual Basic, you ask? 👀 Well, technically, you can, but that's not really my cup of tea. However, if you're curious, here's a super simple example: Capsule with rigid body in Visual Basic.

Conclusion

In this article, you've learned how to get started with the Stride Community Toolkit, focusing on its code-only feature for developing game projects purely through C# and .NET. We walked through setting up a simple .NET 8 Console App, adding entities and motion to the scene, and implementing user interaction with the keyboard. By embracing this code-first approach, you've seen how powerful and flexible game development can be without relying on a graphical Game Studio.

Whether you're building games, simulations, or experimenting with 3D models, the Stride engine and its toolkit open up new possibilities for developers. Now it’s your turn to take these foundational skills and apply them to your own creative projects. Keep experimenting, keep learning, and continue pushing the boundaries of what you can create! 🚀

History

- 23/09/2024 Initial release

License

This article, along with any associated source code and files, is licensed under The MIT License