diff --git a/Camera2D.cs b/Camera2D.cs index d476759..21e2dfd 100644 --- a/Camera2D.cs +++ b/Camera2D.cs @@ -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,11 +66,45 @@ 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() { diff --git a/LunaLightGame.cs b/LunaLightGame.cs index c6603b9..6bb8946 100644 --- a/LunaLightGame.cs +++ b/LunaLightGame.cs @@ -27,11 +27,11 @@ namespace LunaLightXMG // Test Render Target Handler private RenderHandler renderHandler; bool resizing; - + // Test sprites - eventually handle in a sprite manager? private Texture2D platformTexture; private Texture2D sinkArea; - + // Test platforms private Platform[] platforms; private int numOfPlatforms; @@ -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(); - random = new Random(42); - // Init number of platforms / enemies numOfPlatforms = 4; numOfEnemies = 10; + + util = new Utilities(); } protected override void Initialize() @@ -80,7 +80,7 @@ namespace LunaLightXMG playerInput.HasControl = true; // May need to find a new home for this base.Initialize(); - + // Initialize camera camera2D = new Camera2D(retroWidth, retroHeight); // camera test - set level bounds @@ -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() @@ -161,7 +165,7 @@ namespace LunaLightXMG // Test player input and movement with abstracted player class playerInput.Update(); player.Update(gameTime, playerInput, platforms); - + foreach (var enemy in enemies) { enemy.Update(gameTime, player, platforms); } @@ -169,12 +173,13 @@ namespace LunaLightXMG renderHandler.UpdateZoom(); // Update camera position camera2D.Follow(player.position); + camera2D.UpdateScreenShake(gameTime); // camera testing TestCameraWhilePlaying(); base.Update(gameTime); } - + protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); @@ -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 diff --git a/UpdateNotes/Camera2D.md b/UpdateNotes/Camera2D.md index 89b040e..85acfbe 100644 --- a/UpdateNotes/Camera2D.md +++ b/UpdateNotes/Camera2D.md @@ -30,4 +30,20 @@ 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. \ No newline at end of file +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** \ No newline at end of file diff --git a/Utilities.cs b/Utilities.cs new file mode 100644 index 0000000..36487f0 --- /dev/null +++ b/Utilities.cs @@ -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()); + } + } +}