Skip to content

Instantly share code, notes, and snippets.

@Apostolique
Last active March 27, 2018 15:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Apostolique/475b3827fd3094fdbec09b3e899b5db0 to your computer and use it in GitHub Desktop.
Save Apostolique/475b3827fd3094fdbec09b3e899b5db0 to your computer and use it in GitHub Desktop.
Camera2D.cs
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Extended.ViewportAdapters;
namespace MonoGame.Extended
{
public class Camera2D : IMovable, IRotatable
{
private readonly ViewportAdapter _viewportAdapter;
private float _maximumZoom = float.MaxValue;
private float _minimumZoom;
private float _zoom;
private float _depth;
public Camera2D(GraphicsDevice graphicsDevice)
: this(new DefaultViewportAdapter(graphicsDevice))
{
}
public Camera2D(ViewportAdapter viewportAdapter)
{
_viewportAdapter = viewportAdapter;
Rotation = 0;
Zoom = 1;
Depth = 1;
Origin = new Vector2(viewportAdapter.VirtualWidth/2f, viewportAdapter.VirtualHeight/2f);
Position = Vector2.Zero;
}
public Vector2 Origin { get; set; }
public float Zoom
{
get { return _zoom; }
set
{
if ((value < MinimumZoom) || (value > MaximumZoom))
throw new ArgumentException("Zoom must be between MinimumZoom and MaximumZoom");
_zoom = value;
}
}
public float MinimumZoom
{
get { return _minimumZoom; }
set
{
if (value < 0)
throw new ArgumentException("MinimumZoom must be greater than zero");
if (Zoom < value)
Zoom = MinimumZoom;
_minimumZoom = value;
}
}
public float MaximumZoom
{
get { return _maximumZoom; }
set
{
if (value < 0)
throw new ArgumentException("MaximumZoom must be greater than zero");
if (Zoom > value)
Zoom = value;
_maximumZoom = value;
}
}
public float Depth
{
get { return _depth; }
set
{
_depth = value;
}
}
public RectangleF BoundingRectangle
{
get
{
var frustum = GetBoundingFrustum();
var corners = frustum.GetCorners();
var topLeft = corners[0];
var bottomRight = corners[2];
var width = bottomRight.X - topLeft.X;
var height = bottomRight.Y - topLeft.Y;
return new RectangleF(topLeft.X, topLeft.Y, width, height);
}
}
public Vector2 Position { get; set; }
public float Rotation { get; set; }
public void Move(Vector2 direction)
{
Position += Vector2.Transform(direction, Matrix.CreateRotationZ(-Rotation));
}
public void Rotate(float deltaRadians)
{
Rotation += deltaRadians;
}
public void ZoomIn(float deltaZoom)
{
ClampZoom(Zoom + deltaZoom);
}
public void ZoomOut(float deltaZoom)
{
ClampZoom(Zoom - deltaZoom);
}
public void DollyIn(float deltaDolly)
{
Depth -= deltaDolly;
}
public void DollyOut(float deltaDolly)
{
Depth += deltaDolly;
}
private void ClampZoom(float value)
{
if (value < MinimumZoom)
Zoom = MinimumZoom;
else
Zoom = value > MaximumZoom ? MaximumZoom : value;
}
public void LookAt(Vector2 position)
{
Position = position - new Vector2(_viewportAdapter.VirtualWidth/2f, _viewportAdapter.VirtualHeight/2f);
}
public Vector2 WorldToScreen(float x, float y)
{
return WorldToScreen(new Vector2(x, y));
}
public Vector2 WorldToScreen(Vector2 worldPosition)
{
var viewport = _viewportAdapter.Viewport;
return Vector2.Transform(worldPosition + new Vector2(viewport.X, viewport.Y), GetViewMatrix());
}
public Vector2 ScreenToWorld(float x, float y)
{
return ScreenToWorld(new Vector2(x, y));
}
public Vector2 ScreenToWorld(Vector2 screenPosition)
{
var viewport = _viewportAdapter.Viewport;
return Vector2.Transform(screenPosition - new Vector2(viewport.X, viewport.Y),
Matrix.Invert(GetViewMatrix()));
}
/// <summary>
/// Outputs a depth value.
/// Let's say you want a layer with a depth of 2 to be drawn at a scale of 0.5.
/// You'd call this function with 0.5 as the zoom parameter and 2 as the depth parameter:
///
/// ZoomToDepth(0.5f, 2f);
///
/// The result would be 4. In other words, you'd have to set the camera's depth to 4.
/// <seealso cref="DepthToZoom(float, float)"/>
/// </summary>
public float ZoomToDepth(float zoom, float depth)
{
return (1f/Zoom) + depth;
}
/// <summary>
/// Outputs a zoom value.
/// Finds a depth layer's zoom value relative to an other depth layer.
/// <seealso cref="ZoomToDepth(float, float)"/>
/// </summary>
public float DepthToZoom(float depth, float depthOffset)
{
return (1f/(depth - depthOffset));
}
public float DepthScale(float depth)
{
return 1 - depth;
}
public bool IsVisible(float depth)
{
float zoom = DepthToZoom(Depth, depth);
return zoom > 0 && zoom < 10;
}
public Matrix GetViewMatrix()
{
return GetViewMatrix(0);
}
public Matrix GetViewMatrix(float depth)
{
return GetViewMatrix(depth, DepthScale(depth));
}
public Matrix GetViewMatrix(float depth, float scale)
{
return GetVirtualViewMatrix(depth, scale)*_viewportAdapter.GetScaleMatrix();
}
private Matrix GetVirtualViewMatrix()
{
return GetVirtualViewMatrix(0, DepthScale(0));
}
private Matrix GetVirtualViewMatrix(float scale)
{
return GetVirtualViewMatrix(0, scale);
}
private Matrix GetVirtualViewMatrix(float depth, float scale)
{
float depthZoom = DepthToZoom(Depth, depth);
return
Matrix.CreateScale(scale, scale, 1)*
Matrix.CreateTranslation(new Vector3(-Position, 0.0f))*
Matrix.CreateTranslation(new Vector3(-Origin, 0.0f))*
Matrix.CreateRotationZ(Rotation)*
Matrix.CreateScale(Zoom, Zoom, 1)*
Matrix.CreateScale(depthZoom, depthZoom, 1)*
Matrix.CreateTranslation(new Vector3(Origin, 0.0f));
}
public Matrix GetInverseViewMatrix()
{
return Matrix.Invert(GetViewMatrix());
}
public Matrix GetInverseViewMatrix(float depth)
{
return Matrix.Invert(GetViewMatrix(depth));
}
public Matrix GetInverseViewMatrix(float depth, float scale)
{
return Matrix.Invert(GetViewMatrix(depth, scale));
}
private Matrix GetProjectionMatrix(Matrix viewMatrix)
{
var projection = Matrix.CreateOrthographicOffCenter(0, _viewportAdapter.VirtualWidth,
_viewportAdapter.VirtualHeight, 0, -1, 0);
Matrix.Multiply(ref viewMatrix, ref projection, out projection);
return projection;
}
public BoundingFrustum GetBoundingFrustum()
{
return GetBoundingFrustum(0, DepthScale(0));
}
public BoundingFrustum GetBoundingFrustum(float depth)
{
return GetBoundingFrustum(depth, DepthScale(depth));
}
public BoundingFrustum GetBoundingFrustum(float depth, float scale)
{
var viewMatrix = GetVirtualViewMatrix(depth, scale);
var projectionMatrix = GetProjectionMatrix(viewMatrix);
return new BoundingFrustum(projectionMatrix);
}
public ContainmentType Contains(Point point)
{
return Contains(point.ToVector2());
}
public ContainmentType Contains(Vector2 vector2)
{
return GetBoundingFrustum().Contains(new Vector3(vector2.X, vector2.Y, 0));
}
public ContainmentType Contains(Rectangle rectangle)
{
var max = new Vector3(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height, 0.5f);
var min = new Vector3(rectangle.X, rectangle.Y, 0.5f);
var boundingBox = new BoundingBox(min, max);
return GetBoundingFrustum().Contains(boundingBox);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment