Implemented scren shake in the Camera2D class

This commit is contained in:
jme9
2025-03-19 18:59:13 -07:00
parent 0e60bb4207
commit 1c5f4b1b12
4 changed files with 102 additions and 23 deletions
+48 -4
View File
@@ -12,6 +12,15 @@ namespace LunaLightXMG
private float rotation;
private Vector2 position;
private Rectangle levelBounds;
private readonly float camSpeed = 0.1f;
// Screen Shake
private float shakeTimeRemaining = 0f;
private float shakeMagnitudeCurrent = 0f;
private bool shaking = false;
// Util
Utilities util;
// Getters and setters
public Vector2 Position
@@ -33,11 +42,12 @@ namespace LunaLightXMG
// constructor
public Camera2D(int width, int height)
{
this.viewport = new Viewport(0, 0, width, height);
viewport = new Viewport(0, 0, width, height);
levelBounds = new Rectangle(0, 0, 320, 180); // Default
zoom = 1f;
rotation = 0f;
position = Vector2.Zero;
util = new Utilities();
}
public Matrix GetViewMatrix()
@@ -56,12 +66,46 @@ namespace LunaLightXMG
public void Follow(Vector2 targetPostion)
{
// temp code
Vector2 smoothPosition = Vector2.Lerp(position, targetPostion, 0.1f);
position = smoothPosition;
position += (targetPostion - position) * camSpeed;
if (Math.Abs(position.X - targetPostion.X) < 0.1f)
{
position.X = targetPostion.X;
}
if (Math.Abs(position.Y - targetPostion.Y) < 0.1f)
{
position.Y = targetPostion.Y;
}
ClampToBounds();
}
public void ScreenShake(float shakeTime, float shakeMagnitude)
{
shakeTimeRemaining = shakeTime;
shakeMagnitudeCurrent = shakeMagnitude;
shaking = true;
}
public void UpdateScreenShake(GameTime gameTime)
{
if (!shaking) return;
if (shakeTimeRemaining > 0)
{
float shakeX = util.ChooseFromRange(-shakeMagnitudeCurrent, shakeMagnitudeCurrent);
float shakeY = util.ChooseFromRange(-shakeMagnitudeCurrent, shakeMagnitudeCurrent);
position.X += shakeX;
position.Y += shakeY;
shakeTimeRemaining -= (float)gameTime.ElapsedGameTime.TotalSeconds;
shakeMagnitudeCurrent *= 0.9f;
}
else
{
shaking = false;
shakeTimeRemaining = 0f;
shakeMagnitudeCurrent = 0f;
}
}
private void ClampToBounds()
{
// Calculate the cam's half viewport size
+12 -12
View File
@@ -44,8 +44,8 @@ namespace LunaLightXMG
private SpriteFont debugFont;
private Texture2D debugGrid;
// Utility
private static Random random;
// Util
Utilities util;
public LunaLightGame()
{
@@ -63,11 +63,11 @@ namespace LunaLightXMG
playerInput = new PlayerInput();
enemies = new List<Enemy>();
random = new Random(42);
// Init number of platforms / enemies
numOfPlatforms = 4;
numOfEnemies = 10;
util = new Utilities();
}
protected override void Initialize()
@@ -103,7 +103,7 @@ namespace LunaLightXMG
var KeyB = keyboardState.IsKeyDown(Keys.B);
var KeyN = keyboardState.IsKeyDown(Keys.N);
var KeyM = keyboardState.IsKeyDown(Keys.M);
var KeyP = keyboardState.IsKeyDown(Keys.P);
if (KeyK)
{
camera2D.Rotation -= 0.01f;
@@ -124,6 +124,10 @@ namespace LunaLightXMG
{
renderHandler.ZoomOut();
}
if (KeyP)
{
camera2D.ScreenShake(0.5f, 5f);
}
}
// ---------------------------------------
protected override void LoadContent()
@@ -169,6 +173,7 @@ namespace LunaLightXMG
renderHandler.UpdateZoom();
// Update camera position
camera2D.Follow(player.position);
camera2D.UpdateScreenShake(gameTime);
// camera testing
TestCameraWhilePlaying();
@@ -211,7 +216,7 @@ namespace LunaLightXMG
for (int i = 0; i < numberOfEnemies; i++)
{
Vector2 spawnPosition = new Vector2(16 + i * 3, -16);
Enemy enemy = new Enemy(spawnPosition, GetRandomValueBetween1And3());
Enemy enemy = new(spawnPosition, util.ChooseFromRange(1,3));
enemy.LoadContent(Content);
enemies.Add(enemy);
}
@@ -258,16 +263,11 @@ namespace LunaLightXMG
spriteBatch.DrawString(debugFont, $"Camera Rotation Control - Rotate left: K, Rotate right: L", new Vector2(10, 190), Color.White);
spriteBatch.DrawString(debugFont, $"Render Target Zoom Control - Zoom in: B, Zoom normal: N, Zoom out: M", new Vector2(10, 210), Color.White);
spriteBatch.DrawString(debugFont, $"Press P to test screen shake.", new Vector2(10, 230), Color.White);
spriteBatch.End();
}
public static float GetRandomValueBetween1And3()
{
return (float)(1 + 2 * random.NextDouble());
}
private void ResetGame()
{
// reset cam
+16
View File
@@ -31,3 +31,19 @@ Solutions:
- 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.
## March 19th, 2025 - JME
**ScreenShake**
I have already done this in the GML version however it has to be reimplemented to work with our current class structure. We should be able to add a ScreenShake() function to the Camera2D class that takes in shake time and shake magnitude arguments.
The function will work by randomly changing the cam position between the current position + magnitude and current position - magnitude. We already have a nice function written in our LunaLigthGame class that randomly chooses a number between to set numbers. **side note** It might be nice to create a utility class and move this function over there so we can use it from anywehre.
**Refining camera movement a bit**
When the player stops the camera slows to a stop. This is good except that in the last few mometns the camera gets kinda shakey beacause the Lerp() function we are using calculates to the 5th decimal and as it approaches the final postion the camera jumps from pixel to pixel slowly.
Rather than use Lerp() let's explicitly define our follow code and once the cam position is witin a certain threshold of target position we can snap it to the target position.
**Needs more refinement**
+19
View File
@@ -0,0 +1,19 @@
using System;
namespace LunaLightXMG
{
public class Utilities
{
private Random random;
public Utilities(int seed = 42)
{
random = new Random(seed);
}
public float ChooseFromRange(float num1, float num2)
{
return (float)(num1 + (num2 - num1) * random.NextDouble());
}
}
}