using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace BadGame
{
public struct PlayerData
{
public Vector2 Position;
public Vector2 Velocity;
public bool IsAlive;
public bool Win;
public bool CanJump;
}
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
GraphicsDevice device;
SpriteBatch spriteBatch;
SpriteFont font;
Texture2D backgroundTexture;
Texture2D foregroundTexture;
Texture2D playerTexture;
Texture2D rect;
int screenHeight;
int screenWidth;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsFixedTimeStep = false;
}
protected override void Initialize()
{
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 400;
graphics.IsFullScreen = false;
graphics.ApplyChanges();
Window.Title = "Terrible Game";
base.Initialize();
}
PlayerData Player = new PlayerData();
void BuildPlayerData()
{
Player.Position = new Vector2(0, 250); // Start them in the air because it looks/feels cool when a player falls at the start
Player.IsAlive = true;
Player.Win = false;
//Player.Velocity = new Vector2(0.3f, 0); // It defaults to Vector2.Zero (stationary)
Player.CanJump = true;
}
List<Rectangle> Platform = new List<Rectangle>();
void BuildPlatformData()
{
Platform.Add(new Rectangle(100, 210, 150, 30));
Platform.Add(new Rectangle(150, 140, 100, 30));
Platform.Add(new Rectangle(150, 310, 230, 30));
Platform.Add(new Rectangle(230, 280, 150, 30));
Platform.Add(new Rectangle(310, 250, 70, 30));
Platform.Add(new Rectangle(350, 140, 100, 30));
Platform.Add(new Rectangle(400, 230, 150, 30));
Platform.Add(new Rectangle(400, 170, 30, 60));
Platform.Add(new Rectangle(500, 170, 30, 30));
Platform.Add(new Rectangle(600, 130, 30, 300));
Platform.Add(new Rectangle(600, 130, 130, 30));
Platform.Add(new Rectangle(600, 330, 200, 10));
Platform.Add(new Rectangle(700, 250, 100, 30));
Platform.Add(new Rectangle(770, 0, 30, 260));
// Adding new platforms to keep the player confined within the level
// These others are positioned right off the screen so you won't see them
// The only downside would be that these are now DRAWN on the screen (just out of view)
// However, if you ever expand on the project, you would likely check if an element is visible on the screen, and if not, don't draw it :)
// You could do this by settings a rectangle that is the size/position of the screen.. and check if the platforms intersect this screen rect
Platform.Add(new Rectangle(0, 340, 380, 61)); // Bottom boundry (THIS USED TO BE "LEVEL")
Platform.Add(new Rectangle(0, -30, screenWidth, 30)); // Top boundry
Platform.Add(new Rectangle(-30, 0, 30, screenHeight)); // Left boundry
Platform.Add(new Rectangle(screenWidth, 0, 30, screenHeight)); // Right boundry
}
List<Rectangle> Hazard = new List<Rectangle>();
void BuildHazardData()
{
Hazard.Add(new Rectangle(380, 340, 420, 61));
}
protected override void LoadContent()
{
device = graphics.GraphicsDevice;
spriteBatch = new SpriteBatch(GraphicsDevice);
backgroundTexture = Content.Load<Texture2D>("background");
playerTexture = Content.Load<Texture2D>("player");
foregroundTexture = Content.Load<Texture2D>("foreground");
font = Content.Load<SpriteFont>("gameFont");
rect = new Texture2D(GraphicsDevice, 1, 1);
rect.SetData(new[] { Color.White });
screenWidth = device.PresentationParameters.BackBufferWidth;
screenHeight = device.PresentationParameters.BackBufferHeight;
BuildPlayerData();
BuildPlatformData();
BuildHazardData();
}
protected override void UnloadContent()
{
}
void CollisionDetection()
{
// I removed checking collision with LEVEL and added "LEVEL" as a platform.. there is nothing special about it
// And it should really just be treated like any other platform
// I've also removed the screen bounds checking... because if you ever add a camera and have your level "scroll"
// Your code wouldn't be useful anymore. Just add platforms to the screen edges to prevent movement! Then all your code above works with it :)
// Less, more generalized code is the way to do things!
// Figure out the character's destination... collision code will be run based on this position... not the current position!
Rectangle player = new Rectangle((int)Player.Position.X, (int)Player.Position.Y, 28, 28);
Player.CanJump = false; // if they're moving they can't jump until they collide with the top of an object (platform/ground)
// Check horizontal collision
int finalH = (int)(player.X + Player.Velocity.X);
while (true)
{
// We want to move from point A to point B slowly to check for collisions along the way
player.X += Interpolate(player.X, finalH, player.Width);
bool collided = false;
foreach (Rectangle i in Platform)
{
if (player.Intersects(i))
{
collided = true;
if (player.Center.X > i.Center.X)
player.X = i.Right; // Intersecting right
else
player.X = i.Left - player.Width; // intersecting left
}
}
// If we collided with an object or reached our destination with no collision, we're done here
if (collided || player.X == finalH)
break;
}
// Check Vertical collision
int finalV = (int)(player.Y + Player.Velocity.Y);
while (true)
{
// We want to move from point A to point B slowly to check for collisions along the way
player.Y += Interpolate(player.Y, finalV, player.Height);
bool collided = false;
foreach (Rectangle i in Platform)
{
if (player.Intersects(i))
{
collided = true;
if (player.Center.Y > i.Center.Y)
player.Y = i.Bottom; // Intersecting bottom
else
{
player.Y = i.Top - player.Height; // Intersecting top
Player.CanJump = true;
}
}
}
foreach (Rectangle o in Hazard)
{
if (player.Intersects(o))
{
Player.IsAlive = false;
collided = true;
if (player.Center.Y > o.Center.Y)
player.Y = o.Bottom; // Intersecting bottom
else
{
player.Y = o.Top ; // Intersecting top
Player.CanJump = true;
}
}
}
// If we collided with an object or reached our destination with no collision, we're done here
if (collided || player.Y == finalV)
break;
}
// Now that the collisions have been figured out, let's update the player's location to where it should be!
Player.Position = new Vector2(player.X, player.Y);
}
void CheckWin()
{
if (Player.Position.X >= screenWidth - 28) Player.Win = true;
}
int Interpolate(int start, int end, int step)
{
// This function just returns a value starting from START and moving towards END in STEP increments
int difference = Math.Abs(end - start);
int increment = Math.Min(difference, step);
if (start > end)
return -increment;
else
return increment;
}
// We can make this a const since we won't be changing it
const float gravity = 1.25f;
protected override void Update(GameTime gameTime)
{
float elapsedTime = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
float speed = elapsedTime * .3f;
KeyboardState keyboard = Keyboard.GetState();
if (Player.IsAlive && !Player.Win)
{
// Horizontal movement
if (keyboard.IsKeyDown(Keys.A))
Player.Velocity.X += speed*-5;
else if (keyboard.IsKeyDown(Keys.D))
Player.Velocity.X += speed*5;
else
Player.Velocity.X = 0; // Cancel momentum... Stop moving when they let go
// Limit their horizontal velocity
const float maxVelocityH = 5f;
Player.Velocity.X = MathHelper.Clamp(Player.Velocity.X, -maxVelocityH, maxVelocityH);
// Vertical movement
if (keyboard.IsKeyDown(Keys.W) && Player.CanJump)
Player.Velocity.Y += speed*-15;
else if (keyboard.IsKeyDown(Keys.S))
Player.Velocity.Y += speed*1;
// Limit their vertical velocity
const float maxVelocityV = 15f;
Player.Velocity.Y = MathHelper.Clamp(Player.Velocity.Y, -maxVelocityV, maxVelocityV / 2);
// Apply Gravity (downwards force)
Player.Velocity.Y += gravity;
// Collision Detection
CollisionDetection();
CheckWin();
}
if (keyboard.IsKeyDown(Keys.R)) BuildPlayerData();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
DrawLevel();
DrawText();
if (!Player.IsAlive) PlayerDead();
else if (Player.Win) PlayerWin();
spriteBatch.End();
base.Draw(gameTime);
}
private void PlayerDead()
{
//spriteBatch.Draw(rect, new Rectangle(screenWidth / 2, screenHeight / 2, 92, 22), Color.Red);
spriteBatch.DrawString(font, "YOU DIED!\nPress R to restart.", new Vector2(screenWidth / 2, screenHeight / 2), Color.White);
}
private void PlayerWin()
{
spriteBatch.DrawString(font, "YOU WON!\nPress R to restart.", new Vector2(screenWidth / 2, screenHeight / 2), Color.White);
}
private void DrawText()
{
spriteBatch.DrawString(font, String.Format("Position: {0} Gravity: {1} Velocity: {2}", Player.Position, gravity, Player.Velocity), new Vector2(0, 0), Color.White);
}
private void DrawLevel()
{
Rectangle screenRectangle = new Rectangle(0, 0, screenWidth, screenHeight);
spriteBatch.Draw(backgroundTexture, screenRectangle, Color.White);
foreach (Rectangle i in Platform) spriteBatch.Draw(rect, i, Color.Green);
foreach (Rectangle i in Hazard) spriteBatch.Draw(rect, i, Color.Red);
DrawPlayer();
spriteBatch.Draw(foregroundTexture, new Rectangle(0, 0, 380, screenHeight), Color.White);
}
private void DrawPlayer()
{
spriteBatch.Draw(playerTexture, Player.Position, Color.White);
}
}
}