Files
Physics-Engine/PhysicsEngine/Graphics/Camera.cs
2026-09-03 16:50:36 +02:00

188 lines
7.2 KiB
C#

using Raylib_cs;
using System;
using System.Numerics;
namespace PhysicsEngine
{
public class Camera
{
public const float PixelsPerMeter = 100.0f;
private const float GridSpacing = 1.0f; // meters
private const float ZoomLerpSpeed = 30.0f;
private const float MoveLerpSpeed = 20.0f;
private const float ZoomFactor = 0.1f;
private const float MinZoom = 0.2f;
private const float MaxZoom = 50.0f;
public Camera2D RaylibCamera { get; private set; }
public Vector2 Position => RaylibCamera.Target / PixelsPerMeter;
public float Zoom => RaylibCamera.Zoom;
private float targetZoom;
private Vector2 targetPosition;
private Vector2 dragStartMouseScreen;
private Vector2 dragStartTarget;
private bool isDragging = false;
public Camera()
{
RaylibCamera = new Camera2D
{
Target = Vector2.Zero,
Offset = new Vector2(Raylib.GetScreenWidth() / 2.0f, Raylib.GetScreenHeight() / 2.0f),
Rotation = 0.0f,
Zoom = 1.0f
};
targetZoom = 1.0f;
targetPosition = RaylibCamera.Target;
}
public void Update(float frameTime)
{
Camera2D cam = RaylibCamera;
cam.Offset = new Vector2(Raylib.GetScreenWidth() / 2.0f, Raylib.GetScreenHeight() / 2.0f);
// panning
if (Raylib.IsMouseButtonPressed(MouseButton.Middle))
{
isDragging = true;
dragStartMouseScreen = Raylib.GetMousePosition();
dragStartTarget = targetPosition;
}
if (isDragging)
{
if (Raylib.IsMouseButtonDown(MouseButton.Middle))
{
Vector2 currentMouseScreen = Raylib.GetMousePosition();
Vector2 screenDelta = currentMouseScreen - dragStartMouseScreen;
targetPosition = dragStartTarget - (screenDelta / cam.Zoom);
cam.Target = targetPosition;
}
else
{
isDragging = false;
}
}
else
{
// arrow key panning
float moveSpeed = 5.0f * PixelsPerMeter;
if (Raylib.IsKeyDown(KeyboardKey.Right)) targetPosition.X += moveSpeed * frameTime;
if (Raylib.IsKeyDown(KeyboardKey.Left)) targetPosition.X -= moveSpeed * frameTime;
if (Raylib.IsKeyDown(KeyboardKey.Down)) targetPosition.Y += moveSpeed * frameTime;
if (Raylib.IsKeyDown(KeyboardKey.Up)) targetPosition.Y -= moveSpeed * frameTime;
}
// recenter
if (Raylib.IsKeyPressed(KeyboardKey.Space))
{
targetPosition = Vector2.Zero;
targetZoom = 1.0f;
isDragging = false;
cam.Target = Vector2.Zero;
cam.Zoom = 1.0f;
}
// zooming
float wheel = Raylib.GetMouseWheelMove();
if (wheel != 0.0f)
{
targetZoom *= MathF.Pow(1 + ZoomFactor, wheel);
targetZoom = Math.Clamp(targetZoom, MinZoom, MaxZoom);
}
// lerp zoom
if (MathF.Abs(cam.Zoom - targetZoom) > 0.0001f)
{
Vector2 mouseWorldBefore = Raylib.GetScreenToWorld2D(Raylib.GetMousePosition(), cam);
cam.Zoom += (targetZoom - cam.Zoom) * ZoomLerpSpeed * frameTime;
cam.Zoom = Math.Clamp(cam.Zoom, MinZoom, MaxZoom);
Vector2 mouseWorldAfter = Raylib.GetScreenToWorld2D(Raylib.GetMousePosition(), cam);
targetPosition += (mouseWorldBefore - mouseWorldAfter);
cam.Target = targetPosition;
if (isDragging)
{
dragStartMouseScreen = Raylib.GetMousePosition();
dragStartTarget = targetPosition;
}
}
else if (!isDragging)
{
cam.Target.X += (targetPosition.X - cam.Target.X) * MoveLerpSpeed * frameTime;
cam.Target.Y += (targetPosition.Y - cam.Target.Y) * MoveLerpSpeed * frameTime;
}
RaylibCamera = cam;
}
public void DrawGrid()
{
float screenW = Raylib.GetScreenWidth();
float screenH = Raylib.GetScreenHeight();
float cellPixels = GridSpacing * PixelsPerMeter * RaylibCamera.Zoom;
float subCellPixels = cellPixels / 5.0f;
Vector2 originScreen = Raylib.GetWorldToScreen2D(Vector2.Zero, RaylibCamera);
Color gridColor = MuiDarkColor.SurfaceVariant.ToColor();
Color subGridColor = new Color((byte)gridColor.R, (byte)gridColor.G, (byte)gridColor.B, (byte)75);
Color mainGridColor = new Color((byte)gridColor.R, (byte)gridColor.G, (byte)gridColor.B, (byte)150);
// subgrid lines
float startSubX = originScreen.X % subCellPixels;
if (startSubX > 0) startSubX -= subCellPixels;
for (float x = startSubX; x <= screenW; x += subCellPixels)
{
float relativeToOriginX = x - originScreen.X;
int indexX = (int)Math.Round(relativeToOriginX / subCellPixels);
if (indexX % 5 == 0) continue;
int ix = (int)Math.Round(x);
Raylib.DrawLineEx(new Vector2(ix, 0), new Vector2(ix, screenH), 2.0f, subGridColor);
}
float startSubY = originScreen.Y % subCellPixels;
if (startSubY > 0) startSubY -= subCellPixels;
for (float y = startSubY; y <= screenH; y += subCellPixels)
{
float relativeToOriginY = y - originScreen.Y;
int indexY = (int)Math.Round(relativeToOriginY / subCellPixels);
if (indexY % 5 == 0) continue;
int iy = (int)Math.Round(y);
Raylib.DrawLineEx(new Vector2(0, iy), new Vector2(screenW, iy), 2.0f, subGridColor);
}
// main grid lines
float startMainX = originScreen.X % cellPixels;
if (startMainX > 0) startMainX -= cellPixels;
for (float x = startMainX; x <= screenW; x += cellPixels)
{
int ix = (int)Math.Round(x);
Raylib.DrawLineEx(new Vector2(ix, 0), new Vector2(ix, screenH), 3.0f, mainGridColor);
}
float startMainY = originScreen.Y % cellPixels;
if (startMainY > 0) startMainY -= cellPixels;
for (float y = startMainY; y <= screenH; y += cellPixels)
{
int iy = (int)Math.Round(y);
Raylib.DrawLineEx(new Vector2(0, iy), new Vector2(screenW, iy), 3.0f, mainGridColor);
}
}
public void DrawHUD()
{
float screenH = Raylib.GetScreenHeight();
string hudText = $"X: {Position.X:F2}m Y: {Position.Y:F2}m Zoom: {Zoom:F2}x";
Program.DrawText(hudText, 20, (int)screenH - 40, 20, MuiDarkColor.TextPrimary.ToColor());
}
}
}