feat: WASD free-fly camera with F toggle

- Add ICameraController interface and FreeFlyCameraController.
- WASD moves, Q/E up/down, Shift sprint, right-mouse + mouse look.
- Toggle between Orbit and FreeFly with F key.
- OrbitCameraController now implements ICameraController.
- Update Program.cs to switch active controller on F.
- Update CORTEX_ENGINE_ARCHITECTURE.md with camera controls.
- Debug/Release/ReleaseAOT all build.
This commit is contained in:
emil28092005
2026-06-16 21:14:32 +03:00
parent 6d3b5cca37
commit 61f8c7065e
5 changed files with 148 additions and 5 deletions
+5 -1
View File
@@ -529,7 +529,9 @@ In Release (NativeAOT), the MCP server and ASP.NET Core are excluded. The AI can
│ │ ├── Sdl3Window.cs # SDL3 window wrapper
│ │ ├── Timing.cs # DeltaTime, fixed timestep
│ │ ├── InputMapping.cs # Keyboard, mouse, gamepad input
│ │ ├── ICameraController.cs # Camera controller interface
│ │ ├── OrbitCameraController.cs # Mouse orbit camera
│ │ ├── FreeFlyCameraController.cs # WASD + mouse look camera
│ │ └── Components/ # Transform, Camera, Light, Material, Mesh
│ │
│ ├── Engine.Data/
@@ -760,7 +762,9 @@ dotnet run --project src/CortexEngine.App/CortexEngine.App.csproj
- `SDL3 2026.520.0` API: `SDL_Init` returns `SDLBool`, `SDL_PollEvent` returns `SDLBool`, `evt.type` is `uint`.
- Keyboard: `evt.key.key`; Mouse: `evt.motion.x`, `evt.motion.y`, `evt.wheel.y`.
- Orbit camera: right mouse drag rotates, mouse wheel zooms, `ESC` exits.
- **Orbit camera** (по умолчанию): правый клик + движение мыши — вращать, колесо — zoom.
- **FreeFly camera** (переключается клавишей `F`): `WASD` — двигаться, `Q`/`E` — вниз/вверх, `Shift` — ускорение, правый клик + мышь — осмотр.
- `ESC` — выход.
### 13.5 MCP Client Config
+16 -3
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Numerics;
using Engine.AI;
using SDL;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
@@ -73,7 +74,12 @@ class Program
.Set(new Transform(new Vector3(0.0f, 0.0f, 0.0f), Quaternion.Identity, Vector3.One))
.Set(new Light(new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.15f, 0.15f, 0.2f), 0.3f));
var orbit = new OrbitCameraController(cameraEntity, new Vector3(0.0f, 0.5f, 0.0f));
ICameraController[] cameraControllers =
[
new OrbitCameraController(cameraEntity, new Vector3(0.0f, 0.5f, 0.0f)),
new FreeFlyCameraController(cameraEntity)
];
var activeControllerIndex = 0;
var texturePath = GenerateCheckerboardTexture("Content/checkerboard.png", 256);
@@ -159,8 +165,15 @@ class Program
camera.AspectRatio = (float)lastWidth / lastHeight;
}
// Update orbit camera from mouse input.
orbit.Update(input, (float)timing.DeltaTime);
// Toggle camera controller with F.
if (input.IsKeyPressed(SDL_Keycode.SDLK_F))
{
activeControllerIndex = (activeControllerIndex + 1) % cameraControllers.Length;
Console.WriteLine($"Camera controller: {cameraControllers[activeControllerIndex].Name}");
}
// Update active camera controller from input.
cameraControllers[activeControllerIndex].Update(input, (float)timing.DeltaTime);
// Slowly rotate the model so we can see it in 3D.
ref var modelTransform = ref model.Ensure<Transform>();
+114
View File
@@ -0,0 +1,114 @@
using System;
using System.Numerics;
using Flecs.NET.Core;
using SDL;
using Engine.Core.Components;
namespace Engine.Core;
/// <summary>
/// First-person / free-fly camera controller.
/// WASD moves on the ground plane, Q/E move up/down, Shift boosts speed.
/// Mouse look while right mouse button is held.
/// </summary>
public sealed class FreeFlyCameraController : ICameraController
{
private readonly Entity _cameraEntity;
private float _yaw;
private float _pitch;
private float _speed = 3.0f;
private float _fastSpeed = 8.0f;
private float _mouseSensitivity = 0.003f;
private int _lastMouseX;
private int _lastMouseY;
private bool _wasRightMouseDown;
private Vector3 _position;
public string Name => "FreeFly";
public FreeFlyCameraController(Entity cameraEntity)
{
_cameraEntity = cameraEntity;
var camera = cameraEntity.Get<Camera>();
_position = camera.Position;
var forward = Vector3.Normalize(camera.Target - camera.Position);
_pitch = MathF.Asin(-forward.Y);
_yaw = MathF.Atan2(forward.X, forward.Z);
}
public void Update(InputMapping input, float deltaTime)
{
var move = Vector3.Zero;
var forward = new Vector3(MathF.Sin(_yaw), 0.0f, MathF.Cos(_yaw));
var right = new Vector3(MathF.Cos(_yaw), 0.0f, -MathF.Sin(_yaw));
var up = Vector3.UnitY;
if (input.IsKeyDown(SDL_Keycode.SDLK_W))
move += forward;
if (input.IsKeyDown(SDL_Keycode.SDLK_S))
move -= forward;
if (input.IsKeyDown(SDL_Keycode.SDLK_A))
move -= right;
if (input.IsKeyDown(SDL_Keycode.SDLK_D))
move += right;
if (input.IsKeyDown(SDL_Keycode.SDLK_E))
move += up;
if (input.IsKeyDown(SDL_Keycode.SDLK_Q))
move -= up;
if (move.LengthSquared() > 0.0f)
{
move = Vector3.Normalize(move);
var speed = input.IsKeyDown(SDL_Keycode.SDLK_LSHIFT) ? _fastSpeed : _speed;
_position += move * speed * deltaTime;
}
if (input.MouseWheelDelta != 0)
{
_speed *= 1.0f + input.MouseWheelDelta * 0.1f;
_speed = Math.Clamp(_speed, 0.5f, 30.0f);
}
if (input.MouseRight)
{
if (!_wasRightMouseDown)
{
_lastMouseX = input.MouseX;
_lastMouseY = input.MouseY;
_wasRightMouseDown = true;
}
else
{
var dx = input.MouseX - _lastMouseX;
var dy = input.MouseY - _lastMouseY;
_yaw += dx * _mouseSensitivity;
_pitch += dy * _mouseSensitivity;
_pitch = Math.Clamp(_pitch, -MathF.PI / 2.0f + 0.01f, MathF.PI / 2.0f - 0.01f);
_lastMouseX = input.MouseX;
_lastMouseY = input.MouseY;
}
}
else
{
_wasRightMouseDown = false;
}
UpdateCamera();
}
private void UpdateCamera()
{
var camera = _cameraEntity.Get<Camera>();
camera.Position = _position;
var direction = new Vector3(
MathF.Cos(_pitch) * MathF.Sin(_yaw),
-MathF.Sin(_pitch),
MathF.Cos(_pitch) * MathF.Cos(_yaw));
camera.Target = _position + direction;
camera.Up = Vector3.UnitY;
_cameraEntity.Set(camera);
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Engine.Core;
/// <summary>
/// Common interface for camera controllers (orbit, free-fly, etc.).
/// </summary>
public interface ICameraController
{
string Name { get; }
void Update(InputMapping input, float deltaTime);
}
+3 -1
View File
@@ -8,7 +8,7 @@ namespace Engine.Core;
/// Orbit camera controller. Right mouse drag rotates around the target,
/// mouse wheel zooms in/out.
/// </summary>
public sealed class OrbitCameraController
public sealed class OrbitCameraController : ICameraController
{
private readonly Entity _cameraEntity;
private float _distance;
@@ -19,6 +19,8 @@ public sealed class OrbitCameraController
private int _lastMouseY;
private bool _isDragging;
public string Name => "Orbit";
public OrbitCameraController(Entity cameraEntity, Vector3? target = null)
{
_cameraEntity = cameraEntity;