First-Person Character (Bepu)
A first-person character built entirely from code - no Game Studio scene - on a Bepu CharacterComponent, with boxes to walk into and jump onto. Two details make it work. The scene uses a camera without the setup's controller, because the fly-around debug camera would fight the controller for the same camera. And the controller is a component plus a processor pair, registered automatically by DefaultEntityComponentProcessor, which is how you add per-frame behaviour to many entities without a script on each one.
The Program.cs file shows how to:
- Building a character controller with Bepu CharacterComponent
- Attaching a collider by passing the component through BepuPhysicsOptions
- Why the setup's camera has no controller when the scene brings its own
- Pairing an EntityComponent with an EntityProcessor
- Auto-registering a processor with [DefaultEntityComponentProcessor]
- Driving a physics body from the camera entity
- Using helpers: SceneSetup3D, UseSkybox, CreatePrimitive

View on GitHub.
using E05_3D_FirstPersonCharacter;
using Stride.BepuPhysics;
using Stride.BepuPhysics.Definitions.Colliders;
using Stride.CommunityToolkit.Bepu;
using Stride.CommunityToolkit.Engine;
using Stride.CommunityToolkit.Rendering.ProceduralModels;
using Stride.CommunityToolkit.Rendering.Overlay;
using Stride.CommunityToolkit.Skyboxes;
using Stride.Core.Mathematics;
using Stride.Engine;
// A code-only first-person character controller on Bepu physics:
// • a CharacterComponent capsule built entirely from code (no Game Studio),
// • driven by a custom EntityComponent + EntityProcessor pair that auto-registers itself
// via [DefaultEntityComponentProcessor] - see FirstPersonControllerComponent.cs.
CharacterComponent? character = null;
using var game = new Game();
// The setup's camera is deliberately without WithController(): the fly-around debug camera
// controller would fight our first-person controller for the same camera.
var sceneSetup = new SceneSetup3D
{
Camera = CameraSetup.Perspective()
}.UseBepu().UseSkybox();
game.Run(sceneSetup, start: Start);
void Start(Scene scene)
{
// A few boxes to walk into and jump onto.
for (int i = 0; i < 5; i++)
{
var box = game.CreatePrimitive(Primitive3D.Cube(), new() { Position = new Vector3(3 + i * 2.5f, 0.5f + i * 0.4f, -4) });
box.Scene = scene;
}
// The character body: a Bepu CharacterComponent riding a capsule primitive. Passing the
// component through BepuPhysicsOptions makes CreatePrimitive attach a matching capsule
// collider to it.
character = new CharacterComponent
{
Speed = 6f,
JumpForce = 8f,
Collider = new CompoundCollider(),
Gravity = true,
};
var body = game.CreatePrimitive(Primitive3D.Capsule(), new BepuPhysicsOptions { Component = character, Position = new Vector3(0, 2f, 0) });
body.Scene = scene;
// The controller lives on the CAMERA entity and drives the body. Adding the component is all
// it takes - [DefaultEntityComponentProcessor] spins up FirstPersonControllerProcessor
// automatically.
game.GetCameraEntity().Add(new FirstPersonControllerComponent { Character = character });
// The keys, then the live state; the callback runs every frame the overlay is drawn
TextOverlay.GetOrCreate(game).AddSection("Character", () =>
[
new("W A S D", "Move", Color.Gold),
new("Space", "Jump", Color.Gold),
new("Shift", "Hold to sprint", Color.Gold),
new("V", "Toggle fly / noclip", Color.Gold),
new("Escape", "Release the mouse", Color.Gold),
new("Left click", "Grab the mouse again", Color.Gold),
new(""),
new(character?.IsGrounded == true ? "Grounded" : "In the air", Color.LightGreen),
]);
}