Struct Camera2
An immutable 2D camera that converts between world-space LengthPoint2 positions and screen-space Microsoft.Xna.Framework.Vector2 pixel coordinates.
Value type: Camera2 is a readonly record struct.
Declare camera fields as Camera2 (not Camera2?) and initialise them
before first use — an unassigned Camera2 field is default, not
null, and a default camera has Scale of zero.
public readonly record struct Camera2 : IEquatable<Camera2>
- Implements
- Inherited Members
Remarks
All "mutating" operations (such as ZoomBy(double)) return a new instance.
Late-bound camera state (e.g. a field set after construction) will be default,
not null — guard against Scale being zero before calling
WorldToScreen(LengthPoint2, Vector2).
The camera is centred on Position in world space. The centre of the screen always displays the camera's world position. Points to the right of the camera appear to the right on screen; points above the camera appear higher on screen (subject to the YAxis convention).
The Scale defines the zoom level: more pixels per world unit means the world appears larger on screen (zoomed in). Use ZoomBy(double) to adjust zoom and Lerp(PixelScale, PixelScale, double) to animate it smoothly.
screenSize pattern: WorldToScreen(LengthPoint2, Vector2) and ScreenToWorld(Vector2, Vector2)
require the render-target size on every call. Compute it once per frame from
GraphicsDevice.Viewport and store it as a local variable rather than
recalculating on every draw call:
// Compute once per frame; pass to every WorldToScreen / ScreenToWorld call:
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(32));
var screenSize = new Vector2(1920, 1080);
Vector2 screenPos = camera.WorldToScreen(LengthPoint2.FromMeters(5, 3), screenSize);
// Camera centred on (5, 5) at 32 px/m, Y-down (MonoGame default).
var camera = new Camera2(LengthPoint2.FromMeters(5, 5), PixelScale.PerMeter(32));
var screenSize = new Vector2(1920, 1080);
// Convert a world position to a screen pixel coordinate:
Vector2 screenPos = camera.WorldToScreen(LengthPoint2.FromMeters(6, 6), screenSize);
// Smooth zoom toward a target scale — call each update frame:
var currentScale = PixelScale.PerMeter(32);
var targetScale = PixelScale.PerMeter(64);
currentScale = PixelScale.Lerp(currentScale, targetScale, 0.016 * 5.0);
camera = camera with { Scale = currentScale };
Constructors
Camera2(LengthPoint2, PixelScale, YAxisMode)
An immutable 2D camera that converts between world-space LengthPoint2 positions and screen-space Microsoft.Xna.Framework.Vector2 pixel coordinates.
Value type: Camera2 is a readonly record struct.
Declare camera fields as Camera2 (not Camera2?) and initialise them
before first use — an unassigned Camera2 field is default, not
null, and a default camera has Scale of zero.
public Camera2(LengthPoint2 Position, PixelScale Scale, YAxisMode YAxis = YAxisMode.YDown)
Parameters
PositionLengthPoint2The world-space point that maps to the centre of the screen.
ScalePixelScaleThe zoom level expressed as a PixelScale. For example,
PixelScale.PerMeter(32)means one metre occupies 32 pixels.YAxisYAxisModeWhether the screen Y axis increases downward (YDown, the MonoGame default) or upward (YUp). Defaults to YDown.
Remarks
All "mutating" operations (such as ZoomBy(double)) return a new instance.
Late-bound camera state (e.g. a field set after construction) will be default,
not null — guard against Scale being zero before calling
WorldToScreen(LengthPoint2, Vector2).
The camera is centred on Position in world space. The centre of the screen always displays the camera's world position. Points to the right of the camera appear to the right on screen; points above the camera appear higher on screen (subject to the YAxis convention).
The Scale defines the zoom level: more pixels per world unit means the world appears larger on screen (zoomed in). Use ZoomBy(double) to adjust zoom and Lerp(PixelScale, PixelScale, double) to animate it smoothly.
screenSize pattern: WorldToScreen(LengthPoint2, Vector2) and ScreenToWorld(Vector2, Vector2)
require the render-target size on every call. Compute it once per frame from
GraphicsDevice.Viewport and store it as a local variable rather than
recalculating on every draw call:
// Compute once per frame; pass to every WorldToScreen / ScreenToWorld call:
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(32));
var screenSize = new Vector2(1920, 1080);
Vector2 screenPos = camera.WorldToScreen(LengthPoint2.FromMeters(5, 3), screenSize);
// Camera centred on (5, 5) at 32 px/m, Y-down (MonoGame default).
var camera = new Camera2(LengthPoint2.FromMeters(5, 5), PixelScale.PerMeter(32));
var screenSize = new Vector2(1920, 1080);
// Convert a world position to a screen pixel coordinate:
Vector2 screenPos = camera.WorldToScreen(LengthPoint2.FromMeters(6, 6), screenSize);
// Smooth zoom toward a target scale — call each update frame:
var currentScale = PixelScale.PerMeter(32);
var targetScale = PixelScale.PerMeter(64);
currentScale = PixelScale.Lerp(currentScale, targetScale, 0.016 * 5.0);
camera = camera with { Scale = currentScale };
Properties
Position
The world-space point that maps to the centre of the screen.
public LengthPoint2 Position { get; init; }
Property Value
Scale
The zoom level expressed as a PixelScale. For example,
PixelScale.PerMeter(32) means one metre occupies 32 pixels.
public PixelScale Scale { get; init; }
Property Value
YAxis
Whether the screen Y axis increases downward (YDown, the MonoGame default) or upward (YUp). Defaults to YDown.
public YAxisMode YAxis { get; init; }
Property Value
Methods
ClampZoom(double, double)
Clamps the zoom level so that PixelsPerUnit stays within
[min, max]. Position and
YAxis are unchanged.
public Camera2 ClampZoom(double min, double max)
Parameters
mindoubleThe minimum allowed pixels-per-unit value (most zoomed out). Must be positive.
maxdoubleThe maximum allowed pixels-per-unit value (most zoomed in). Must be ≥
min.
Returns
- Camera2
A new Camera2 whose Scale is clamped to the given range. Returns the receiver unchanged when its zoom is already within range.
Examples
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(128));
// Restrict zoom to between 16 and 64 px/m:
var clamped = camera.ClampZoom(16, 64);
Remarks
Prefer the ClampZoom(PixelScale, PixelScale) overload when bounds come from typed game configuration. This overload is a convenience for raw double values.
ClampZoom(PixelScale, PixelScale)
Clamps the zoom level so that PixelsPerUnit stays within
[min, max]. Position and
YAxis are unchanged.
public Camera2 ClampZoom(PixelScale min, PixelScale max)
Parameters
minPixelScaleThe minimum allowed zoom level (most zoomed out) expressed as a PixelScale.
maxPixelScaleThe maximum allowed zoom level (most zoomed in) expressed as a PixelScale.
Returns
- Camera2
A new Camera2 whose Scale is clamped to the given range. Returns the receiver unchanged when its zoom is already within range.
Examples
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(128));
var clamped = camera.ClampZoom(PixelScale.PerMeter(16), PixelScale.PerMeter(64));
Remarks
Bounds are normalised to the camera's Unit before comparison,
so min/max can be in any length unit.
For raw pixel-per-unit double bounds, use ClampZoom(Double, Double).
Lerp(Camera2, double)
Returns a new Camera2 linearly interpolated between this instance and
other at parameter t.
public Camera2 Lerp(Camera2 other, double t)
Parameters
otherCamera2The target camera state (
t= 1).tdoubleInterpolation parameter.
0.0returns the receiver;1.0returnsother. Values outside [0, 1] extrapolate.
Returns
Examples
var a = new Camera2(LengthPoint2.FromMeters(0, 0), PixelScale.PerMeter(32));
var b = new Camera2(LengthPoint2.FromMeters(10, 0), PixelScale.PerMeter(64));
var current = a.Lerp(b, 0.016 * 5.0);
Remarks
Both Position and PixelsPerUnit are interpolated. YAxis is taken from the receiver and is not interpolated. Prefer the Lerp(Camera2, Ratio) overload when the interpolation factor comes from typed game state.
Lerp(Camera2, Ratio)
Returns a new Camera2 linearly interpolated between this instance and
other at parameter t.
public Camera2 Lerp(Camera2 other, Ratio t)
Parameters
otherCamera2The target camera state (
t= 1).tRatioInterpolation parameter as a UnitsNet.Ratio. A decimal-fraction value of
0returns the receiver;1returnsother.
Returns
Examples
var a = new Camera2(LengthPoint2.FromMeters(0, 0), PixelScale.PerMeter(32));
var b = new Camera2(LengthPoint2.FromMeters(10, 0), PixelScale.PerMeter(64));
var current = a.Lerp(b, Ratio.FromDecimalFractions(0.08));
Remarks
Both Position and PixelsPerUnit are interpolated. YAxis is taken from the receiver and is not interpolated.
ScreenToWorld(Vector2, Vector2)
Converts a screen-space pixel coordinate back to a world-space position.
public LengthPoint2 ScreenToWorld(Vector2 screenPoint, Vector2 screenSize)
Parameters
screenPointVector2The pixel coordinate on the screen.
screenSizeVector2The size of the render target in pixels (width, height). Must match the value passed to WorldToScreen(LengthPoint2, Vector2) to get an exact inverse.
Returns
- LengthPoint2
The world-space LengthPoint2 corresponding to the screen pixel, expressed in Unit.
Examples
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(32));
var screenSize = new Vector2(1920, 1080);
LengthPoint2 worldMouse = camera.ScreenToWorld(new Vector2(960, 540), screenSize);
Remarks
This is the exact inverse of WorldToScreen(LengthPoint2, Vector2):
ScreenToWorld(WorldToScreen(p, size), size) ≈ p within floating-point precision.
Typical use is mouse picking — convert the cursor's screen position to a world coordinate
to test against game objects.
TranslateBy(LengthVector2)
public Camera2 TranslateBy(LengthVector2 delta)
Parameters
deltaLengthVector2The world-space displacement to apply.
Returns
Examples
var camera = new Camera2(LengthPoint2.FromMeters(5, 3), PixelScale.PerMeter(32));
var moved = camera.TranslateBy(LengthVector2.FromMeters(0.1, 0));
TranslateTo(LengthPoint2)
public Camera2 TranslateTo(LengthPoint2 destination)
Parameters
destinationLengthPoint2The world-space point to centre the camera on.
Returns
Examples
var camera = new Camera2(LengthPoint2.FromMeters(2, 3), PixelScale.PerMeter(32));
var focused = camera.TranslateTo(LengthPoint2.FromMeters(7, -1));
Remarks
Complements TranslateBy(LengthVector2) for absolute positioning: use
TranslateTo when you know the target world coordinate, and
TranslateBy when you have an incremental displacement.
WorldToScreen(LengthPoint2, Vector2)
Converts a world-space position to a screen-space pixel coordinate.
public Vector2 WorldToScreen(LengthPoint2 worldPoint, Vector2 screenSize)
Parameters
worldPointLengthPoint2The position in world space to project.
screenSizeVector2The size of the render target in pixels (width, height). The screen centre is at
screenSize / 2.
Returns
- Vector2
The pixel coordinate on screen. The screen centre corresponds to Position. Values outside
[0, screenSize]are off-screen but valid — callers may clip as needed.
Examples
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(32));
Vector2 px = camera.WorldToScreen(LengthPoint2.FromMeters(1, 0), new Vector2(1920, 1080));
WorldToScreenScale(Length)
Converts a world-space length (such as a shape radius) to a pixel size for rendering.
public float WorldToScreenScale(Length worldLength)
Parameters
worldLengthLengthThe length in world space to convert.
Returns
- float
The equivalent size in pixels as a float. Lengths in units other than Unit are automatically converted.
Examples
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(32));
Single pixelRadius = camera.WorldToScreenScale(Length.FromMeters(2));
Remarks
Use this when you need a scalar pixel size rather than a position — for example,
to compute the pixel radius of a Circle2 for drawing.
Y-axis mode does not affect this result; lengths are always positive.
ZoomBy(double)
public Camera2 ZoomBy(double factor)
Parameters
factordoubleThe zoom multiplier. Values greater than
1.0zoom in (more pixels per unit); values between0and1.0zoom out (fewer pixels per unit).
Returns
Examples
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(32));
// Zoom in 10%
camera = camera.ZoomBy(1.1);
ZoomBy(double, Vector2, Vector2)
Returns a new Camera2 zoomed by factor, adjusted so
that screenFocus remains at the same world position before and
after the zoom.
public Camera2 ZoomBy(double factor, Vector2 screenFocus, Vector2 screenSize)
Parameters
factordoubleThe zoom multiplier. Values greater than
1.0zoom in; values between0and1.0zoom out. Must be positive and non-zero.screenFocusVector2The screen-space pixel coordinate that should stay fixed during the zoom — typically the cursor position or the centre of a pinch gesture.
screenSizeVector2The size of the render target in pixels (width, height). For high-frequency render loops, compute this once per frame and pass it in rather than recalculating it on every call.
Returns
- Camera2
A new Camera2 whose Scale has been multiplied by
factorand whose Position has been adjusted so that the world point underscreenFocusis unchanged.
Examples
var camera = new Camera2(LengthPoint2.Origin, PixelScale.PerMeter(32));
var screenSize = new Vector2(1920, 1080);
// Zoom in 2× toward the screen centre
var zoomed = camera.ZoomBy(2.0, new Vector2(960, 540), screenSize);
Remarks
The adjustment formula is:
newPosition = worldFocus + (oldPosition − worldFocus) / factor,
where worldFocus = ScreenToWorld(screenFocus, screenSize) on the pre-zoom camera.
factor is a raw double multiplier rather than a
Ratio because it represents a scale change, not a fractional quantity.