Created basic camera class.

This commit is contained in:
jme9
2025-03-17 09:15:46 -07:00
parent 306d4328e2
commit 529522499e
4 changed files with 166 additions and 17 deletions
+82
View File
@@ -0,0 +1,82 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace LunaLightXMG
{
public class Camera2D
{
// Fields
private readonly Viewport viewport;
private float zoom;
private float rotation;
private Vector2 position;
private Rectangle levelBounds;
// Getters and setters
public Vector2 Position
{
get => position;
set => position = value;
}
public float Zoom
{
get => zoom;
set => zoom = (int)Math.Floor(MathHelper.Clamp(value, 1f, 3f));
}
public float Rotation
{
get => rotation;
set => rotation = value;
}
// constructor
public Camera2D(int width, int height)
{
this.viewport = new Viewport(0, 0, width, height);
levelBounds = new Rectangle(0, 0, 320, 180); // Default
zoom = 1f;
rotation = 0f;
position = Vector2.Zero;
}
public Matrix GetViewMatrix()
{
return
Matrix.CreateTranslation(new Vector3(-position, 0f)) *
Matrix.CreateRotationZ(rotation) *
Matrix.CreateScale(zoom, zoom, 1f) *
Matrix.CreateTranslation(new Vector3(viewport.Width * 0.5f, viewport.Height * 0.5f, 0f));
}
public void SetLevelBounds(Rectangle bounds)
{
levelBounds = bounds;
}
public void Follow(Vector2 targetPostion)
{
// temp code
Vector2 smoothPosition = Vector2.Lerp(position, targetPostion, 0.1f);
position = smoothPosition;
ClampToBounds();
}
private void ClampToBounds()
{
// Calculate the cam's half viewport size
var halfViewportWidth = viewport.Width * 0.5f / zoom;
var halfViewportHeight = viewport.Height * 0.5f / zoom;
// Clamp cam to level bounds
float minX = levelBounds.Left + halfViewportWidth;
float maxX = levelBounds.Right - halfViewportWidth;
float minY = levelBounds.Top + halfViewportHeight;
float maxY = levelBounds.Bottom - halfViewportHeight;
// Set cam position within level bounds
position.X = MathHelper.Clamp(position.X, minX, maxX);
position.Y = MathHelper.Clamp(position.Y, minY, maxY);
}
}
}
+48 -4
View File
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media; using Microsoft.Xna.Framework.Media;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace LunaLightXMG namespace LunaLightXMG
{ {
@@ -16,13 +17,12 @@ namespace LunaLightXMG
private List<Enemy> enemies; private List<Enemy> enemies;
private int numOfEnemies; private int numOfEnemies;
private GraphicsDeviceManager graphics; private GraphicsDeviceManager graphics;
private Camera2D camera2D;
private SpriteBatch spriteBatch; private SpriteBatch spriteBatch;
// Native retro scale // Native retro scale
private int retroWidth = 320; private int retroWidth = 320;
private int retroHeight = 180; private int retroHeight = 180;
// private int retroWidth = 480;
// private int retroHeight = 270;
// Test Render Target // Test Render Target
private RenderTarget2D renderTarget; private RenderTarget2D renderTarget;
@@ -81,6 +81,12 @@ namespace LunaLightXMG
playerInput.HasControl = true; // May need to find a new home for this playerInput.HasControl = true; // May need to find a new home for this
base.Initialize(); base.Initialize();
// Initialize camera
camera2D = new Camera2D(retroWidth, retroHeight);
// camera test - set level bounds
camera2D.SetLevelBounds(new Rectangle(0,0,480,270)); // default 320x180
CalculateRenderDestination(); CalculateRenderDestination();
// Play music // Play music
// PlayBackgroundMusic(sinkBeats); // PlayBackgroundMusic(sinkBeats);
@@ -89,7 +95,33 @@ namespace LunaLightXMG
platforms = PlatformGenerator.CreatePlatforms(numOfPlatforms); platforms = PlatformGenerator.CreatePlatforms(numOfPlatforms);
} }
// Testing camera ------------------------
private void TestCameraWhilePlaying()
{
KeyboardState keyboardState = Keyboard.GetState();
var KeyI = keyboardState.IsKeyDown(Keys.I) ? true : false;
var KeyO = keyboardState.IsKeyDown(Keys.O) ? true : false;
var KeyK = keyboardState.IsKeyDown(Keys.K) ? true : false;
var KeyL = keyboardState.IsKeyDown(Keys.L) ? true : false;
if (KeyI)
{
camera2D.Zoom += 1f;
}
if (KeyO)
{
camera2D.Zoom -= 1f;
}
if (KeyK)
{
camera2D.Rotation -= 0.01f;
}
if (KeyL)
{
camera2D.Rotation += 0.01f;
}
}
// ---------------------------------------
protected override void LoadContent() protected override void LoadContent()
{ {
spriteBatch = new SpriteBatch(GraphicsDevice); spriteBatch = new SpriteBatch(GraphicsDevice);
@@ -130,6 +162,11 @@ namespace LunaLightXMG
enemy.Update(gameTime, player, platforms); enemy.Update(gameTime, player, platforms);
} }
// Update camera position
camera2D.Follow(player.position);
// camera testing
TestCameraWhilePlaying();
base.Update(gameTime); base.Update(gameTime);
} }
@@ -140,8 +177,12 @@ namespace LunaLightXMG
// Test Render Target - Draw sprites to the render target // Test Render Target - Draw sprites to the render target
GraphicsDevice.SetRenderTarget(renderTarget); GraphicsDevice.SetRenderTarget(renderTarget);
// Test Sprite // Add camera to draw batch
spriteBatch.Begin(samplerState: SamplerState.PointClamp); spriteBatch.Begin(
transformMatrix: camera2D.GetViewMatrix(),
samplerState: SamplerState.PointClamp
);
// Debug Grid // Debug Grid
// spriteBatch.Draw(debugGrid, new Vector2(0, 0), Color.Blue); // spriteBatch.Draw(debugGrid, new Vector2(0, 0), Color.Blue);
// spriteBatch.Draw(sinkArea, new Vector2(0, 0), Color.White); // spriteBatch.Draw(sinkArea, new Vector2(0, 0), Color.White);
@@ -242,6 +283,9 @@ namespace LunaLightXMG
spriteBatch.DrawString(debugFont, $"Grounded: {player.grounded}", new Vector2(10, 30), Color.White); spriteBatch.DrawString(debugFont, $"Grounded: {player.grounded}", new Vector2(10, 30), Color.White);
spriteBatch.DrawString(debugFont, $"Walled: {player.walled}", new Vector2(10, 50), Color.White); spriteBatch.DrawString(debugFont, $"Walled: {player.walled}", new Vector2(10, 50), Color.White);
spriteBatch.DrawString(debugFont, $"Position: {player.position}", new Vector2(10, 70), Color.White); spriteBatch.DrawString(debugFont, $"Position: {player.position}", new Vector2(10, 70), Color.White);
spriteBatch.DrawString(debugFont, $"Camera Position: {camera2D.Position}", new Vector2(10, 90), Color.White);
spriteBatch.DrawString(debugFont, $"Camera Zoom: {camera2D.Zoom}", new Vector2(10, 110), Color.White);
spriteBatch.DrawString(debugFont, $"Camera Rotation: {camera2D.Rotation}", new Vector2(10, 130), Color.White);
spriteBatch.End(); spriteBatch.End();
} }
+24 -1
View File
@@ -2,9 +2,32 @@
Currentlly the camera is static, or non-existtent really.. we need to create a camera class that handles creating and managing a viewport that follows the player wihin the level. Currentlly the camera is static, or non-existtent really.. we need to create a camera class that handles creating and managing a viewport that follows the player wihin the level.
- If the level is the same size as te viewport then the camera is static. - If the level is the same size as the viewport then the camera is static.
- We also want to have a zoom capability that zooms between 320x180 and 480x270. The current view is 320x180. - We also want to have a zoom capability that zooms between 320x180 and 480x270. The current view is 320x180.
- We should be able to use a lot of the code from LunaLightGML however we will need to create the class objects and restructure to work with MonoGame. - We should be able to use a lot of the code from LunaLightGML however we will need to create the class objects and restructure to work with MonoGame.
## March 16th, 2025 - JME
Created intitial Camera2D class. We will need to add in code from GML for the smooth following effect, screen shake, etc. I did add some other stuff that is cool though.
- Zoom, we can set the camera zoom between 0.1f and 10f. These values may need to be changed after testing.
- Rotation, I thought this might be fun.. might come in handy for an effect later on. The camera is 2D but rotates on what woudl be the z-axis.
- We can set the level bounds that the camera exists in when moving from level to level.
## March 17th, 2025 - JME
**Zoom Weirdness**
Testing shows some serious pixel distortion when zooming. Rotation is odd.. but could still be cool at somepoint, lets focus on fixing the zoom.
I think the probelm is due to the fixed render target. We are changing the veiwport width and height when zooming but the render target stays the same. We are also zooming at sub-pixel values so it is possible to land on a zoom value of 1.5f for example which isn't pixel perfect and causes distortion.
Solutions:
- We could abandon zoom in the camera and control zoom by changing the render target values.
- We should in either case only zoom at integer values to preserve pixel-perfect zoom.
Leaning towards abstracting the render target code to its own class and handling zoom there. Overall I think this will be better as it is what draws to the backbuffer.