XNA sprite movement gone wrong - c#

Hy everyone, so first off all, here is the image of my sprite:
I scaled it down in my game so its not that big in-game.
So anyway my desired result of the code below:
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
spritePos = spriteVelocity + spritePos;
spriteR = new Rectangle((int)spritePos.X, (int)spritePos.Y, spriteT.Width, spriteT.Height);
spriteOrigin = new Vector2(spriteR.Width/2, spriteR.Height / 2);
if (Keyboard.GetState().IsKeyDown(Keys.Right)) rotation += 0.1f;
if (Keyboard.GetState().IsKeyDown(Keys.Left)) rotation -= 0.1f;
if (Keyboard.GetState().IsKeyDown(Keys.Up))
{
spriteVelocity.X = (float)Math.Cos(rotation) * tangentialVelocity;
spriteVelocity.Y = (float)Math.Sin(rotation) * tangentialVelocity;
} else if (spriteVelocity != Vector2.Zero)
{
float i = spriteVelocity.X;
float j = spriteVelocity.Y;
spriteVelocity.X = i -= friction * i;
spriteVelocity.Y = j -= friction * j;
}
base.Update(gameTime);
}
and then Draw method:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
foreach(Bushes bush in bushes)
{
bush.Draw(spriteBatch);
}
player.Draw(spriteBatch);
spriteBatch.Draw(spriteT, spritePos, null, Color.White, rotation, spriteOrigin, 0.1f, SpriteEffects.None, 0f);
spriteBatch.End();
base.Draw(gameTime);
}
is that when I press 'Up' arrow this little rocket should go into direction in whis it is pointing (becouse I can rotate it with Left and Right arrow to have it pointed where desired), but the result of this code is that when I press Up arrow the sprite moves to the right at first, and when its rotated it doesnt go where its pointed but kinda sideways :/
What am I doing wrong here?
PS. all those variables that arent declared and initialized in code are global variables and initialized in Initialize() and LoadContent() methods :/

If your rotation is initialized at 0, it should point to the right, look at where 0 points at on the trig circle below :)
You could compute your direction with Vector2.Transform and give as value parameter (first) Vector2(0,1) so your reference direction would be up.
Or you rotate your sprite to the right and initialize your angle at Pi/2.

Related

Monogame - Making the camera stop when my player collides

I have come to a position with my 3D game where I am now trying to perfect the camera. What I ultimately want is a working first person camera that stops when it collides with a wall. I assume you do this by having the camera move with a model and have the model collide to stop the camera also, i am just having issues seeing how to do that.
I have the model working so it stops, i just need to lock the camera from moving also. So far this is the code I have, making the model stop with collison:
if (player_health != 0)
{
if (kb.IsKeyDown(Keys.Left))
{
player_x = player_x + 0.05f;
world_player = Matrix.CreateTranslation(new Vector3(player_x, player_y, player_z));
if (IsCollision(ball, world_player, ball, world_spike))
{
player_x = player_x - 0.05f;
world_player = Matrix.CreateTranslation(new Vector3(player_x, player_y, player_z));
player_health = 0;
}
if (IsCollision(ball, world_player, ball, world_bullet[0]))
{
player_health = 0;
}
// use this code for any standard collision
if ((IsCollision(ball, world_player, ball, world_cannon)) || (IsCollision(ball, world_player, ball, walls[0])) || (IsCollision(ball, world_player, ball, walls[1])))
{
player_x = player_x - 0.05f;
world_player = Matrix.CreateTranslation(new Vector3(player_x, player_y, player_z));
}
}
My camera added into the game1 class:
camera = new Camera(this, new Vector3(10f, 3f, 5f), Vector3.Zero, 5f);
Components.Add(camera);
And the camera class itself (I have been told this is rather over complicated? for a camera class, but the simpler ones didnt work):
namespace Game7
{
class Camera : GameComponent
{
private Vector3 cameraPosition;
private Vector3 cameraRotation;
private float cameraSpeed;
private Vector3 cameraLookAt;
private Vector3 mouseRotationBuffer;
private MouseState currentMouseState;
private MouseState previousMouseState;
// Properties
public Vector3 Position
{
get { return cameraPosition; }
set
{
cameraPosition = value;
UpdateLookAt();
}
}
public Vector3 Rotation
{
get { return cameraRotation; }
set
{
cameraRotation = value;
UpdateLookAt();
}
}
public Matrix Projection
{
get;
protected set;
}
public Matrix View
{
get
{
return Matrix.CreateLookAt(cameraPosition, cameraLookAt, Vector3.Up);
}
}
//Constructor
public Camera(Game game, Vector3 position, Vector3 rotation, float speed)
: base(game)
{
cameraSpeed = speed;
// projection matrix
Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4,
Game.GraphicsDevice.Viewport.AspectRatio,
0.05f,
1000.0f);
// set camera positiona nd rotation
MoveTo(position, rotation);
previousMouseState = Mouse.GetState();
}
// set Camera's position and rotation
private void MoveTo(Vector3 pos, Vector3 rot)
{
Position = pos;
Rotation = rot;
}
//update the look at vector
private void UpdateLookAt()
{
// build rotation matrix
Matrix rotationMatrix = Matrix.CreateRotationX(cameraRotation.X) * Matrix.CreateRotationY(cameraRotation.Y);
// Look at ofset, change of look at
Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);
// update our cameras look at vector
cameraLookAt = cameraPosition + lookAtOffset;
}
// Simulated movement
private Vector3 PreviewMove(Vector3 amount)
{
// Create rotate matrix
Matrix rotate = Matrix.CreateRotationY(cameraRotation.Y);
// Create a movement vector
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
return cameraPosition + movement;
}
// Actually move the camera
private void Move(Vector3 scale)
{
MoveTo(PreviewMove(scale), Rotation);
}
// updat method
public override void Update(GameTime gameTime)
{
// smooth mouse?
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
currentMouseState = Mouse.GetState();
KeyboardState ks = Keyboard.GetState();
// input
Vector3 moveVector = Vector3.Zero;
if (ks.IsKeyDown(Keys.W))
moveVector.Z = 1;
if (ks.IsKeyDown(Keys.S))
moveVector.Z = -1;
if (ks.IsKeyDown(Keys.A))
moveVector.X = 1;
if (ks.IsKeyDown(Keys.D))
moveVector.X = -1;
if (moveVector != Vector3.Zero)
{
//normalize it
//so that we dont move faster diagonally
moveVector.Normalize();
// now smooth and speed
moveVector *= dt * cameraSpeed;
// move camera
Move(moveVector);
}
// Handle mouse input
float deltaX;
float deltaY;
if(currentMouseState != previousMouseState)
{
//Cache mouse location
deltaX = currentMouseState.X - (Game.GraphicsDevice.Viewport.Width / 2);
deltaY = currentMouseState.Y - (Game.GraphicsDevice.Viewport.Height / 2);
// smooth mouse ? rotation
mouseRotationBuffer.X -= 0.01f * deltaX * dt;
mouseRotationBuffer.Y -= 0.01f * deltaY * dt;
if (mouseRotationBuffer.Y < MathHelper.ToRadians(-75.0f))
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(-75.0f));
if (mouseRotationBuffer.Y > MathHelper.ToRadians(75.0f))
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(75.0f));
Rotation = new Vector3(-MathHelper.Clamp(mouseRotationBuffer.Y, MathHelper.ToRadians(-75.0f), MathHelper.ToRadians(75.0f)), MathHelper.WrapAngle(mouseRotationBuffer.X), 0);
deltaX = 0;
deltaY = 0;
}
// Alt + F4 to close now.
// Makes sure the mouse doesn't wander across the screen (might be a little buggy by showing the mouse)
Mouse.SetPosition(Game.GraphicsDevice.Viewport.Width / 2, Game.GraphicsDevice.Viewport.Height / 2);
previousMouseState = currentMouseState;
base.Update(gameTime);
}
}
}
The Preview Move part of the camera class is meant to be where it detects for collision, at least the guide i followed said so, i just dont see how to integrate it. Thanks for any help or input, even if it is just a link to another guide or resource, would be very appreciated!
If your hero model (player) already has move functionality and collision functionality, and you want the camera to follow the model, then you can simplify the camera update tremendously.
Assuming after moving the player, the new position and/or orientation of the player is represented in player_world, simply borrow the location and orientation information stored in the player_world matrix to build a view matrix each frame.
//after moving player and setting its matrix accordingly:
view = Matrix.Invert(player_world);
And that is your complete camera class update. This creates a view matrix that is in the same position as the player and facing the same direction he is. If the player model stops because it hits a wall, then the camera (view matrix) stops too, because it is built from the player's 'stopped' matrix. No need to create a whole class for moving and rotating the camera around when the movement and rotation is the same as the player.
Sometimes the local player model origin would be located at the player's feet or belly and you want the camera up in the head where the eyes are. If so, simply apply something like this:
//after moving player and setting its matrix accordingly:
Matrix camera_world = player_world;
camera_world *= Matrix.CreateTranslation(Vector3.Up * ??f);// set ??f to the height difference between feet and head
view = Matrix.Invert(camera_world);

What is wrong with my player movement?

I have a small problem that i cannot work out. I'm currently learning to apply vector math and trigonometry to games, To learn i have created a game where the player fly's a space ship of sorts however i have a problem.
My movement seems to be inverted?
He will fly upwards and downwards correctly, However the left and right movement is inverted?
Here is my code:
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
// TODO: Add your update logic here
angleDeg = (float)(angle * (360 / (Math.PI * 2)));
var angleRad = (float)MathHelper.ToRadians(angleDeg);
var delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
xSpeed = (float)Math.Cos(angle) * speed;
ySpeed = (float)Math.Sin(angle) * speed;
position.Y += xSpeed * delta;
position.X += ySpeed * delta;
keyboardState = Keyboard.GetState();
if(angleDeg < -360 || angleDeg > 360)
{
angle = 0;
angleDeg = 0;
}
if(keyboardState.IsKeyDown(Keys.Left))
{
angle -= 1f * delta;
}
if (keyboardState.IsKeyDown(Keys.Right))
{
angle += 1 * delta;
}
if (keyboardState.IsKeyDown(Keys.Up))
{
speed -= 1f;
}
if (keyboardState.IsKeyDown(Keys.Down))
{
speed += 1f;
}
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.Clear(Color.Black);
// TODO: Add your drawing code here
spriteBatch.Begin();
spriteBatch.Draw(player, position, sourceRect, Color.White, angle, origin, 1.0f, SpriteEffects.None, 1);
spriteBatch.DrawString(font, "Rotation: " + angleDeg, new Vector2(10, 5), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
Anyone have any ideas as to why this is happening?
Thanks in advance.
Mathematician's conventions and screen coordinates sometimes don't match.
Try this:
xSpeed = (float)Math.Sin(angle) * speed;
ySpeed = -(float)Math.Cos(angle) * speed; // notice '-' !
based on this answer.
As Andrew Russell said in this answer:
If you are using maths functions like sin or cos or atan2, then
absolute angles always start from the X+ axis as zero radians, and the
positive rotation direction rotates towards Y+.
The XNA SpriteBatch works in Client Space. Where "up" is Y-, not Y+
(as in Cartesian space, projection space, and what most people usually
select for their world space). This makes the rotation appear as
clockwise (not counter-clockwise as it would in Cartesian space). The
actual coordinates the rotation is producing are the same.
From this piece of your code:
if(keyboardState.IsKeyDown(Keys.Left))
{
angle -= 1f * delta;
}
if (keyboardState.IsKeyDown(Keys.Right))
{
angle += 1 * delta;
}
Left key will turn your space ship clockwise (lower value of angle), and Right key will turn it counterclockwise (higher value of angle).
From your last comment:
My rotations are fine, It just when add speed when i pres the up arrow
and turn, the movement is inverted
I would check this other part of your code:
position.Y += xSpeed * delta;
position.X += ySpeed * delta;
It seems weird to use xSpeed to update position.Y and viceversa, but also maybe you need to use -= to update position.Y

How to make an enemy face a character

Hey guys ive got this problem where i just can't get my enemy to rotate towards my character
ive been trying for days and have asked around but nothing and so it would be awsome if you could give me some ideas.
this is my enemy class now in this code here everything works fine it does what i want however it faces the mouse and not my character
class Class1
{
Character character = new Character();
EnemyShip blah = new EnemyShip();
Texture2D texture;
Rectangle rectangle;
public Vector2 origin;
public Vector2 velocity;
public Vector2 position;
float rotation;
const float forwardvelocity = 1f;
float friction = 0.1f;
public Vector2 distance;
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>("Ships/WarShip");
position = new Vector2(800, 300);
}
public void Update(GameTime gameTime)
{
MouseState mouse = Mouse.GetState();
distance.X = mouse.X - position.X; // these two line are the one i want to
distance.Y = mouse.Y - position.Y; // change however when i change mouse.X to //say character.Position.X my enemy ship moves towards the top left corner of the screen //and not the character
rotation = (float)Math.Atan2(distance.Y, distance.X);
position = velocity + position;
velocity.X = (float)Math.Cos(rotation) * forwardvelocity;
velocity.Y = (float)Math.Sin(rotation) * forwardvelocity;
rectangle = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
origin = new Vector2(rectangle.Width / 2, rectangle.Height / 2);
}
public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
{
spriteBatch.Draw(texture, position, null, Color.White, rotation, origin, 1f, SpriteEffects.None, 0);
}
}
}
And this is my Character class
class Character
{
public Texture2D texture;
public float angle = 0;
public Vector2 velocity;
public Vector2 Position = new Vector2(0, 0);
public float forwardvelocity = 5;
public float friction = 0.03f;
public Vector2 origin;
public Rectangle sourcerectangle;
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>("Ships/charactership");
}
public void Update(GameTime gameTime)
{
Position += velocity;
if (Keyboard.GetState().IsKeyDown(Keys.W))
{
velocity.X = (float)Math.Cos(angle ) * forwardvelocity;
velocity.Y = (float)Math.Sin(angle) * forwardvelocity;
}
if (Keyboard.GetState().IsKeyDown(Keys.S))
{
velocity.X = -(float)Math.Cos(angle) * forwardvelocity;
velocity.Y = -(float)Math.Sin(angle) * forwardvelocity;
}
if (Keyboard.GetState().IsKeyDown(Keys.A)) angle -= 0.05f;
if (Keyboard.GetState().IsKeyDown(Keys.D)) angle += 0.05f;
else if (velocity != Vector2.Zero)
{
float i = velocity.X;
float j = velocity.Y;
velocity.X = i -= friction * i;
velocity.Y = j -= friction * j;
}
//--------------------------------------------------------------
}
public void Draw(SpriteBatch spriteBatch)
{
sourcerectangle = new Rectangle(0, 0, texture.Width, texture.Height);
origin = new Vector2(texture.Width / 2, texture.Height / 2);
spriteBatch.Draw(texture, Position, sourcerectangle, Color.White, angle, origin, 1.0f, SpriteEffects.None, 0);
}
}
}
I notice you have a Character variable in your Class1. It is set to new Character() but then nothing else is ever done with it ever. I'm guessing this means your actual Character is within your Game somewhere, and this other Character in this Class1 is a completely different variable entirely. So naturally, using its Position is meaningless.
Since your enemy depends on the Character variable for its own computations, pass in the dependency:
public void Update(Character c, GameTime gameTime)
{
MouseState mouse = Mouse.GetState();
distance.X = c.Position.X - position.X;
distance.Y = c.Position.Y - position.Y;
...
Then in the top level Game or what not:
//your actual character
Character c;
...
//in Game.Update
c.Update(gameTime);
c1.Update(c, gameTime);
Then you can simply remove that Character character = new Character(); in Class1, as it is useless.
There are "lazier" methods such as singletons and other staticness-related methods, but I don't recommend these.
You say that "Class1" is your enemy class...
class Class1
{
Character character = new Character();
EnemyShip blah = new EnemyShip();
Instanciate the character inside your enemy class makes no sense, I think you are not referencing the right character instance in your enemy class.
Seems that the character variable inside your enemy class is never updated, what can be the reason it is always at (0,0)

XNA 3.1 Movement Collision Issue

So heres my problem. I have a box that I want my character to move around. But I want to be able to move around it while holding multiple move commands, for instance..
when moving right (towards the left of the obstacle) I want to be able to hold move right and up or down at the same time without the character sticking to the box. The funny part is, it works fine for the left and right side of the obstacle, yet it sticks when i try it on the top and bottom side of the obstacle.
Heres the Player Class (object im moving)
<!-- language: c# -->
public class Player
{
public Texture2D texture;
public Vector2 position;
public int speed;
public Vector2 offset;
public bool left, right, up, down;
public Rectangle collisionRect
{
get
{
return new Rectangle((int)position.X , (int)position.Y, texture.Width, texture.Height);
}
}
public Vector2 direction;
public Player(Texture2D texture, Vector2 position, int speed)
{
this.texture = texture;
this.position = position;
this.speed = speed;
offset.X = speed;
offset.Y = speed;
left = false;
right = false;
up = false;
down = false;
}
public virtual void Update(GameTime gameTime, Rectangle clientBounds)
{
direction = Vector2.Zero;
if (Keyboard.GetState().IsKeyDown(Keys.A))
{
direction.X -= 1;
left = true;
}
else
left = false;
if (Keyboard.GetState().IsKeyDown(Keys.D))
{
direction.X += 1;
right = true;
}
else
right = false;
if (Keyboard.GetState().IsKeyDown(Keys.W))
{
direction.Y -= 1;
up = true;
}
else
up = false;
if (Keyboard.GetState().IsKeyDown(Keys.S))
{
direction.Y += 1;
down = true;
}
else
down = false;
position += (direction * speed);
}
public virtual void Draw(GameTime gameTime, SpriteBatch spritebatch)
{
spritebatch.Draw(texture, collisionRect, Color.White);
}
}
Heres the Update Method in my maingame
<!-- language: c# -->
public override void Update(GameTime gameTime)
{
// TODO: Add your update code here
player.Update(gameTime, Game.Window.ClientBounds);
if (player.right && HitWall(player))
{
player.position.X -= player.offset.X;
}
else if (player.left && HitWall(player))
{
player.position.X += player.offset.X;
}
if (player.down && HitWall(player))
{
player.position.Y -= player.offset.Y;
}
else if (player.up && HitWall(player))
{
player.position.Y += player.offset.Y;
}
base.Update(gameTime);
}
And the HitWall function
<!-- language: c# -->
public bool HitWall(Player player)
{
for (int i = player.collisionRect.Top; i < player.collisionRect.Bottom; i++)
for (int j = player.collisionRect.Left; j < player.collisionRect.Right; j++)
if (TextureData[i * gameMap.map.Width + j] != Color.White)
return true;
return false;
}
I'm not sure where offset is defined, but I'm assuming it's the movement you've just made that frame.
Your problem is that because you check left & right before up and down, if you're moving diagonally down onto the top edge of the box, then you'll register a hit in the Y direction — HitWall doesn't check which direction you're going, it just checks for a collision. Therefore, the collision in the Y axis stil counts on the line if (player.right && HitWall(player)) and stops your lateral movement.
Best bet is to apply your sideways movement, check for a hit, move back if there is one — then apply your downwards movement, check for a hit, and move back if there is one. Correcting the position like this should mean you slide along the sides as you want.

XNA Rectangle intersection

I recently started on XNA development and having some (very limited) experience with it in the past, I decided to try and make a Pong clone to see how much I could remember. Most of what I knew came back to me, but I am having problems with collision detection between the bats and the ball. I have 3 rectangles set to update position along with the sprites that I am using, and when the ball rectangle intersects with a bat rectangle, I multiply the X speed of the ball by -1. However this produces an unusual effect by which the ball bounces around the bat as shown in this video.What am I doing wrong here?
Here is the code for this project (poor, I know):
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 Pong
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
System.Random generator = new Random();
Texture2D ball;
Texture2D bat1;
Texture2D bat2;
Texture2D middle;
Vector2 midPos;
Vector2 bat1Pos;
Vector2 bat2Pos;
Vector2 ballPos;
Vector2 ballVelo;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load sprites
ball = Content.Load<Texture2D>(#"sprites/pongball");
bat1 = Content.Load<Texture2D>(#"sprites/pongbat");
bat2 = Content.Load<Texture2D>(#"sprites/pongbat");
middle = Content.Load<Texture2D>(#"sprites/pongmiddle");
//Set default sprite positions
midPos.X = (Window.ClientBounds.Width / 2) - 5;
midPos.Y = 0;
bat1Pos.X = 10;
bat1Pos.Y = (Window.ClientBounds.Height/2) - 50;
bat2Pos.X = Window.ClientBounds.Width - 20;
bat2Pos.Y = (Window.ClientBounds.Height/2) - 50;
ballPos.X = (Window.ClientBounds.Width / 2) - 5;
ballPos.Y = (Window.ClientBounds.Height / 2) - 5;
//Generate random ball velocity
ballVelo.X = generator.Next(5,10);
ballVelo.Y = generator.Next(4, 7);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
//Update rectangle values
Rectangle bat1Rect = new Rectangle((int)bat1Pos.X, (int)bat1Pos.Y, 10, 100);
Rectangle bat2Rect = new Rectangle((int)bat2Pos.X, (int)bat2Pos.Y, 10, 100);
Rectangle ballRect = new Rectangle((int)ballPos.X, (int)ballPos.Y, 10, 100);
//Move ball
ballPos += ballVelo;
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
this.Exit();
//Bat 1 movement and restriction
if (Keyboard.GetState().IsKeyDown(Keys.Up))
bat1Pos.Y -= 4;
if (Keyboard.GetState().IsKeyDown(Keys.Down))
bat1Pos.Y += 4;
if (bat1Pos.Y <= 0)
bat1Pos.Y = 0;
if (bat1Pos.Y >= Window.ClientBounds.Height - 100)
bat1Pos.Y = Window.ClientBounds.Height - 100;
//Bat 2 movement and restriction
if (Keyboard.GetState().IsKeyDown(Keys.W))
bat2Pos.Y -= 4;
if (Keyboard.GetState().IsKeyDown(Keys.S))
bat2Pos.Y += 4;
if (bat2Pos.Y <= 0)
bat2Pos.Y = 0;
if (bat2Pos.Y >= Window.ClientBounds.Height - 100)
bat2Pos.Y = Window.ClientBounds.Height - 100;
//Ball movement restrictions
if (ballPos.X <= 0)
ballVelo.X *= -1;
if (ballPos.Y <= 0)
ballVelo.Y *= -1;
if (ballPos.X >= Window.ClientBounds.Width - 5)
ballVelo.X *= -1;
if (ballPos.Y >= Window.ClientBounds.Height - 5)
ballVelo.Y *= -1;
//Collision detection between bats and ball
if (ballRect.Intersects(bat1Rect))
{
ballVelo.X *= -1;
}
if (ballRect.Intersects(bat2Rect))
{
ballVelo.X *= -1;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
spriteBatch.Draw(middle, midPos, Color.White);
spriteBatch.Draw(bat1, bat1Pos, Color.White);
spriteBatch.Draw(bat2, bat2Pos, Color.White);
spriteBatch.Draw(ball, ballPos, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Lyise's solution will work, but it does not attack the actual source of the problem. In bigger games you will require a more robust solution, so I'll explain the high level fix.
The issue is that you are changing the X velocity while the ball is still inside the bat. In the next frame, the most probable outcome is that the ball will move away, but not enough to exit the bat, so a new collision is detected. The speed is changed again, and the cycle repeats until the ball exits the bat by going below or above it.
The precise solution requires moving the ball out of the bat and then inverting the speed.
Options:
Return the ball to where it was at the beginning of the frame. The ball will never touch the bats. If the ball speed is high enough or the frame rate low enough, this will be noticeable.
Calculate how far into the bat the ball has gone, and substract that amount from the ball's position.
// Colliding from the right
impactCorrection = bat.Right - ball.Left;
// Colliding from the left
impactCorrection = bat.Left - ball.Right;
For extra points you might move the ball away 2 * impactCorrection in X so the ball always travels the same distance every frame.
One way around this would be to do make the following change to your ballRect.Intersect(barXRect)) if statements:
if (ballRect.Intersects(bat1Rect))
{
ballVelo.X = Math.Abs(ballVelo.X) * -1;
}
if (ballRect.Intersects(bat2Rect))
{
ballVelo.X = Math.Abs(ballVelo.X);
}
This way the left bar will only send the ball right, and the right bar will only send it left. I may have the bars around the wrong way, so it might be best to double check, but it just means moving the * -1 to the other bat.

Categories