diff --git a/BoundingBox.cs b/BoundingBox.cs new file mode 100644 index 0000000..3c2afb7 --- /dev/null +++ b/BoundingBox.cs @@ -0,0 +1,24 @@ +using Microsoft.Xna.Framework; + +namespace LunaLightXMG +{ + public class BoundingBox + { + public Rectangle Bounds { get; private set; } + + public BoundingBox(Rectangle bounds) + { + Bounds = bounds; + } + + public bool Intersects(BoundingBox other) + { + return Bounds.Intersects(other.Bounds); + } + + public void Update(Vector2 position, int width, int height) + { + Bounds = new Rectangle((int)position.X, (int)position.Y, width, height); + } + } +} \ No newline at end of file diff --git a/ClassDiagram1.cd b/ClassDiagram1.cd new file mode 100644 index 0000000..7b89419 --- /dev/null +++ b/ClassDiagram1.cd @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Content/CastleWall TS16.png b/Content/CastleWall TS16.png new file mode 100644 index 0000000..e95bbd0 Binary files /dev/null and b/Content/CastleWall TS16.png differ diff --git a/Content/Content.mgcb b/Content/Content.mgcb index 5e70b96..0ff1bfd 100644 --- a/Content/Content.mgcb +++ b/Content/Content.mgcb @@ -13,6 +13,49 @@ #---------------------------------- Content ---------------------------------# +#begin CastleWall TS16.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:CastleWall TS16.png + +#begin fonts/debugFont.spritefont +/importer:FontDescriptionImporter +/processor:FontDescriptionProcessor +/processorParam:PremultiplyAlpha=True +/processorParam:TextureFormat=Compressed +/build:fonts/debugFont.spritefont + +#begin platform.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:platform.png + +#begin player.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:player.png + #begin spr_cave1_background.png /importer:TextureImporter /processor:TextureProcessor diff --git a/Content/fonts/debugFont.spritefont b/Content/fonts/debugFont.spritefont new file mode 100644 index 0000000..bd33ecf --- /dev/null +++ b/Content/fonts/debugFont.spritefont @@ -0,0 +1,60 @@ + + + + + + + Arial + + + 12 + + + 0 + + + true + + + + + + + + + + + + ~ + + + + diff --git a/Content/platform.png b/Content/platform.png new file mode 100644 index 0000000..34c5c3e Binary files /dev/null and b/Content/platform.png differ diff --git a/Content/player.png b/Content/player.png new file mode 100644 index 0000000..550dc69 Binary files /dev/null and b/Content/player.png differ diff --git a/Game1.cs b/Game1.cs index c8b3576..e30bb02 100644 --- a/Game1.cs +++ b/Game1.cs @@ -7,27 +7,32 @@ namespace LunaLightXMG { public class Game1 : Game { + // Class Objects + private Player player; + private PlayerInput playerInput; private GraphicsDeviceManager graphics; private SpriteBatch spriteBatch; + // Native retro scale + private int retroWidth = 320; + private int retroHeight = 180; + // Test Render Target private RenderTarget2D renderTarget; private Rectangle renderDestination; bool resizing; - // Test sprites - Texture2D sprRo; - Texture2D sprCave1Background; - Texture2D sprCave1Foreground; - Texture2D sprCave1Walls; + // Test sprites - eventually handle in a sprite manager? + //Texture2D sprCave1Background; + //Texture2D sprCave1Foreground; + //Texture2D sprCave1Walls; + Texture2D platformTexture; - // Test player input and movement with abstracted player class - private Player player; - // Player input - private PlayerInput playerInput; - // Native retro scale - private int retroWidth = 320; - private int retroHeight = 180; + // Test platforms + Platform[] platforms; + + // Debugging + SpriteFont debugFont; public Game1() { @@ -37,10 +42,11 @@ namespace LunaLightXMG Window.ClientSizeChanged += OnClientSizeChanged; Window.AllowUserResizing = true; - IsMouseVisible = true; + IsMouseVisible = true; // Test player input and movement with abstracted player class - player = new Player(); + Vector2 initPlayerPosition = new Vector2(163, 63); + player = new Player(initPlayerPosition); playerInput = new PlayerInput(); } @@ -65,6 +71,72 @@ namespace LunaLightXMG base.Initialize(); CalculateRenderDestination(); + + // Test initialize platforms -- this should be abstracted to a class + // Also, I'm assuming using a tool like tiled will prevent having to + // manually input platforms like below... + platforms = new Platform[] + { + // platforms + new Platform(new Vector2(48, 128), 16, 16), + new Platform(new Vector2(64, 128), 16, 16), + new Platform(new Vector2(96, 96), 16, 16), + new Platform(new Vector2(112, 96), 16, 16), + new Platform(new Vector2(192, 128), 16, 16), + new Platform(new Vector2(208, 128), 16, 16), + new Platform(new Vector2(240, 96), 16, 16), + new Platform(new Vector2(256, 96), 16, 16), + new Platform(new Vector2(192, 64), 16, 16), + new Platform(new Vector2(208, 64), 16, 16), + new Platform(new Vector2(144, 32), 16, 16), + new Platform(new Vector2(160, 32), 16, 16), + new Platform(new Vector2(288, 128), 16, 16), + new Platform(new Vector2(16, 96), 16, 16), + + // bottom + new Platform(new Vector2(16, 160), 16, 16), + new Platform(new Vector2(32, 160), 16, 16), + new Platform(new Vector2(48, 160), 16, 16), + new Platform(new Vector2(64, 160), 16, 16), + new Platform(new Vector2(80, 160), 16, 16), + new Platform(new Vector2(96, 160), 16, 16), + new Platform(new Vector2(112, 160), 16, 16), + new Platform(new Vector2(128, 160), 16, 16), + new Platform(new Vector2(144, 160), 16, 16), + new Platform(new Vector2(160, 160), 16, 16), + new Platform(new Vector2(176, 160), 16, 16), + new Platform(new Vector2(192, 160), 16, 16), + new Platform(new Vector2(208, 160), 16, 16), + new Platform(new Vector2(224, 160), 16, 16), + new Platform(new Vector2(240, 160), 16, 16), + new Platform(new Vector2(256, 160), 16, 16), + new Platform(new Vector2(272, 160), 16, 16), + new Platform(new Vector2(288, 160), 16, 16), + + // left side + new Platform(new Vector2(0, 0), 16, 16), + new Platform(new Vector2(0, 16), 16, 16), + new Platform(new Vector2(0, 32), 16, 16), + new Platform(new Vector2(0, 48), 16, 16), + new Platform(new Vector2(0, 64), 16, 16), + new Platform(new Vector2(0, 80), 16, 16), + new Platform(new Vector2(0, 96), 16, 16), + new Platform(new Vector2(0, 112), 16, 16), + new Platform(new Vector2(0, 128), 16, 16), + new Platform(new Vector2(0, 144), 16, 16), + + // right side + new Platform(new Vector2(304, 0), 16, 16), + new Platform(new Vector2(304, 16), 16, 16), + new Platform(new Vector2(304, 32), 16, 16), + new Platform(new Vector2(304, 48), 16, 16), + new Platform(new Vector2(304, 64), 16, 16), + new Platform(new Vector2(304, 80), 16, 16), + new Platform(new Vector2(304, 96), 16, 16), + new Platform(new Vector2(304, 112), 16, 16), + new Platform(new Vector2(304, 128), 16, 16), + new Platform(new Vector2(304, 144), 16, 16) + }; } private void CalculateRenderDestination() @@ -89,13 +161,20 @@ namespace LunaLightXMG protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); - sprRo = Content.Load("sprRo_Wall"); - sprCave1Background = Content.Load("spr_cave1_background"); - sprCave1Foreground = Content.Load("spr_Cave1_foreground"); - sprCave1Walls = Content.Load("spr_Cave1_walls"); + //sprCave1Background = Content.Load("spr_cave1_background"); + //sprCave1Foreground = Content.Load("spr_Cave1_foreground"); + //sprCave1Walls = Content.Load("spr_Cave1_walls"); + // Test platform + platformTexture = Content.Load("platform"); + + // load player sprite + player.LoadContent(Content); // player sprite is now handled in player.cs // Test Render Target renderTarget = new RenderTarget2D(GraphicsDevice, retroWidth, retroHeight); + + // Debugging + debugFont = Content.Load("fonts/debugFont"); } protected override void Update(GameTime gameTime) @@ -106,7 +185,7 @@ namespace LunaLightXMG // Test player input and movement with abstracted player class playerInput.Update(); - player.Update(gameTime, playerInput); + player.Update(gameTime, playerInput, platforms); base.Update(gameTime); } @@ -121,13 +200,19 @@ namespace LunaLightXMG // Test Sprite spriteBatch.Begin(samplerState: SamplerState.PointClamp); // Draw sprites here - spriteBatch.Draw(sprCave1Background,new Vector2(0,0),Color.White); + //spriteBatch.Draw(sprCave1Background,new Vector2(0,0),Color.White); + + // Test draw tiles + foreach (var platform in platforms) + { + platform.Draw(spriteBatch, platformTexture); // Assuming all tiles use the same texture for now + } // Test player input and movement with abstracted player class - player.Draw(spriteBatch, sprRo); + player.Draw(spriteBatch); - spriteBatch.Draw(sprCave1Walls, new Vector2(0, 0), Color.White); - spriteBatch.Draw(sprCave1Foreground, new Vector2(0, 0), Color.White); + //spriteBatch.Draw(sprCave1Walls, new Vector2(0, 0), Color.White); + //spriteBatch.Draw(sprCave1Foreground, new Vector2(0, 0), Color.White); spriteBatch.End(); @@ -137,7 +222,24 @@ namespace LunaLightXMG spriteBatch.Draw(renderTarget, renderDestination, Color.White); spriteBatch.End(); + // Debugging + DrawDebugInfo(); + base.Draw(gameTime); } + + // Debugging + private void DrawDebugInfo() + { + spriteBatch.Begin(); + + // Draw debug messages + spriteBatch.DrawString(debugFont, $"VSP: {player.vsp}", new Vector2(10, 10), 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, $"Position: {player.position}", new Vector2(10, 70), Color.White); + + spriteBatch.End(); + } } } diff --git a/Platform.cs b/Platform.cs new file mode 100644 index 0000000..9f1b657 --- /dev/null +++ b/Platform.cs @@ -0,0 +1,23 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace LunaLightXMG +{ + public class Platform + { + public Vector2 Position { get; private set; } + public BoundingBox BoundingBox { get; private set; } + + public Platform(Vector2 position, int width, int height) + { + Position = position; + BoundingBox = new BoundingBox(new Rectangle((int)position.X, (int)position.Y, width, height)); + } + + public void Draw(SpriteBatch spriteBatch, Texture2D texture) + { + spriteBatch.Draw(texture, Position, Color.White); + } + } +} + diff --git a/Player.cs b/Player.cs index ac2f26d..e5e83ed 100644 --- a/Player.cs +++ b/Player.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; namespace LunaLightXMG @@ -8,25 +9,28 @@ namespace LunaLightXMG { // Declare variables private float hsp; // Horizontal speed - private float vsp; // Vertical speed + public float vsp; // Vertical speed private float wallJumpDelay; // Delay for wall jumping - private bool grounded; // Is the player grounded? - private bool walled; // Is the player wall sliding? - private float hspAcc; // Horizontal acceleration - private float hspFrictionGround; // Ground friction - private float hspFrictionAir; // Air friction - private float hspWalk; // Maximum horizontal speed + public bool grounded; // Is the player grounded? + public bool walled; // Is the player wall sliding? + private bool wasWalled; // Remember is the player was on a wall or ground. + private float hspAcc; // Horizontal acceleration + private float hspFrictionGround; // Ground friction + private float hspFrictionAir; // Air friction + private float hspWalk; // Maximum horizontal speed private float grv; // Gravity - private float vspMax; // Maximum vertical speed - private float grvWall; // Gravity when wall sliding - private float vspMaxWall; // Maximum vertical speed when wall sliding - private float vspJump; // Jump speed + private float vspMax; // Maximum vertical speed + private float grvWall; // Gravity when wall sliding + private float vspMaxWall; // Maximum vertical speed when wall sliding + private float vspJump; // Jump speed - // Player position + // Player initilization + private Texture2D texture; public Vector2 position; - + public BoundingBox BoundingBox { get; private set; } + // Constructor to initialize variables - public Player() + public Player(Vector2 initPosition) { // Initialize your variables here hsp = 0; @@ -34,32 +38,46 @@ namespace LunaLightXMG wallJumpDelay = 0; grounded = false; walled = false; - hspAcc = 0.5f; - hspFrictionGround = 0.2f; - hspFrictionAir = 0.1f; - hspWalk = 4f; - //grv = 0.5f; - grv = 0.0f; // No grav until we have walls, bounding boxes, etc.. + wasWalled = false; + hspAcc = 0.2f; + hspFrictionGround = 0.3f; + hspFrictionAir = 0.3f; + hspWalk = 3f; + grv = 0.2f; vspMax = 10f; grvWall = 0.1f; vspMaxWall = 5f; - vspJump = -10f; + vspJump = -4f; - position = new Vector2(163, 63); // Initialize player position + position = initPosition; + BoundingBox = new BoundingBox(new Rectangle((int)position.X, (int)position.Y, 8, 16)); + } + public void LoadContent(ContentManager content) + { + texture = content.Load("player"); } // Update method to handle movement logic - public void Update(GameTime gameTime, PlayerInput input) + public void Update(GameTime gameTime, PlayerInput input, Platform[] platforms) { + // Update player position Movement(input); - // Update player position based on speed - position.X += hsp; - position.Y += vsp; + // Update the player's bounding box + BoundingBox.Update(position, 8, 16); + + CollisionChecks(platforms); } private void Movement(PlayerInput input) { + // Handle jumping + if (input.KeyJump && grounded) + { + grounded = false; + vsp = vspJump; + } + // Horizontal movement with acceleration + wall jump delay wallJumpDelay = Math.Max(wallJumpDelay - 1, 0); // counts down every frame @@ -86,7 +104,6 @@ namespace LunaLightXMG grvFinal = grvWall; vspMaxFinal = vspMaxWall; } - vsp += (vsp == 0 ? grvFinal * 2 : grvFinal); vsp = MathHelper.Clamp(vsp, vspJump, vspMaxFinal); } @@ -109,12 +126,83 @@ namespace LunaLightXMG return start; } - public void Draw(SpriteBatch spriteBatch, Texture2D texture) + // Collision Checks - maybe abstract to a class --------------------------------------------------------------- + private void CollisionChecks(Platform[] platforms) + { + // Horizontal collision prediction + float onePixh = Math.Sign(hsp); + if (IsColliding(position + new Vector2(hsp, 0), platforms)) + { + while (!IsColliding(position + new Vector2(onePixh, 0), platforms)) + { + position.X += onePixh; + } + hsp = 0; + } + position.X += hsp; // Horizontal move + + // Vertical collision prediction + float onePixv = Math.Sign(vsp); + if (IsColliding(position + new Vector2(0, vsp), platforms)) + { + while (!IsColliding(position + new Vector2(0, onePixv), platforms)) + { + position.Y += onePixv; + } + vsp = 0; + } + position.Y += vsp; // Vertical move + + // Grounded? + grounded = IsColliding(position + new Vector2(0, 1), platforms); + + // Walled? + walled = WallCheck(platforms); + if (walled) + wasWalled = walled; + } + + private bool IsColliding(Vector2 newPosition, Platform[] platforms) + { + Rectangle newBounds = new Rectangle((int)newPosition.X, (int)newPosition.Y, BoundingBox.Bounds.Width, BoundingBox.Bounds.Height); + foreach (var platform in platforms) + { + if (newBounds.Intersects(platform.BoundingBox.Bounds)) + { + return true; + } + } + return false; + } + + private Platform GetCollidingPlatform(Vector2 newPosition, Platform[] platforms) + { + Rectangle newBounds = new Rectangle((int)newPosition.X, (int)newPosition.Y, BoundingBox.Bounds.Width, BoundingBox.Bounds.Height); + foreach (var platform in platforms) + { + if (newBounds.Intersects(platform.BoundingBox.Bounds)) + { + return platform; + } + } + return null; + } + + private bool WallCheck(Platform[] platforms) + { + if (IsColliding(position + new Vector2(1, 0), platforms) || IsColliding(position + new Vector2(-1, 0), platforms)) + { + return true; + } + return false; + } + // Collision checks end --------------------------------------------------------------------------------------------------------- + + public void Draw(SpriteBatch spriteBatch) { // Snap position to integer values Vector2 drawPosition = new Vector2((int)position.X, (int)position.Y); spriteBatch.Draw(texture, drawPosition, Color.White); } } - } diff --git a/PlayerInput.cs b/PlayerInput.cs index f853ab9..6e3169e 100644 --- a/PlayerInput.cs +++ b/PlayerInput.cs @@ -27,7 +27,7 @@ namespace LunaLightXMG // If using controller, switch to keyboard by pressing any key if (Keyboard.GetState().GetPressedKeys().Length > 0) { - // Insert your logic to create the objMouseJoyStick instance + // TODO: Insert logic to create the objMouseJoyStick instance Controller = false; } @@ -67,7 +67,7 @@ namespace LunaLightXMG GamePadState gamepadState = GamePad.GetState(PlayerIndex.One); if (gamepadState.IsConnected && gamepadState.Buttons.Start == ButtonState.Pressed) { - // Insert your logic to destroy the objMouseJoyStick instance + // TODO: Insert logic to destroy the objMouseJoyStick instance Controller = true; } @@ -111,5 +111,4 @@ namespace LunaLightXMG } } } - }