Failing to flip sprite with simplified Draw method - c#

I'm making a game in which I draw sprites from a sprite sheet. My Sprite class has an integer property called spriteDirection, and when this is equal to +1 the sprite faces right, and when it is equal to -1 the sprite is flipped to face the left. I have been using this Draw method successfully:
public void Draw(SpriteBatch spriteBatch)
{
if (draw)
{
int drawX = (int)(spritePosition.X - spriteOffset.X);
int drawY = (int)(spritePosition.Y - spriteOffset.Y);
if (texture != null)
{
spriteBatch.Draw(texture, new Rectangle(drawX, drawY, frameWidth, frameHeight), rSpriteSourceRectangle, Color.White);
}
}
}
The rSpriteSourceRectangle is calculated like this:
private Rectangle rSpriteSourceRectangle
{
get
{
if (spriteDirection == -1)
{
return new Rectangle(frameOffsetX + (frameWidth * (currentFrame + 1)), frameOffsetY, -frameWidth, frameHeight);
}
else
{
return new Rectangle(frameOffsetX + (frameWidth * currentFrame), frameOffsetY, frameWidth, frameHeight);
}
}
}
This all works absolutely fine. However I want to be able to switch to using this new Draw method:
public void Draw(SpriteBatch spriteBatch)
{
if (draw)
{
Vector2 positionDraw = spritePosition - spriteOffset;
if (texture != null)
{
spriteBatch.Draw(texture, positionDraw, rSpriteSourceRectangle, Color.White);
}
}
}
This works, provided my spriteDirection property is equal to 1 (so the sprite faces right). If I make it equal to -1, the sprite disappears from the screen. I can't work out why this is happening. I'm sure it's something simple, so hopefully some nice person can point me in the right direction!
Thanks!

I have found a way around the problem by using a different Draw overload:
Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);

Related

Ball is getting more and more energi for each collision

Im trying to write some simple 2d physics in monogame.
I release a ball from a given start position with a given velocity and I want it to bounce back up when it is colliding with the floor.
My problem is that I seem to give the ball more energi for each bounce i.e. it bounces higher and higher for each collision with the floor. It should be the other way around.
I have:
float get_VelocityX(float _speed, double _angle)
{
return velocity_x = velocity_x +_speed * (float)Math.Cos(_angle);
}
public float get_VelocityY(float _speed, double _angle, float _t, float gravity)
{
return velocity_y = velocity_y + _speed * (float)Math.Cos(_angle); // - (float)(-gravity * _t);
}
And in my Update function I have this:
if (speed > 0)
{
timeCount += (float)gameTime.ElapsedGameTime.TotalSeconds;
t += timeCount;
}
else
{
return;
}
Vx = ball.get_VelocityX(speed, angle);
Vy = ball.get_VelocityY(speed, angle, t, gravity);
if (posX >= windowMAX)
{
posX = posX + -Vx * friction * t;
}
if (posY > windowMIN)
{
posY = posY + -Vy * friction * t;
}
else
{
posY += gravity;
}
ballRect.X = (int)posX;
ballRect.Y = (int)posY;
Where posX, posY and speed are user inputs for start position and velocity.
Gravity is just a float = 9.82f;
Right now Im not doing anything with the posX except setting the balls starting position. Next step will be to implement a throwing motion.
EDIT:
Friction = 0.001f;
t is deltatime.
I went through your logic and have prepared a sample code. Please read the following before you go through it.
In order to simulate real-life motion, you need to implement the physics accurately. Although your implemented velocity and position seems mostly correct, the gravity needs to be treated as acceleration, and therefore adding its value to the position (as done in your code) is incorrect. I assume that this is the reason why you aren't getting your expected result since the value of increment on the Y-component of position is far greater than it should be.
Instead of keeping PosX, PosY for the position, Velocity_X..(), Velocity_Y..() for velocity, I would advise you to use struct Vector2 as shown below in my code, which is included in the Monogame framework and has a lot more helping functions built-in. This will help in making your code shorter and cleaner.
I did not understand why you used the Cosine of the given angle in your implementation of Velocity for both its X and Y components. My code below is ignoring this.
You can see my code below. Here the bouncing object Box is of the type struct PhyObj with all the needed Physics of motion implemented within it.
public class Game1 : Game
{
private SpriteBatch _batch;
internal Texture2D Texture;
public static Vector2 GravityAcceleration => new Vector2(0, 9.8f);//Constant accerleration along Y-Axis
internal Rectangle Ground;
internal PhyObj Box;
public Game1()
{
_ = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void LoadContent()
{
Point center = Window.ClientBounds.Center;
Box = new PhyObj(new Vector2(center.X, 0), Vector2.Zero, 30, 30);
Ground = new Rectangle(0, center.Y, center.X * 2, 10);
_batch = new SpriteBatch(GraphicsDevice);
Texture = new Texture2D(GraphicsDevice, 1, 1);
Texture.SetData(new[] { Color.White });
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
Box.Accelerate(GravityAcceleration, gameTime);
if (Box.Pos.Y > Ground.Top - Box.Dest.Height)//Check if bounce needed
{
Box.Pos.Y = Ground.Top - Box.Dest.Height;//Clipping
Box.Vel.Y *= -1; //Bouncing
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_batch.Begin();
_batch.Draw(Texture, Ground, Color.Black);
_batch.Draw(Texture, Box.Dest, Color.White);
_batch.End();
base.Draw(gameTime);
}
}
public struct PhyObj
{
internal static float friction => 0.005f;
public PhyObj(Vector2 x, Vector2 v, int width, int height)
{
Pos = x;
Vel = v;
Dest = new Rectangle(0, 0, width, height);
(Dest.X, Dest.Y) = ((int)Pos.X, (int)Pos.Y);
}
internal Vector2 Pos, Vel;
internal Rectangle Dest;
public void Accelerate(Vector2 acc, GameTime time)
{
Vel += acc - Vel * friction;
Pos += Vel * (float)time.ElapsedGameTime.TotalSeconds;
(Dest.X, Dest.Y) = ((int)Pos.X, (int)Pos.Y);
}
}
As shown in the Update() function, the PhyObj Box is being accelerated externally (In this case gravity, but you can add your custom external force), and the velocity/position it needs to attain is calculated internally.
The bouncing logic is simple: The Y-component of velocity is inverted.
The "Clipping" process here makes sure that the Box does not cross the Ground object even when the downward acceleration is acting upon it.
The next subsequent bounces have their height reduced due to the friction value (Done internally by the struct PhyObj).

Updating Texture During Movement

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.

Unable to create collision rectangle

I am trying to make a small game for my programming class at college and I am having a problem trying to create a rectangle for collision.
When I try and use the width and height from my texture. I get an error telling me that I can't convert from a float to an int. But the pixel size of the image is not a float value?
Here is the code I have in my game objects class (there is a lot of comments to help direct where things are meant to go):
class ButtonSprite
{
public Texture2D Art;
public Vector2 Position;
public ButtonSprite(Vector2 pos, Texture2D tex)
{
// Copy the texture "tex" into the "Art" class variable
Art = tex;
// Copy the vector "pos" into the "Position" class variable
Position = pos;
}
public void DrawMe(SpriteBatch sb, Color col)
{
// use the spritebatch "sb" to draw the sprite at "Position" using the texture "Art" with the tint from "col"
sb.Draw(Art, Position, col);
}
}
class PlayerSprite
{
public Texture2D Art;
public Vector2 Position;
public Rectangle CollisionRect;
public PlayerSprite(Vector2 pos, Texture2D tex)
{
// Copy the texture "tex" into the "Art" class variable
Art = tex;
// Copy the vector "pos" into the "Position" class variable
Position = pos;
// create a new CollisionRect Rectangle using the X and Y from Position and the Width and Height from Art
CollisionRect = new Rectangle(Position.X, Position.Y, Art.Width, Art.Height);
}
public void UpdateMe(ButtonState leftB, ButtonState rightB, ButtonState downB, ButtonState upB)
{
// if leftB is pressed
if (leftB == ButtonState.Pressed)
{
// subtract 1 from the X that belongs to Position
Position.X -= 1;
}
// endif
// if rightB is pressed
if (rightB == ButtonState.Pressed)
{
// add 1 to the X that belongs to Position
Position.X += 1;
}
// endif
// if downB is pressed
if (downB == ButtonState.Pressed)
{
// add 1 to the Y that belongs to Position
Position.Y += 1;
}
// endif
// if upB is pressed
if (upB == ButtonState.Pressed)
{
// subtract 1 from the Y that belongs to Position
Position.Y -= 1;
}
// endif
// set the X that belongs to CollisionRect to equal the integer version of the X that belongs to Position
// set the Y that belongs to CollisionRect to equal the integer version of the Y that belongs to Position
}
public void DrawMe(SpriteBatch sb)
{
// use the spritebatch "sb" to draw the sprite at "Position" using the texture "Art" with a white tint
sb.Draw(Art, Position, Color.White);
}
}
}
Right off the bat, I would suggest you calculate your collisions using the movement too. This will prevent more glitches (e.g. passing through walls).
A good way to do this is to inflate the target rectangle (temporarily) by the size of the moving rectangle. Then, you perform a line to bounding box intersection. This will give you an intersection point, while being safer against glitches.
Try this line when building your collision rect. It will convert the height & width floats to integers. The + 1 is optional.
CollisionRect = new Rectangle(Position.X, Position.Y, (int)Art.Width + 1, (int)Art.Height + 1);

Having problems drawing animations in monogame

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);
}

xna sprite to mouse position

In XNA 4.0 how do I get a sprite to move to the mouses coordinates. I know that this would be possible to do like this:
if (ms.LeftButton == ButtonState.Pressed)
{
Vector2 shipPos = new Vector2(ms.X,ms.Y);
}
but because I'm using a camera that follows the ship this does not work properly. The reason for this is that the position of the mouse is relative to the screen and if the ship has been moved to let say (500,500) when I click in the top left corner of the window the ship goes back to (0,0), even tough I want the ship to move from the ships position up towards the corner. Here's the code for my matrix:
class Camera
{
public Matrix transform;
Viewport view;
Vector2 centre;
public Camera(Viewport newView)
{
view = newView;
}
public void Update(GameTime gameTime, Game1 ship)
{
int w = Game1.width;
int h = Game1.height;
centre = new Vector2(ship.FSpos.X - (w / 2 - 189/2),
ship.FSpos.Y - (h / 2-128/2));
transform = Matrix.CreateScale(new Vector3(1, 1, 0)) *
Matrix.CreateTranslation(new Vector3(-centre.X, -centre.Y, 0));
}
}
You could do something like this:
if (ms.LeftButton == ButtonState.Pressed)
{
Vector2 shipPos = new Vector2(
ms.X-screenWidth/2 + previousShipPosition.X,
ms.Y-screenHeight/2 + previousShipPosition.Y
);
}

Categories

Resources