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);
}
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);
'''
I'm new to this so not completely sure whether I am posting this correctly, but I have been having a few issues when creating my game. My main goal is to create a topdown shooter styled game, using movement where the 'player' rotates based on the current position of the mouse and can press 'w' to move towards it at a set speed.
The main issue is, when the game loads, the movement works exactly how I want it to, but the texture itself is not moving, but only the drawRectangle.
Game1.cs:
player = new Player(Content, #"graphics\Player1", 500, 500, spritePosition);
spritePosition = new Vector2(player.CollisionRectangle.X, player.CollisionRectangle.Y);
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
KeyboardState keyboard = Keyboard.GetState();
MouseState mouse = Mouse.GetState();
IsMouseVisible = true;
distance.X = mouse.X - spritePosition.X;
distance.Y = mouse.Y - spritePosition.Y;
//Works out the rotation depending on how far away mouse is from sprite
rotation = (float)Math.Atan2(distance.Y, distance.X);
// TODO: Add your update logic here
spritePosition = spriteVelocity + spritePosition;
spriteOrigin = new Vector2(player.DrawRectangle.X / 2, player.DrawRectangle.Y / 2);
if (Keyboard.GetState().IsKeyDown(Keys.W))
{
spriteVelocity.X = (float)Math.Cos(rotation) * tangentialVelocity;
spriteVelocity.Y = (float)Math.Sin(rotation) * tangentialVelocity;
}
else if(spriteVelocity != Vector2.Zero)
{
Vector2 i = spriteVelocity;
spriteVelocity = i -= friction * i;
}
This is the main movement code from the Update function as well as where the new player has been created.
Player.cs:
class Player
{
Texture2D sprite;
Rectangle drawRectangle;
int health = 100;
public Player(ContentManager contentManager, string spriteName, int x , int y, Vector2 velocity)
{
LoadContent(contentManager, spriteName, x, y, velocity);
}
public Rectangle CollisionRectangle
{
get { return drawRectangle; }
}
public Rectangle DrawRectangle
{
get { return drawRectangle; }
set { drawRectangle = value; }
}
public int Health
{
get { return health; }
set {
health = value;
if (health <= 0)
health = 0;
if (health > 100)
health = 100;
}
}
public Vector2 Velocity
{
get { return Velocity; }
set { Velocity = value; }
}
public void Update(GameTime gameTime, KeyboardState keyboard, MouseState mouse)
{
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(sprite, drawRectangle, Color.White);
}
private void LoadContent(ContentManager contentManager, string spriteName, int x, int y, Vector2 velocity)
{
sprite = contentManager.Load<Texture2D>(spriteName);
drawRectangle = new Rectangle(x - sprite.Width / 2, y - sprite.Height / 2, sprite.Width, sprite.Height);
}
}
I didn't know what to include in the Update function of the player.cs, whether the code for movement should go in there or the main Game1.cs.
Hopefully this is enough code for you guys to be able to help. Sorry for there being quite a lot of code, but I'm just unsure where the error is occurring.
Thanks in advance
For the rotation you would want to use 'another' kind of spriteBatch.Draw, because the draw function can have more parameters than your currently using.
The full draw function is as followed:
SpriteBatch.Draw(Texture2D texture, Vector2 position, Rectangle sourceRectangle, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerdepht)
As you can see, now we have a parameter for rotation that can be used.
So your code will look something like this:
spriteBatch.Draw(sprite, drawRectangle, null, Color.White, rotation, Vector2.Zero, 1, SpriteEffects.None, 0);
As for your question for where your code should go. It is good to learn yourself to put the code where it belongs, in this case the player. Because when your programs become bigger, it would be very messy to do it this way.
To call the update function in your player class, you can just simply call player.Update(gameTime, keyboard, mouse) in the Update of Game1.
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);
I am drawing a line in XNA which I want to never end. I also have a tool that moves forward in X-direction and a camera which is centered at this tool. However, when I reach the end of the viewport the lines are not drawn anymore. Here are some pictures to illustrate my problem:
At the start the line goes across the whole screen, but as my tool moves forward, we reach the end of the line.
Here are the method which draws the lines:
private void DrawEvenlySpacedSprites (Texture2D texture, Vector2 point1, Vector2 point2, float increment)
{
var distance = Vector2.Distance (point1, point2); // the distance between two points
var iterations = (int)(distance / increment); // how many sprites with be drawn
var normalizedIncrement = 1.0f / iterations; // the Lerp method needs values between 0.0 and 1.0
var amount = 0.0f;
if (iterations == 0)
iterations = 1;
for (int i = 0; i < iterations; i++) {
var drawPoint = Vector2.Lerp (point1, point2, amount);
spriteBatch.Draw (texture, drawPoint, Color.White);
amount += normalizedIncrement;
}
}
Here are the draw method in Game. The dots are my lines:
protected override void Draw (GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.Black);
nyVector = nextVector (gammelVector);
GraphicsDevice.SetRenderTarget (renderTarget);
spriteBatch.Begin ();
DrawEvenlySpacedSprites (dot, gammelVector, nyVector, 0.9F);
spriteBatch.End ();
GraphicsDevice.SetRenderTarget (null);
spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, camera.transform);
spriteBatch.Draw (renderTarget, new Vector2 (), Color.White);
spriteBatch.Draw (tool, new Vector2(toolPos.X - (tool.Width/2), toolPos.Y - (tool.Height/2)), Color.White);
spriteBatch.End ();
gammelVector = new Vector2 (nyVector.X, nyVector.Y);
base.Draw (gameTime);
}
Can anyone point me in the right direction here? I'm guessing it has to do with the viewport.width, but I'm not quite sure how to solve it. Thank you for reading!
I read this and thought it would be a fun exercise this morning, so I decided to write this for fun.
The implementation is pretty simple, keep adding lines at the end of each other until the last line is outside of the viewable area.
The following code will draw a line infinitely going right. As an added optimization, the lines on the left side of the screen are deleted as you pass them. You could easily make it retain the lines that were there previously, or also create lines going left as well. I will leave these exercises to you.
Take a look at the following Line class, which will define a single line on screen:
public class Line
{
Texture2D Texture;
Color Color;
public Vector2 PointA;
public Vector2 PointB;
float Width;
public Line(Vector2 pointA, Vector2 pointB, float width, Color color, Texture2D texture)
{
Texture = texture;
PointA = pointA;
PointB = pointB;
Width = width;
Color = color;
}
public void Draw(SpriteBatch spritebatch)
{
float angle = (float)Math.Atan2(PointB.Y - PointA.Y, PointB.X - PointA.X);
float length = Vector2.Distance(PointA, PointB);
spritebatch.Draw(Texture, PointA, null, Color, angle, Vector2.Zero, new Vector2(length, Width), SpriteEffects.None, 0);
}
}
I wrote the implementation inside of a game class, since I was speedcoding. You can see the Game class below:
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Camera Camera;
Texture2D LineTexture;
List<Line> Lines;
Random Random;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
Camera = new Camera(GraphicsDevice.Viewport, 1f);
LineTexture = new Texture2D(GraphicsDevice, 1, 1);
LineTexture.SetData<Color>(new Color[] { Color.White });
Random = new Random();
Lines = new List<Line>();
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
//handle input
KeyboardState kbState = Keyboard.GetState();
if (kbState.IsKeyDown(Keys.Escape))
this.Exit();
if (kbState.IsKeyDown(Keys.OemMinus))
Camera.Zoom -= 0.015f;
else if (kbState.IsKeyDown(Keys.OemPlus))
Camera.Zoom += 0.015f;
if (kbState.IsKeyDown(Keys.Up))
Camera.Move(new Vector2(0, -30));
else if (kbState.IsKeyDown(Keys.Down))
Camera.Move(new Vector2(0, 30));
if (kbState.IsKeyDown(Keys.Left))
Camera.Move(new Vector2(-30, 0));
else if (kbState.IsKeyDown(Keys.Right))
Camera.Move(new Vector2(30, 0));
//check if line is still in viewport - if not remove it
for (int i = 0; i < Lines.Count; i++)
{
if (Lines[i].PointB.X < Camera.Viewport.X)
{
Lines.RemoveAt(i);
i--;
}
}
//if there are no lines, create one to get started
if (Lines.Count == 0)
{
Vector2 p1 = new Vector2(Camera.Viewport.X, Random.Next(Camera.Viewport.Y + 50, Camera.Viewport.Height - 100));
Vector2 p2 = new Vector2(p1.X + Random.Next(5, 20), p1.Y + Random.Next(-5, 5));
Line line = new Line(p1, p2, 1, Color.Black, LineTexture);
Lines.Add(line);
}
//Check if we need to add some lines to the right of our last list item
while (Lines[Lines.Count - 1].PointB.X < Camera.Viewport.X + Camera.Viewport.Width)
{
Vector2 p1 = new Vector2(Lines[Lines.Count - 1].PointB.X, Lines[Lines.Count - 1].PointB.Y); ;
Vector2 p2 = new Vector2(p1.X + Random.Next(5, 20), p1.Y + Random.Next(-5, 5));
Line line = new Line(p1, p2, 1, Color.Black, LineTexture);
Lines.Add(line);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(new Color(30, 90, 150));
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, Camera.View);
foreach (Line line in Lines)
line.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
I've also included the Camera class for your convenience:
public class Camera
{
private const float zoomUpperLimit = 1.5f;
private const float zoomLowerLimit = 0.1f;
private float _zoom;
private Vector2 _pos;
private int ViewportWidth, ViewportHeight;
#region Properties
public float Zoom
{
get { return _zoom; }
set
{
_zoom = value;
if (_zoom < zoomLowerLimit)
_zoom = zoomLowerLimit;
if (_zoom > zoomUpperLimit)
_zoom = zoomUpperLimit;
}
}
public Rectangle Viewport
{
get
{
int width = (int)((ViewportWidth / _zoom));
int height = (int)((ViewportHeight / _zoom));
return new Rectangle((int)(_pos.X - width / 2), (int)(_pos.Y - height / 2), width, height);
}
}
public void Move(Vector2 amount)
{
_pos += amount;
}
public Vector2 Position
{
get { return _pos; }
set { _pos = value; }
}
public Matrix View
{
get
{
return Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
Matrix.CreateTranslation(new Vector3(ViewportWidth * 0.5f, ViewportHeight * 0.5f, 0));
}
}
#endregion
public Camera(Viewport viewport, float initialZoom)
{
_zoom = initialZoom;
_pos = Vector2.Zero;
ViewportWidth = viewport.Width;
ViewportHeight = viewport.Height;
}
}
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
//
}