The problem that I am having is wrapping my brain around how I could use a single png called Test Map.png:
This has a black border around the screen and smaller stepping blocks for the player to test the collision. I have the gravity working by using a player class and the main class Game1.cs to draw and update the game. I use this ball:
This is my sprite that I move around the screen.
Here is the player.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Gravity_Test_V2
{
class Player
{
public Texture2D Texture;
public Vector2 Velocity;
public Vector2 Position;
public float ground;
private float Speed;
private Rectangle screenBound;
public bool isJumping; //are we jumping or not
public bool goingUp; //if going up or not
public float initialVelocity; //initial velocity
private float jumpU; //How high the player can jump
private float g; //gravity
public float t; //time
private KeyboardState prevKB;
public Player(Texture2D Texture, Vector2 Position, float Speed, Rectangle screenBound)
{
this.Texture = Texture;
this.Position = Position;
ground = Position.Y;
this.Speed = Speed;
this.screenBound = screenBound;
Velocity = Vector2.Zero;
isJumping = goingUp = true;
jumpU = 2.5f;
g = -9.8f;
t = 0;
}
public void Update(GameTime gameTime)
{
Position.X += (Velocity.X * Speed);
//Set the Y position to be subtracted so that the upward movement would be done by decreasing the Y value
Position.Y -= (Velocity.Y * Speed);
goingUp = (Velocity.Y > 0);
// TODO: Add your update logic here
if (isJumping == true)
{
//motion equation using velocity: v = u + at
Velocity.Y = (float)(initialVelocity + (g * t));
//Increase the timer
t += (float)gameTime.ElapsedGameTime.TotalSeconds;
}
if (isJumping == true && Position.Y > screenBound.Height - Texture.Height)
{
Position.Y = ground = screenBound.Height - Texture.Height;
Velocity.Y = 0;
isJumping = false;
t = 0;
}
if (Position.X < 0)
{
//if Texture touches left side of the screen, set the position to zero and the velocity to zero.
Position.X = 0;
Velocity.X = 0;
}
else if (Position.X + Texture.Width > screenBound.Width)
{
//if Texture touches left side of the screen, set the position to zero and the velocity to zero.
Position.X = screenBound.Width - Texture.Width;
Velocity.X = 0;
}
if (Position.Y < 0)
{
//if the Texture touches the top of the screen, reset the timer and set the initial velocity to zero.
Position.Y = 0;
t = 0;
initialVelocity = 0;
}
}
public void Input(KeyboardState keyState)
{
if (keyState.IsKeyDown(Keys.Space) && (isJumping == false || Position.Y == ground))
{
isJumping = true;
initialVelocity = jumpU;
}
if (keyState.IsKeyDown(Keys.Left) && !keyState.IsKeyDown(Keys.Right))
{
if (Velocity.X > -1.0f)
{
Velocity.X -= (1.0f / 10);
}
else
{
Velocity.X = -1.0f;
}
}
else if (!keyState.IsKeyDown(Keys.Left) && keyState.IsKeyDown(Keys.Right))
{
if (Velocity.X < 1.0f)
{
Velocity.X += (1.0f / 10);
}
else
{
Velocity.X = 1.0f;
}
}
else
{
if (Velocity.X > 0.05 || Velocity.X < -0.05)
Velocity.X *= 0.70f;
else
Velocity.X = 0;
}
prevKB = keyState;
}
public void Fall()
{
t = 0;
initialVelocity = 0;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height), Color.White);
}
}
}
Is there some simple way to make it so the player can collide with the Test Map.png while its inside the Test Map's texture?
EDIT: 1/21/2014 9 AM 'ish'
Level One:
EDIT: 1/21/2014 10:27
I have used a pixle based system to test to see if the player colides with an object but I try to sepreate the object from the play into classes and it will stop working. I mixed both my movment and collision projects together to try and make it work. I took player.cs (witch I have not changed) and added the pixel based collision into the Game1.cs I need to know how to make the player, witch is being controlled by the player.cs class, be seen by the Game1.cs class and used while being called upon by the player.cs class.
Note* I have also changed it so that the game would be using the falling triangles supplied by the pixel based system. I will add the test image when I am able to make this work.
At the moment The player can move and jump but is not reconsidered as colliding.
EDIT: 1/21/2014 10:34
I use 2 projects:
Collision:
http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel
Movment System:
http://gamepopper.co.uk/academic-projects/2012-2/jumping-platformer-example/
I have mixed them up and used pices of them to try and make my own platform.
Game1.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Collision_Test
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
KeyboardState prevKB;
Player player;
SpriteFont font;
Texture2D personTexture;
Texture2D blockTexture;
// The color data for the images; used for per pixel collision
Color[] personTextureData;
Color[] blockTextureData;
Vector2 personPosition;
const int PersonMoveSpeed = 5;
public static int screenWidth = 800;
public static int screenHeight = 500;
// Blocks
List<Vector2> blockPositions = new List<Vector2>();
float BlockSpawnProbability = 0.01f;
const int BlockFallSpeed = 1;
Random random = new Random();
// For when a collision is detected
bool personHit = false;
// The sub-rectangle of the drawable area which should be visible on all TVs
Rectangle safeBounds;
// Percentage of the screen on every side is the safe area
const float SafeAreaPortion = 0.05f;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
this.graphics.PreferredBackBufferWidth = screenWidth;
this.graphics.PreferredBackBufferHeight = screenHeight;
this.graphics.ApplyChanges();
}
protected override void Initialize()
{
base.Initialize();
// Calculate safe bounds based on current resolution
Viewport viewport = graphics.GraphicsDevice.Viewport;
safeBounds = new Rectangle(
(int)(viewport.Width * SafeAreaPortion),
(int)(viewport.Height * SafeAreaPortion),
(int)(viewport.Width * (1 - 2 * SafeAreaPortion)),
(int)(viewport.Height * (1 - 2 * SafeAreaPortion)));
// Start the player in the center along the bottom of the screen
personPosition.X = (safeBounds.Width - personTexture.Width) / 2;
personPosition.Y = safeBounds.Height - personTexture.Height;
}
/// <summary>
/// Load your graphics content.
/// </summary>
protected override void LoadContent()
{
blockTexture = Content.Load<Texture2D>("Block");
personTexture = Content.Load<Texture2D>("Person");
font = Content.Load<SpriteFont>("Font");
player = new Player(personTexture, Vector2.Zero, 6.0f, new Rectangle(0, 0,
this.graphics.PreferredBackBufferWidth,
this.graphics.PreferredBackBufferHeight));
// Extract collision data
blockTextureData =
new Color[blockTexture.Width * blockTexture.Height];
blockTexture.GetData(blockTextureData);
personTextureData =
new Color[personTexture.Width * personTexture.Height];
personTexture.GetData(personTextureData);
// Create a sprite batch to draw those textures
spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
}
void HandleInput(KeyboardState keyState)
{
player.Input(keyState);
if (prevKB.IsKeyUp(Keys.F) && keyState.IsKeyDown(Keys.F))
{
this.graphics.ToggleFullScreen();
this.graphics.ApplyChanges();
}
}
protected override void Update(GameTime gameTime)
{
// Get input
KeyboardState keyboard = Keyboard.GetState();
GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
HandleInput(Keyboard.GetState());
player.Update(gameTime);
prevKB = Keyboard.GetState();
// Allows the game to exit
if (gamePad.Buttons.Back == ButtonState.Pressed ||
keyboard.IsKeyDown(Keys.Escape))
{
this.Exit();
}
// Spawn new falling blocks
if (random.NextDouble() < BlockSpawnProbability)
{
float x = (float)random.NextDouble() *
(Window.ClientBounds.Width - blockTexture.Width);
blockPositions.Add(new Vector2(x, -blockTexture.Height));
}
// Get the bounding rectangle of the person
Rectangle personRectangle =
new Rectangle((int)personPosition.X, (int)personPosition.Y,
personTexture.Width, personTexture.Height);
// Update each block
personHit = false;
for (int i = 0; i < blockPositions.Count; i++)
{
// Animate this block falling
blockPositions[i] =
new Vector2(blockPositions[i].X,
blockPositions[i].Y + BlockFallSpeed);
// Get the bounding rectangle of this block
Rectangle blockRectangle =
new Rectangle((int)blockPositions[i].X, (int)blockPositions[i].Y,
blockTexture.Width, blockTexture.Height);
// Check collision with person
if (IntersectPixels(personRectangle, personTextureData,
blockRectangle, blockTextureData))
{
personHit = true;
}
// Remove this block if it have fallen off the screen
if (blockPositions[i].Y > Window.ClientBounds.Height)
{
blockPositions.RemoveAt(i);
// When removing a block, the next block will have the same index
// as the current block. Decrement i to prevent skipping a block.
i--;
}
}
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice device = graphics.GraphicsDevice;
// Change the background to red when the person was hit by a block
if (personHit)
{
device.Clear(Color.Red);
}
else
{
device.Clear(Color.CornflowerBlue);
}
spriteBatch.Begin();
player.Draw(spriteBatch);
// Draw blocks
foreach (Vector2 blockPosition in blockPositions)
spriteBatch.Draw(blockTexture, blockPosition, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
/// <summary>
/// Determines if there is overlap of the non-transparent pixels
/// between two sprites.
/// </summary>
/// <param name="rectangleA">Bounding rectangle of the first sprite</param>
/// <param name="dataA">Pixel data of the first sprite</param>
/// <param name="rectangleB">Bouding rectangle of the second sprite</param>
/// <param name="dataB">Pixel data of the second sprite</param>
/// <returns>True if non-transparent pixels overlap; false otherwise</returns>
static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
Rectangle rectangleB, Color[] dataB)
{
// Find the bounds of the rectangle intersection
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
// Check every point within the intersection bounds
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
// Get the color of both pixels at this point
Color colorA = dataA[(x - rectangleA.Left) +
(y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) +
(y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
// No intersection found
return false;
}
}
}
Provided the background of the texture is transparent, you could use Per-Pixel Collision detection.
Essentially it checks the pixels rather than a rectangular box to determine if a collision has occurred. Given your "player" is a ball, it's probably a good idea to use this anyway.
Considering that your map is only black & White, you could process it before starting the game and obtain coords of every Rectangle of black areas, and then use it to use simple collision detection by just checking intersection between the ball bounding box and rectangles.
For example, in pseudocode:
You have a list:
List<Rectangle> rects = new List<Rectangle>();
void LoadMap()
{
Texture2D map = Content.Load<Texture2D>(#"TexturePath");
//Get a 1D array with image's pixels
Color[] map_pixels = new Color[map.Width * map.Height];
map.GetData(map_pixels);
//Convert it in a 2D array
Color[,] map_pixels_2D = new Color[map.Width, map.Height];
for (int x = 0; x < map.Width; x++)
for (int y = 0; y < map.Height; y++)
map_pixels_2D[x, y] = map_pixels[x + y * map.Width];
//**NOTE THAT**: From here it is just an example, probably not working good,
//I wrote it just to share the idea
//Here goes the code to trace rectangles in the map
Rectangle r = Rectangle.Empty;
bool NWvertex_done = false, NEvertex_done = false, SWvertex_done = false;
for (int x = 0; x < map.Width; x++)
{
if (!SWvertex_done)
{
if (map_pixels_2D[x, y+1] == Color.White); //last bottom vertex
{
r.Height = r.Y + y;
SWvertex_done = true;
rects.Add(r);
NWvertex_done = false;
NEvertex_done = false;
r = Rectangle.Empty;
}
}
for (int y = 0; y < map.Height; y++)
{
if (map_pixels_2D[x, y] != Color.White
{
if (!NWvertex_done)
{
SWvertex_done = false;
r.X = x;
r.Y = y;
NWvertex_done = true;
}
else if(!NEvertex_done)
{
if (map_pixels_2D[x, y+1] == Color.White); //last right vertex
{
r.Width = r.X + x;
NEvertex_done = true;
}
}
}
}
}
}
public override void Update(GameTime gametime)
{
//maybe other things
//
foreach (Rectangle rect in rects)
{
//Better with Distance of ball-center and rect
if (ballRect.Intersect(rect))
{
//there is a collision!
//Do something
}
break;
}
//maybe other things
//
}
Related
Currently, when you run my code, a square sprite draws whenever and wherever you click the mouse. They fall with gravity. There is also a static black square in the screen to test for collisions, a red square also appears to show that the collision returned true just for the meantime to test the code. However, it totally doesn't work and the red square appears at the wrong time.
private SpriteBatch _spriteBatch;
public Vector2 Origin;
public float Rotation;
Texture2D staticSprite; //static sprite used to check collision with moving particle("white circle")
//white circle gets generated and added to particle list whenever mouse is clicked
List<Particle> particleList = new List<Particle>();
public bool mReleased = true; //mouse button released
public Vector2 clickedMousePos = Vector2.Zero; //default mouse click position
bool collision = false; // no collision by default
Rectangle tTextureRectangle = new Rectangle(200, 200, 100, 100);// rectangle placed for
Rectangle particleRect;
//public int particleIndex = 0; **code adding later**
MouseState mState = Mouse.GetState();
//PerPixelCollision
Color[] stiffSpriteData;
Color[] tTextureData;
// intersect method takes two rectangles and color data to check for collision
static bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
{
// Find the bounds of the rectangle intersection
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
// Check every point within the intersection bounds
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
// Get the color of both pixels at this point
Color colorA = dataA[(x - rectangleA.Left) +
(y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) +
(y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
// No intersection found
return false;
}
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void Initialize()
{
_graphics.PreferredBackBufferWidth = 1280;
_graphics.PreferredBackBufferHeight = 720;
_graphics.ApplyChanges();
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
staticSprite = Content.Load<Texture2D>("whiteCircle");
//PerPixelCollision
stiffSpriteData = new Color[staticSprite.Width * staticSprite.Height];
staticSprite.GetData<Color>(stiffSpriteData);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
collision = false;
mState = Mouse.GetState();
clickedMousePos = new Vector2 (mState.Position.X, mState.Position.Y); // updated mouse position when clicked
//draws a particle ("white circle") with garvity and adds to particleList
if (mState.LeftButton == ButtonState.Pressed && mReleased == true)
{
particleList.Add(new Particle(Content.Load<Texture2D>("whiteCircle"), new Vector2(0,1), new Vector2(clickedMousePos.X, clickedMousePos.Y), 1f));
var listSize = particleList.Count - 1;
//PerPixelCollision
tTextureData = new Color[particleList[listSize].texture.Width * particleList[listSize].texture.Height];
particleList[listSize].texture.GetData<Color>(tTextureData);
//Rectangle generates for first moving particle in list for now
particleRect = new Rectangle((int)particleList[listSize].Position.X, (int)particleList[listSize].Position.Y, particleList[0].texture.Width, particleList[0].texture.Height);
mReleased = false;
}
if (mState.LeftButton == ButtonState.Released)
{
mReleased = true;
}
foreach (Particle particle in particleList)
{
particle.Update(gameTime);
}
// Check collision with person
if (IntersectPixels(tTextureRectangle, tTextureData, particleRect, stiffSpriteData))
{
collision = true;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
_spriteBatch.Draw(staticSprite, new Vector2(200, 200), null, Color.Black, Rotation, Origin, 0.1f, SpriteEffects.None, 0);
foreach (Particle particle in particleList)
{
particle.Draw(_spriteBatch);
}
// this is to test wether a collision was actually detected
if (collision == true)
{
_spriteBatch.Draw(staticSprite, new Vector2(500, 500), null, Color.Red, Rotation, Origin, 0.1f, SpriteEffects.None, 0);
}
_spriteBatch.End();
base.Draw(gameTime);
}
The core of your problem(among too many others) is the particleRect variable should not exist in Game1, but needs to reference each particle instance (it needs to be referenced from a foreach loop of each particle).
I have annotated your code with //... comments.
The new Game1.cs:
private SpriteBatch _spriteBatch;
// ...Objection; it is poor form to treat Game1 as a game object, as it makes, among many other things, menus near impossible.
// ... The only variables that should be defined here are those pertinent to the entire game (names, difficulty level, score, lives, avatar, level, hasWon, counters for timed events(I would segment per level, but still appropriate here), draw offsets, matrix transforms, aspect ratios ...)
public Vector2 Origin;
public float Rotation;
// ... Nothing in Game1 should have the above properties, but so be it for the minimal example.
Texture2D staticSprite; //static sprite used to check collision with moving particle("white circle")
// ... same objection as above
//white circle gets generated and added to particle list whenever mouse is clicked
// ...When is it removed?
List<Particle> particleList = new List<Particle>();
// ... Lists cannot be modified while enumerated by a foreach
List<Particle> particleListRemove = new List<Particle>();
// bool collision = false; // no collision by default
// ... Collision needs to be handled per particle.
Rectangle tTextureRectangle = new Rectangle(200, 200, 100, 100);// rectangle placed for
//... Rectangle particleRect;
//public int particleIndex = 0; **code adding later**
// ... bad for this example, see above particleListRemove list with this pattern, and below
MouseState mState = Mouse.GetState();
MouseState omState;
// ... having the previous state is much better than a single button
//PerPixelCollision
Color[] stiffSpriteData; // ... I get the fixed box, may be better as described in the above objection.
// ... tTextureData data is not needed across calls and should be defined locally
// Color[] tTextureData;
// intersect method takes two rectangles and color data to check for collision
// ... I have stopped here.
//static bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
// {
// Check every point within the intersection bounds
// for (int y = top; y < bottom; y++)
// {
// for (int x = left; x < right; x++)
// {
// // Get the color of both pixels at this point
// Color colorA = dataA[(x - rectangleA.Left) +
// (y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) +
// (y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent,
// if (colorA.A != 0 && colorB.A != 0)
// {
// then an intersection has been found
// return true;
// }
// }
// }
// No intersection found
return false;
}
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void Initialize()
{
_graphics.PreferredBackBufferWidth = 1280;
_graphics.PreferredBackBufferHeight = 720;
_graphics.ApplyChanges();
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
staticSprite = Content.Load<Texture2D>("whiteCircle");
//PerPixelCollision
stiffSpriteData = new Color[staticSprite.Width * staticSprite.Height];
staticSprite.GetData<Color>(stiffSpriteData);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
collision = false;
mState = Mouse.GetState();
clickedMousePos = new Vector2 (mState.Position.X, mState.Position.Y); // updated mouse position when clicked
//draws a particle ("white circle") with garvity and adds to particleList
if (mState.LeftButton == ButtonState.Pressed && mReleased == true)
{
particleList.Add(new Particle(Content.Load<Texture2D>("whiteCircle"), new Vector2(0,1), new Vector2(clickedMousePos.X, clickedMousePos.Y), 1f));
var listSize = particleList.Count - 1;
//PerPixelCollision
tTextureData = new Color[particleList[listSize].texture.Width * particleList[listSize].texture.Height];
particleList[listSize].texture.GetData<Color>(tTextureData);
//Rectangle generates for first moving particle in list for now
particleRect = new Rectangle((int)particleList[listSize].Position.X, (int)particleList[listSize].Position.Y, particleList[0].texture.Width, particleList[0].texture.Height);
mReleased = false;
}
if (mState.LeftButton == ButtonState.Released)
{
mReleased = true;
}
foreach (Particle particle in particleList)
{
particle.Update(gameTime);
}
// Check collision with person
if (IntersectPixels(tTextureRectangle, tTextureData, particleRect, stiffSpriteData))
{
collision = true;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
_spriteBatch.Draw(staticSprite, new Vector2(200, 200), null, Color.Black, Rotation, Origin, 0.1f, SpriteEffects.None, 0);
foreach (Particle particle in particleList)
{
particle.Draw(_spriteBatch);
}
// this is to test wether a collision was actually detected
if (collision == true)
{
_spriteBatch.Draw(staticSprite, new Vector2(500, 500), null, Color.Red, Rotation, Origin, 0.1f, SpriteEffects.None, 0);
}
_spriteBatch.End();
base.Draw(gameTime);
}
Your Rectangle intersection code never checks to see if there was an actual collision. Replace:
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
With:
// not colliding check
if (!rectangleA.Intersects(rectangleB)) return false;
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
'''
So I'm trying to figure out how to split the rectangle of my paddle up so i can define different points on the paddle at which the ball can be hit. I want to make it so that if the ball is hit on either the left or right side of the paddle, then the ball direction will change slightly rather than following the exact same path every time.
Here is my paddle class (I started writing the code I would need in the isboxcolliding method but got stuck and confused):
class Ball
{
Texture2D texture;
Vector2 position;
Vector2 speed;
Rectangle bounds;
Rectangle screenBounds;
Random rnd = new Random();
bool collisionWithBrick; // Prevents rapid brick removal.
public Ball(Texture2D texture, Rectangle screenbounds) // Constructor
{
this.texture = texture;
this.screenBounds = screenbounds;
}
public int Width
{
get { return texture.Width; }
}
public int Height
{
get { return texture.Height; }
}
public Rectangle Bounds
{
get
{
bounds = new Rectangle((int)position.X, (int)position.Y,
Width, Height);
return bounds;
}
}
public void SetStartBallPosition(Rectangle paddle)
{
position.X = paddle.X + (paddle.Width - Width) / 2;
position.Y = paddle.Y = paddle.Y - Height;
bounds = Bounds;
if (rnd.Next(0, 2) < 1)
speed = new Vector2(-200.0f, 200.0f); // Move ball left.
else
speed = new Vector2(200.0f, -200.0f); // Move Ball Right.
}
internal void Deflection(MultiBallBrick multiBallBrick)
{
if (!collisionWithBrick)
{
speed.Y *= -1;
collisionWithBrick = true;
}
}
internal void Deflection(ReinforcedBrick reinforcedBrick)
{
if (!collisionWithBrick)
{
speed.Y *= -1;
collisionWithBrick = true;
}
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.Silver);
}
// Check if our ball collides with the right paddle.
public void IsBoxColliding(Rectangle paddle)
{
if (bounds.Intersects(paddle))
{
int center = paddle.Center.X - bounds.Center.X;
if (center > )
{
speed.Y *= -1;
speed.X = speed.X * 1.5f;
}
}
/*if (paddle.Intersects(bounds)) // Bounds = our ball
{
position.Y = paddle.Y - Height;
speed.Y *= - 1; // Reverse the direction of the ball.
}*/
}
public void Update(GameTime gameTime)
{
collisionWithBrick = false;
position += speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
// Check to see if the ball goes of the screen.
if (position.X + Width > screenBounds.Width) // Right side
{
speed.X *= -1;
position.X = screenBounds.Width - Width;
}
if (position.X < 0) // Left side
{
speed.X *= -1;
position.X = 0;
}
if (position.Y < 0) // Top
{
speed.Y *= -1;
position.Y = 0;
}
}
public bool OffBottom()
{
if (position.Y > screenBounds.Height)
return true;
return false;
}
public void Deflection(Brick brick)
{
if (!collisionWithBrick)
{
speed.Y *= -1;
collisionWithBrick = true;
}
}
}
}
any help would be greatly apreciated!
You do not necessarily need to split the collision rectangle up. You can check if the collision is on the left or right side by having the following information.
The collision point or the centre position of the ball when the collision occurs, lets call it Vector2 collisionPoint
Centre position of the paddle, lets call it Vector2 paddleCentrePosition
This only works if the paddle does not rotate.
Vector2 difference = collisionPoint - paddleCentrePosition;
if(difference.x > 0) {
/*Right side of the paddle*/
}
else if(difference.x < 0) {
/*Left side of the paddle*/
}
else{
/*The exact same position*/
}
Hello stackoverflow!
I'm working on a 2D sidescroller adventure game in C# using MonoGame, and I've come upon a peculiar problem.
In short: Player instance has assigned a Texture2D containing the player's texture (in this example a blue box with a 2px green border)
When player moves, texture "stays in place" and player gradually becomes green as he leaves the space where the texture is.
This is of course not supposed to happen, as the texture is supposed to be redrawn at Player's exact location everytime Draw() is called.
In other words - the player should still be a blue box with a 2px green border no matter where he moves.
Images for better illustration:
Original state
Player moves downward, leaving texture behind.
Until he is completely green.
And when he comes back to his original position, the correct texture starts to show again
I've cut down the code to it's bare bones, but the issue is still persisting.
Game.cs contains nothing interesting, Player initialization and Draw method here:
//inside of Initialize()
player = new Player(GraphicsDevice, Vector2.Zero);
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
levelSpriteBatch.Begin();
player.Draw(gameTime, levelSpriteBatch);
levelSpriteBatch.End();
base.Draw(gameTime);
}
Player.cs (whole file) here:
class Player {
public Vector2 Position
{
get { return position; }
}
Vector2 position;
public Texture2D Texture { get; private set; }
public const int Width = 32;
public const int Height = 32;
// Input configuration
private const Keys leftButton = Keys.A;
private const Keys rightButton = Keys.D;
private const Keys upButton = Keys.W;
private const Keys downButton = Keys.S;
//Current state
private float LRmovement;
private float UDmovement;
public Player(GraphicsDevice graphicsDevice, Vector2 position) {
this.position = position;
CreateTexture(graphicsDevice, Width, Height);
}
public void Update(GameTime gameTime, KeyboardState keyboardState) {
GetInput(keyboardState);
position.X += LRmovement;
position.Y += UDmovement;
LRmovement = 0.0f;
UDmovement = 0.0f;
}
private void CreateTexture(GraphicsDevice graphicsDevice, int width, int height) {
Texture = new Texture2D(graphicsDevice, width, height);
GraphicsHelper.FillRectangle(Texture, Color.Red);
GraphicsHelper.OutlineRectangle(Texture, Color.Green, 2);
}
private void GetInput(KeyboardState keyboardState) {
LRmovement = 0;
UDmovement = 0;
if (Math.Abs(LRmovement) < 0.5f)
LRmovement = 0.0f;
if (Math.Abs(UDmovement) < 0.5f)
UDmovement = 0.0f;
if (keyboardState.IsKeyDown(leftButton))
LRmovement = -1.0f;
else if (keyboardState.IsKeyDown(rightButton))
LRmovement = 1.0f;
if (keyboardState.IsKeyDown(upButton))
UDmovement = -1.0f;
else if (keyboardState.IsKeyDown(downButton))
UDmovement = 1.0f;
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch) {
spriteBatch.Draw(Texture, position, new Rectangle((int)Position.X, (int)Position.Y, Width, Height), Color.White);
}
}
And GraphicsHelper.cs:
class GraphicsHelper {
public static void FillRectangle(Texture2D texture, Color fill) {
Color[] color = new Color[texture.Width * texture.Height];
texture.GetData(color);
for (int i = 0; i < texture.Width * texture.Height; ++i)
color[i] = fill;
texture.SetData(color);
}
public static void OutlineRectangle(Texture2D texture, Color outline, int outlineWidth) {
Color[] color = new Color[texture.Width * texture.Height];
texture.GetData(color);
int index = 0;
for (int y = 0; y < texture.Height; ++y) {
for (int x = 0; x < texture.Width; ++x) {
if (y < outlineWidth || x < outlineWidth || y > texture.Height - outlineWidth || x > texture.Width - outlineWidth)
color[index] = outline;
++index;
}
}
texture.SetData(color);
}
}
This is ALL THE CODE THERE IS. I'm honestly out of ideas.
This is the problematic line:
spriteBatch.Draw(Texture, position, new Rectangle((int)Position.X, (int)Position.Y, Width, Height), Color.White);
The third parameter (the rectangle) defines which part of the texture to draw. And you always want to draw the same part of the texture, hence you should pass a constant rectangle.
Actually, if you just want to draw the entire texture, pass null:
spriteBatch.Draw(Texture, position, null, Color.White);
In my Boss class, it says that my List "bulletlist" is null and give me a run time error but I see no errors can someone tell me what I am doing wrong? The games runs fine if I don't update my boss class. I put in my main class, player class, and bullet class, in as well.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Space_Shooter
{
public class Boss
{
public Texture2D texture, bulletTexture , healthBarTexture;
public Vector2 position;
public float randomPositionX, randomPositionY , bulletDamage, shipDamage , health , MaxBullets;
public int speed, bulletSpeed , bulletDelayTime, bulletDelayTimeReset;
public bool isVisible;
public Rectangle boundingBox , bossHealthBar;
public List<Bullet> bulletList;
Random random = new Random();
Player p = new Player();
public Boss(Texture2D newTexture, Vector2 newPosition, Texture2D newBulletTexture)
{
texture = newTexture;
position = newPosition;
bulletTexture = newBulletTexture;
bulletList = new List<Bullet>();
bulletSpeed = 25;
bulletDamage = 25;
shipDamage = p.health / 2;
health = 2000;
bulletDelayTime = 30;
bulletDelayTimeReset = 30;
MaxBullets = 30;
isVisible = true;
}
public Boss()
{
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.White);
spriteBatch.Draw(healthBarTexture, bossHealthBar, Color.White);
foreach (Bullet b in bulletList)
{
b.Draw(spriteBatch);
}
}
public void loadContent(ContentManager content)
{
texture = content.Load<Texture2D>("Ship4");
bulletTexture = content.Load<Texture2D>("EnemyBullet");
healthBarTexture = content.Load <Texture2D> ("healthbar");
}
public void update(GameTime gameTime)
{
updateBullets();
Shoot();
checkBossCollison();
// Making bosses health bar
bossHealthBar = new Rectangle((int)position.X, (int)position.Y + 30, (int)health, 25);
// Creating collision box for boss
boundingBox = new Rectangle((int)position.X, (int)position.Y, (int)texture.Width, (int)texture.Height);
randomPositionX = random.Next(50, 750);
randomPositionY = random.Next(50, 400);
// setting boss movement
if (position.Y > randomPositionY)
position.Y -= speed;
if (position.Y < randomPositionY)
position.Y += speed;
if (position.X > randomPositionX)
position.X -= speed;
if (position.X < randomPositionX)
position.X += speed;
}
public void updateBullets()
{
// for each bullet in the bulletList: update the movement and if the bullet hits the bottom of the screen, remove it from the list
foreach (Bullet b in bulletList)
{
//make a Bounding box for every bullet in the bulletList
b.boundingBox = new Rectangle((int)b.position.X, (int)b.position.Y, b.texture.Width, b.texture.Height);
// set movement for the bullets
// if a bullet hits the bottom of the screen, then make visable false
if (b.position.Y >= 850)
b.isVisible = false;
// go through the bulletList and see if any of the bullets are not visible, if they are not then remove that bullet from the bulletList
for (int i = 0; i < bulletList.Count; i++)
{
if (!bulletList[i].isVisible)
{
bulletList.RemoveAt(i);
i--;
}
}
}
}
public void checkBossCollison()
{
// if boss ship comes in bcontact with player ship player is damaged
if (boundingBox.Intersects(p.boundingBox))
p.health -= shipDamage;
// if player bullets hit boss then boss is damaged and player bullets go invisible
for(int i = 0; i < p.bulletList.Count; i++)
{
if (boundingBox.Intersects(p.bulletList[i].boundingBox))
{
health -= p.bulletDamage;
p.bulletList[i].isVisible = false;
if (health == 0)
isVisible = false;
}
}
// if boss shoots player player is damaged and boss bullets go invisible
for(int i = 0; i < bulletList.Count; i++)
{
if (p.boundingBox.Intersects(bulletList[i].boundingBox))
{
p.health -= bulletDamage;
bulletList[i].isVisible = false;
}
}
}
public void Shoot()
{
// shoot only if our bullet delay resets
if (bulletDelayTime >= 0)
bulletDelayTime--;
// If bulletDelay is at 0, create a new bullet at enemy position, make it visible on the screen, then add that bullet to bulletList
if (bulletDelayTime <= 0)
{
// create new bullet and center it front and center of enemy ship
Bullet newBullet = new Bullet(bulletTexture);
newBullet.position = new Vector2(position.X + texture.Width / 2 - newBullet.texture.Width / 2, position.Y + 30);
newBullet.isVisible = true;
if (bulletList.Count() < MaxBullets)
bulletList.Add(newBullet);
}
// Reset bullet delay
if (bulletDelayTime == 0)
bulletDelayTime = bulletDelayTimeReset;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Space_Shooter
{
// Main
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
// variabls
SpriteBatch spriteBatch;
Random random = new Random();
Random randomNumberOfAsteroids = new Random();
int maxNumberOfAsteroids;
int numbersOfInvisibleEnemies;
// Lists
List<Asteroid> asteroidList = new List<Asteroid>();
List<Enemy> enemyList = new List<Enemy>();
// making player and starfield objects
Player P = new Player();
StarField starfield = new StarField();
Enemy E = new Enemy();
Boss boss = new Boss();
// Construtor
public Game1()
{
graphics = new GraphicsDeviceManager(this);
// setting full scren to off and setting screen measurments
graphics.IsFullScreen = false;
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 850;
maxNumberOfAsteroids = 5; // Set the max number of asteroids that will be on the screen at one time ( randomly 0 to Max number)
numbersOfInvisibleEnemies = 0;
// Setting Title of screen
this.Window.Title = "Star Voyage";
Content.RootDirectory = "Content";
}
// Initialize
protected override void Initialize()
{
base.Initialize();
}
// Load content
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Call Load content function from Player.cs
P.LoadContent(Content);
starfield.LoadContent(Content);
boss.loadContent(Content);
}
// unload content
protected override void UnloadContent()
{
}
// update game screen
protected override void Update(GameTime gameTime)
{
// updating enemies and checking collision of thier ships to player ship
foreach (Enemy e in enemyList)
{
if (e.boundingBox.Intersects(P.boundingBox))
{
P.health -= e.enemyShipDamage;
e.isVisible = false;
}
// check enemy bullet collision with player ship
for (int i = 0; i < e.bulletList.Count; i++)
{
if (P.boundingBox.Intersects(e.bulletList[i].boundingBox))
{
P.health -= e.enemyBulletDamage;
e.bulletList[i].isVisible = false;
}
}
// check player bullet collision with enemy ships
for (int i = 0; i < P.bulletList.Count; i++)
{
if (e.boundingBox.Intersects(P.bulletList[i].boundingBox))
{
// when bullet hits enemy ship they disapear
P.bulletList[i].isVisible = false;
e.health -= P.bulletDamage;
if (e.health == 0)
e.isVisible = false;
}
}
e.Update(gameTime);
}
// for each asteroid in the asteroid list call the update function from Asteroid.cs
foreach (Asteroid a in asteroidList)
{
//Check to see if any of the arteroids are colliding with player ship
if (a.boundingBox.Intersects(P.boundingBox))
{
P.health -= a.asteroidDamage; // taking 20 off of player health bar each time the play shit is hit by an asteroid
a.isVisible = false;
}
// going through bullet list to see if any bullets come in contact with asteroids if they do set both the bullet and the asteroid to not visible
for (int i = 0; i < P.bulletList.Count; i++)
{
if (a.boundingBox.Intersects(P.bulletList[i].boundingBox))
{
a.isVisible = false;
P.bulletList.ElementAt(i).isVisible = false;
}
}
a.Update(gameTime);
}
loadAsteroids();
loadEnemyShips();
// Call fucntion Update from Player,cs
P.Update(gameTime);
// Call function update from StarField.cs
starfield.Update(gameTime);
boss.update(gameTime);
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
// Draw to game screen
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
starfield.Draw(spriteBatch);
foreach (Asteroid a in asteroidList)
{
a.Draw(spriteBatch);
}
foreach (Enemy e in enemyList)
{
e.Draw(spriteBatch);
}
// Backgound color
GraphicsDevice.Clear(Color.CornflowerBlue);
// Call Draw fuction from Player.cs
P.Draw(spriteBatch);
// boss.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
// Load Asteroids function
public void loadAsteroids()
{
// creating random variabls for X and Y positions of asteroids
int random_x = random.Next(0, 750), random_y = random.Next(-600, -50);
// if the number of Asteroids on the screen is less than the variable (numberOfAsteroids), create more untill it is not
if (asteroidList.Count() < randomNumberOfAsteroids.Next(0, maxNumberOfAsteroids))
{
asteroidList.Add(new Asteroid(Content.Load<Texture2D>("asteroid"), new Vector2(random_x, random_y)));
}
// if any of the asteroids from the list are destroyed (not visible) then remove them from the list
for (int i = 0; i < asteroidList.Count; i++)
{
if (!asteroidList[i].isVisible)
{
asteroidList.RemoveAt(i);
i--;
}
}
}
public void loadEnemyShips()
{
// creating random variabls for X and Y positions of asteroids
int random_x = random.Next(0, 750), random_y = random.Next(-600, -50);
// if the number of enemies on the screen is less than the variable (numberOfEnemies), create more untill it is not
if (enemyList.Count() < 3 && numbersOfInvisibleEnemies <= 9)
{
enemyList.Add(new Enemy(Content.Load<Texture2D>("enemyship"), new Vector2(random_x, random_y), Content.Load<Texture2D>("EnemyBullet")));
}
// keeping count of the number of destroied enemies
for (int i = 0; i < enemyList.Count; i++)
{
if (!enemyList[i].isVisible)
numbersOfInvisibleEnemies += 1;
// if any of the enemies from the list are destroyed (not visible) or if they go passed the bottom of the screen, then remove them from the list
if (!enemyList[i].isVisible || enemyList[i].position.Y >= 840)
{
enemyList.RemoveAt(i);
--i;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Space_Shooter
{
// main
public class Player
{
// Variables
public Texture2D texture , bulletTexture , healthTexture;
public Vector2 position , healthBarPosition;
public int speed;
public float health , bulletDamage , bulletDelay , bulletDelayReset;
double bulletCount;
// Lists
public List<Bullet> bulletList;
// Collision variables
public bool isColliding;
public Rectangle boundingBox , healthRectangle;
// Constructor
public Player()
{
bulletList = new List<Bullet>();
bulletDelay = 1; // for faster fire rate reduce number
bulletDelayReset = 5; // bullet delay reset has to be the same number as bullet delay
bulletCount = 1000; // amount of bullets alowed on the screen at a time
texture = null;
health = 200;
bulletDamage = 3;
healthBarPosition = new Vector2(50, 50);
position = new Vector2 (300, 300) ;
speed = 10;
isColliding = false;
}
// Load content
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>("player01");
bulletTexture = Content.Load<Texture2D>("playerbullet");
healthTexture = Content.Load<Texture2D>("healthbar");
}
// Draw
public void Draw(SpriteBatch spritebatch)
{
spritebatch.Draw(texture, position, Color.White);
spritebatch.Draw(healthTexture, healthRectangle, Color.White);
// For each Bullet b (bullet) in our bullet list, draw the bullet
foreach (Bullet b in bulletList)
{
b.Draw(spritebatch);
}
}
// Update
public void Update(GameTime gametime)
{
// Checking for keyboard input every frame
KeyboardState keystate = Keyboard.GetState();
// setting collision box for player ship
boundingBox = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
//set rectangle for healthbar (where to print on the screen and the height and width (25) is height (health is width
healthRectangle = new Rectangle((int) healthBarPosition.X, (int) healthBarPosition.Y, (int)health, 25);
if (keystate.IsKeyDown(Keys.Space))
{
shoot();
}
updateBullets();
// ship controls
if (keystate.IsKeyDown(Keys.W))
position.Y = position.Y - speed;
if (keystate.IsKeyDown(Keys.S))
position.Y = position.Y + speed;
if (keystate.IsKeyDown(Keys.A))
position.X = position.X - speed;
if (keystate.IsKeyDown(Keys.D))
position.X = position.X + speed;
// Keep ship in screen bounds
if (position.X <= 0)
position.X = 0;
if (position.X >= 800 - texture.Width)
position.X = 800 - texture.Width;
if (position.Y <= 0)
position.Y = 0;
if (position.Y >= 850 - texture.Height)
position.Y = 850 - texture.Height;
}
// Shoot Function, used to set the starting position of the players bullet
public void shoot()
{
// Shoot only if the bullet delay resets
if (bulletDelay >= 0)
bulletDelay--;
// If bulletDelay is at 0, create a new bullet at player position, make it visible on the scrren, then add that bullet to bulletList
if (bulletDelay <= 0)
{
Bullet newBullet1 = new Bullet(bulletTexture);
newBullet1.position = new Vector2(position.X - newBullet1.texture.Width / 2, position.Y + 30);
newBullet1.isVisible = true;
Bullet newBullet2 = new Bullet(bulletTexture);
newBullet2.position = new Vector2(position.X + 64 - newBullet2.texture.Width / 2, position.Y + 30);
newBullet2.isVisible = true;
// control the amount of bullets on the screen by increasing number
if (bulletList.Count() < bulletCount)
bulletList.Add(newBullet1);
bulletList.Add(newBullet2);
}
// Reset bullet dleay
if (bulletDelay == 0)
bulletDelay = bulletDelayReset;
}
public void updateBullets()
{
// for each bullet in the bulletList: update the movement and if the bullet hits the top of the screen, remove it from the list
foreach (Bullet b in bulletList)
{
// set movement for the bullets
b.position.Y = b.position.Y - b.speed;
//make a Bounding box for every bullet in the bullet list
b.boundingBox = new Rectangle((int)b.position.X, (int)b.position.Y, b.texture.Width, b.texture.Height);
// if a bullet hits the top of the screen, then make visable false
if (b.position.Y <= 0)
b.isVisible = false;
}
// go through the bulletList and see if any of the bullets are not visible, if they are not then remove that bullet from the bulletList
for (int i = 0; i < bulletList.Count; i++)
{
if (!bulletList[i].isVisible)
{
bulletList.RemoveAt(i);
i--;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Space_Shooter
{
public class Bullet
{
public Rectangle boundingBox;
public Texture2D texture;
public Vector2 position , origin;
public float speed;
public bool isVisible;
public Bullet(Texture2D newTexture)
{
speed = 25;
texture = newTexture;
isVisible = false;
}
public void Draw(SpriteBatch spritebatch)
{
spritebatch.Draw(texture, position, Color.White);
}
}
}
You initialize the list in the constructor:
public Boss(Texture2D newTexture, Vector2 newPosition, Texture2D newBulletTexture)
But, you don't call this constructor. You call Boss boss = new Boss();
Thus the constructor which initializes the list is not called.
You should initialize the list in all constructors:
public Boss()
{
bulletList = new List<Bullet>();
}
Better is too make the default constructor set all the default values, and if you need to set other values in another constructor, is to call the default constructor from the "second" constructor using : this():
public Boss()
{
bulletList = new List<Bullet>();
bulletSpeed = 25;
bulletDamage = 25;
shipDamage = p.health / 2;
health = 2000;
bulletDelayTime = 30;
bulletDelayTimeReset = 30;
MaxBullets = 30;
isVisible = true;
}
public Boss(Texture2D newTexture, Vector2 newPosition, Texture2D newBulletTexture)
: this() //Call default constructor
{
texture = newTexture;
position = newPosition;
bulletTexture = newBulletTexture;
}
I am trying to create a spaceship with multiple animations (idle, attack, damaged, etc)
But when I'm trying to change the animations I see the first frame of the current animation for a split second on the "old" position (where the animation was changed the last time)
Picture for explanation: http://i.imgur.com/L3O8rsc.png
The red rectangle is supposed to be a health bar.
The class that runs my animations:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
namespace Test
{
class Animation
{
Texture2D spriteStrip;
float scale;
int elapsedTime;
int frameTime;
int frameCount;
int currentFrame;
Color color;
// The area of the image strip we want to display
Rectangle sourceRect = new Rectangle();
// The area where we want to display the image strip in the game
Rectangle destinationRect = new Rectangle();
public int FrameWidth;
public int FrameHeight;
public bool Active;
public bool Looping;
public Vector2 Position;
public void Initialize(Texture2D texture, Vector2 position, int frameWidth, int frameHeight, int frameCount, int frametime, Color color, float scale, bool looping)
{
this.color = color;
this.FrameWidth = frameWidth;
this.FrameHeight = frameHeight;
this.frameCount = frameCount;
this.frameTime = frametime;
this.scale = scale;
Looping = looping;
Position = position;
spriteStrip = texture;
elapsedTime = 0;
currentFrame = 0;
Active = true;
}
public void Update(GameTime gameTime)
{
if (Active == false) return;
elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;
if (elapsedTime > frameTime)
{
currentFrame++;
if (currentFrame == frameCount)
{
currentFrame = 0;
if (Looping == false)
Active = false;
}
elapsedTime = 0;
}
sourceRect = new Rectangle(currentFrame * FrameWidth, 0, FrameWidth, FrameHeight);
destinationRect = new Rectangle((int)Position.X - (int)(FrameWidth * scale) / 2, (int)Position.Y - (int)(FrameHeight * scale) / 2, (int)(FrameWidth * scale), (int)(FrameHeight * scale));
}
public void Draw(SpriteBatch spriteBatch)
{
if (Active)
{
spriteBatch.Draw(spriteStrip, destinationRect, sourceRect, color);
}
}
}
}
The reason that I wrote the whole code is because I don't know where the problem is.
In my Game1.cs class I have:
Global Variables:
Texture2D playerTexture;
Texture2D playerShootingTexure;
Animation playerAnimation = new Animation();
Animation ShootingAnimation = new Animation();
...
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
playerTexture = Content.Load<Texture2D>("Graphics/Player/PlayerShip");
playerShootingTexure = Content.Load<Texture2D>("Graphics/Player/Player_Shooting");
playerAnimation.Initialize(playerTexture, Vector2.Zero, playerTexture.Width / 5, playerTexture.Height, 5, 50, Color.Wheat, 1f, true);
ShootingAnimation.Initialize(playerShootingTexure, Vector2.Zero, playerShootingTexure.Width / 5, playerShootingTexure.Height, 5, 50, Color.WhiteSmoke, 0.75f, true);
}
Where I change the animations:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// Start drawing
spriteBatch.Begin();
if (Player_Attacking == false)
{
player.PlayerAnimation = playerAnimation;
}
else
{
player.PlayerAnimation = ShootingAnimation;
}
player.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
Note: Player_Attacking is a boolean variable. When 'Space' is pressed, the value of the variable changes to TRUE, when 'Space' is released, value is FALSE
Please help!
Thanks in advance!
And sorry for huge wall of text
I'm a little unclear on the exact issue, so maybe you can help clarify if I'm not understanding.
But based on your picture, it seems when you press space to fire, the player animation should change to light up his gun with blue flames (or w/e it is :)) And it looks like it is doing that, but it's in the wrong position (above the health bar - I see the laser it is shooting is in the right location :))
But then it corrects itself once the shooting is done, as shown by the second picture (no more blue flames).
So this implies the Animation for the Player Shooting is using the wrong position.
Are you:
Updating your Player Shooting Animation's Position in your Player Update?
Calling your Player Shooting Animation's Update in your Player Update?
so you should have this somewhere in your Player Update
PlayerAnimation.Position = Position;
PlayerAnimation.Update(gameTime);
Also, try moving the check for which Player Animation to use in the Update of your Game1.cs:
protected override void Update(GameTime gameTime)
{
if (Player_Attacking == false)
{
player.PlayerAnimation = playerAnimation;
}
else
{
player.PlayerAnimation = ShootingAnimation;
}
player.Update(gameTime);
}