mirror of
https://github.com/TheRavenwoodArts/LunaLightXMG.git
synced 2026-07-30 22:05:02 -07:00
Created basic camera class.
This commit is contained in:
+82
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
-15
@@ -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;
|
||||||
@@ -47,7 +47,7 @@ namespace LunaLightXMG
|
|||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
private static Random random;
|
private static Random random;
|
||||||
|
|
||||||
public LunaLightGame()
|
public LunaLightGame()
|
||||||
{
|
{
|
||||||
graphics = new GraphicsDeviceManager(this);
|
graphics = new GraphicsDeviceManager(this);
|
||||||
@@ -70,26 +70,58 @@ namespace LunaLightXMG
|
|||||||
numOfPlatforms = 4;
|
numOfPlatforms = 4;
|
||||||
numOfEnemies = 10;
|
numOfEnemies = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
// Set initial window size
|
// Set initial window size
|
||||||
graphics.PreferredBackBufferWidth = 1920;
|
graphics.PreferredBackBufferWidth = 1920;
|
||||||
graphics.PreferredBackBufferHeight = 1080;
|
graphics.PreferredBackBufferHeight = 1080;
|
||||||
graphics.ApplyChanges();
|
graphics.ApplyChanges();
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
// Generate random platforms - test this idea out...
|
// Generate random platforms - test this idea out...
|
||||||
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);
|
||||||
@@ -129,23 +161,32 @@ namespace LunaLightXMG
|
|||||||
foreach (var enemy in enemies) {
|
foreach (var enemy in enemies) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(GameTime gameTime)
|
protected override void Draw(GameTime gameTime)
|
||||||
{
|
{
|
||||||
GraphicsDevice.Clear(Color.CornflowerBlue);
|
GraphicsDevice.Clear(Color.CornflowerBlue);
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
// Test draw tiles
|
// Test draw tiles
|
||||||
foreach (var platform in platforms)
|
foreach (var platform in platforms)
|
||||||
{
|
{
|
||||||
@@ -242,7 +283,10 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ namespace LunaLightXMG
|
|||||||
|
|
||||||
// Keyboard input
|
// Keyboard input
|
||||||
KeyboardState keyboardState = Keyboard.GetState();
|
KeyboardState keyboardState = Keyboard.GetState();
|
||||||
|
|
||||||
KeyLeft = keyboardState.IsKeyDown(Keys.A) ? 1 : 0;
|
KeyLeft = keyboardState.IsKeyDown(Keys.A) ? 1 : 0;
|
||||||
KeyRight = keyboardState.IsKeyDown(Keys.D) ? 1 : 0;
|
KeyRight = keyboardState.IsKeyDown(Keys.D) ? 1 : 0;
|
||||||
KeyUp = keyboardState.IsKeyDown(Keys.W) ? 1 : 0;
|
KeyUp = keyboardState.IsKeyDown(Keys.W) ? 1 : 0;
|
||||||
|
|||||||
+24
-1
@@ -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.
|
||||||
Reference in New Issue
Block a user