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:
@@ -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
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user