i just got into game development and XNA and was following a tutorial and decided to try and add in a bound area for a floor. In the tutorial the sprite could move freely and i wanted to have a stopping point for it, so i added a statement to part of the input method
if (aCurrentKeyboardState.IsKeyDown(Keys.Down) == true)
{
if (this.Position.Y == 420)
{
MOVE_DOWN = 0;
mDirection.Y = MOVE_DOWN;
}
else
{
mSpeed.Y = PLAYER_SPEED;
MOVE_DOWN = 1;
mDirection.Y = MOVE_DOWN;
}
}
MOVE_DOWN is my variable for the y change, if it = 0, there is no movement, 1 it moves down, -1 it moves up.
this worked only if the position of the bounds(420) was equal to the position that my sprite started out at, other than that it doesnt work.
i think its because the position isnt updating correctly. i dont know ive tried a lot of things and am pretty new with XNA and game development. Any help would be greatly appreciated.
Here is the update method for my player sprite
public void Update(GameTime theGameTime)
{
KeyboardState aCurrentKeyboardState = Keyboard.GetState();
UpdateMovement(aCurrentKeyboardState);
mPreviousKeyboardState = aCurrentKeyboardState;
base.Update(theGameTime, mSpeed, mDirection);
}
and here is the update for the base class
public void Update(GameTime theGameTime, Vector2 theSpeed, Vector2 theDirection)
{
Position += theDirection * theSpeed * (float)theGameTime.ElapsedGameTime.TotalSeconds;
}
If 'Position' is a Vector2, then it is using floats of the X & Y components.
For practical purposes, a float will rarely equal a whole number due to floating point rounding errors.
if (this.Position.Y == 420)//will never return true
should be changed to:
if (this.Position.Y < 420)
{
this.Position = 420;
//other stuff you have
}
Related
I have a sprite, Player. I update Player's position the following way:
_position += (_direction * _velocity) * (float)gameTime.ElapsedGameTime.TotalSeconds;
Where _position, _direction and _velocity are Vector2s.
I have a simple collider(BoxCollider) which simply generates a rectangle (BoundingBox) given the position and dimensions of the collider.
In Initialize(), I create a new List<BoxCollider> and fill it with colliders for the level.
On Update(), I pass the list to Player to check for collisions.
The collision check method:
public void CheckPlatformCollision(List<BoxCollider> colliders, GameTime gameTime)
{
var nextPosition = _position + (_direction * _velocity) * (float)gameTime.ElapsedGameTime.TotalSeconds;
Rectangle playerCollider = new Rectangle((int)nextPosition.X, (int)nextPosition.Y, BoundingBox.Width, BoundingBox.Height);
foreach(BoxCollider collider in colliders)
{
if(playerCollider.Intersects(collider.BoundingBox))
{
nextPosition = _position;
}
}
Position = nextPosition;
}
Right now, every way I've tried to implement gravity has failed. If Player is dropped from too high, nextPosition becomes too far away from Player and leaves it stuck in mid-air.
I'm also having problems with horizontal collisions as well, the issue being similar: Player stops too soon, leaving a space between. Sometimes I've had Player stick to the side of the collider.
What I would like to know is:
How do I properly implement gravity & jumping with _position, _direction, and _velocity? How do I properly handle collisions both horizontally and vertically?
For gravity, add this just before you update _position:
_velocity += gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
Where gravity is something like new Vector2(0, 10).
For jumping, you need to set the vertical component of the velocity when the player presses the jump button:
if (jumpPressed && jumpAvailable)
{
_velocity.Y = -10; // Numbers are example. You need to adjust this
jumpAvailable = false;
}
And you need to reset jumpAvailable when the player touches the floor.
Colliding is a much complicated thing. But if you look for "XNA implement collision" on the internet you will find a lot of answers.
There are many ways. One of them is pushing back the player to the border of the boxcollider, instead of not letting him move, like you did in your code. The code would be:
if (IntersectsFromTop(playerCollider, collider.BoundingBox))
{
_position.Y = collider.BoundingBox.Y - BoundingBox.Height;
}
else if (IntersectsFromRight(playerCollider, collider.BoundingBox))
{
_position.X = collider.BoundingBox.X + collider.BoundingBox.Width;
}
// And so on...
The helper methods can implemented like:
private static bool IntersectsFromTop(Rectange player, Rectangle target)
{
var intersection = Rectangle.Intersect(player, target);
return player.Intersects(target) && intersection.Y == target.Y && intersection.Width >= intersection.Height;
}
private static bool IntersectsFromRight(Rectange player, Rectangle target)
{
var intersection = Rectangle.Intersect(player, target);
return player.Intersects(target) && intersection.X + intersection.Width == target.X + target.Width && intersection.Width <= intersection.Height;
}
// And so on...
The rationnale behind that code can be explained with a picture:
In that picture width and height correspond to the intersection, in purple.
I'm writing my own Pong game in C# and I'm having some issues with my ball not resetting correctly. I've spent a good hour or two debugging the code but I can't figure it out.
Basically what's supposed to happen is that when the ball is detected to leave the bounds of the window then the Center() method is going to get called and Center() then resets the ball Point by accessing the backing field directly. Now this works, I've verified that it does what it's supposed to by stepping through the code.
Now the weird that happens is that after Center() gets called the ball position reverts back to what it used to be pre-centering. Now the weird thing is that, this reset happens before the set accessor of the Point property is even called. And I'm 100% sure that I'm not accesing the backing field directly in any other place than Center() so I can't figure it out.. Here's the code
namespace Pong
{
internal enum CollisionType
{
Paddle, Boundary
}
class Ball
{
private readonly IGameView view;
private readonly IGameController controller;
private int velocity = 10;
private double angle;
private event CollisionHandler Collision;
private Point _point;
public Point Point
{
get { return _point; }
set
{ // If UpdatePosition() tries to move the ball beyond the boundaries in one tick move the ball to the boundaries
if(value.Y > view.Boundaries.Height || value.Y < 0)
{
_point = new Point(value.X, view.Boundaries.Height);
Collision(CollisionType.Boundary); // Also raise the collision event
}
//If the ball is going to pass the X boundaries of the map then a player should score and the ball should reset
if (value.X > view.Boundaries.Width || value.X < 0)
{
if (angle > 90) // If the angle of the ball of the ball is above 90 degrees then the left paddle was the shooter
{ // So he should score
var scoringPlayer = Array.Find(controller.Players, player => player.Paddle.Orientation.Equals(Orientation.Left));
controller.PlayerScore(scoringPlayer);
Center(scoringPlayer);
}
else // If not, then it's the right paddle
{
var scoringPlayer = Array.Find(controller.Players, player => player.Paddle.Orientation.Equals(Orientation.Right));
controller.PlayerScore(scoringPlayer);
Center(scoringPlayer);
}
}
// If the ball will collide with a player paddle then raise collision event
if (controller.Players.Any(player => player.Paddle.Position.Equals(value)))
{
Collision(CollisionType.Paddle);
_point = value;
}
_point = value;
}
}
public Ball(IGameView view, IGameController controller)
{
this.view = view;
this.controller = controller;
}
public void Center(Player server)
{
//Center the ball, acccess the backing field directly to avoid all the conditional logic in the property
_point = new Point(view.Boundaries.Width / 2, view.Boundaries.Height / 2);
//The ball will start moving from the center Point towards one of the different sides
/*TODO: Apparently the ball moves sideways down towards one of the player paddles, so we must implement more complex
logic to calculate the starting angle of the ball */
angle = (server.Paddle.Orientation.Equals(Orientation.Left)) ? 0 : 180;
}
public void UpdatePosition()
{
//Called to update ball position based on velocity and angle of ball
//For now let's just implement a very primitive movement algorithm
if (angle < 90)
{
Point = new Point(Point.X + velocity, Point.Y);
}
else
{
Point = new Point(Point.X - velocity, Point.Y);
}
}
}
//TODO: Add collision detection through BallCollision Event and implement BallCollisionHandler(CollisionType as Object args)
You set _point = value immediately after you call Center - you need to ensure that your backing field isn't updated after you set it during Center
public Point Point
{
get { return _point; }
set
{ // If UpdatePosition() tries to move the ball beyond the boundaries in one tick move the ball to the boundaries
if(value.Y > view.Boundaries.Height || value.Y < 0)
{
_point = new Point(value.X, view.Boundaries.Height);
Collision(CollisionType.Boundary); // Also raise the collision event
}
//If the ball is going to pass the X boundaries of the map then a player should score and the ball should reset
if (value.X > view.Boundaries.Width || value.X < 0)
{
if (angle > 90) // If the angle of the ball of the ball is above 90 degrees then the left paddle was the shooter
{ // So he should score
var scoringPlayer = Array.Find(controller.Players, player => player.Paddle.Orientation.Equals(Orientation.Left));
controller.PlayerScore(scoringPlayer);
Center(scoringPlayer);
}
else // If not, then it's the right paddle
{
var scoringPlayer = Array.Find(controller.Players, player => player.Paddle.Orientation.Equals(Orientation.Right));
controller.PlayerScore(scoringPlayer);
Center(scoringPlayer);
}
}
// If the ball will collide with a player paddle then raise collision event
if (controller.Players.Any(player => player.Paddle.Position.Equals(value)))
{
Collision(CollisionType.Paddle);
_point = value;
}
_point = value; // <--- This always gets set - even after you call Center above
}
}
Edit:
As Sayse pointed out whilst this is the issue you are getting, it might be useful to move this logic into an 'update' part of the code - this logic seems like it should be part of the IGameController implementation or a IBallController maybe; either that or put an Update method into your Ball class which is called by the game controller when it wants to update all game objects
Property setters ideally shouldn't be side-effecting
This is a follow up question to a question that I posted last night. I have to write a game in a Windows Form for school, and I am creating a maze game in which a player must navigate through a maze before they are killed. As a maze game, some collision detection must be used to make sure that the player doesn't simply run through the walls (this would be quite a boring game). I've implemented a feature which prevents this based on the question that I asked last night, but I'm getting some weird results.
When the player touches a wall, the game stops them, and the player ends up getting stuck. The player CANNOT move unless they press a combination of keys to move through the wall (my game uses WASD, so if I touch a wall, I can press W + A and go through the wall to the other side where my player gets unstuck).
This is my collision code:
// This goes in the main class
foreach (Rectangle wall in mazeWalls)
{
if (playerRectangle.IntersectsWith(wall))
{
player.Stop();
}
}
This is the player's movement code:
public void Move(Direction dir)
{
// First, check & save the current position.
this.lastX = this.x;
this.lastY = this.y;
if (dir == Direction.NORTH)
{
if (!CheckCollision())
{
this.y -= moveSpeed;
}
else
{
this.y += 1;
}
}
else if (dir == Direction.SOUTH)
{
if (!CheckCollision())
{
this.y += moveSpeed;
}
else
{
this.y -= 1;
}
}
else if (dir == Direction.EAST)
{
if (!CheckCollision())
{
this.x += moveSpeed;
}
else
{
this.x -= 1;
}
}
else if (dir == Direction.WEST)
{
if (!CheckCollision())
{
this.x -= moveSpeed;
}
else
{
this.x += 1;
}
}
}
My CheckCollision() method:
private bool CheckCollision()
{
// First, check to see if the player is hitting any of the boundaries of the game.
if (this.x <= 0)
{
isColliding = true;
}
else if (this.x >= 748)
{
isColliding = true;
}
else if (this.y <= 0)
{
isColliding = true;
}
else if (this.y >= 405)
{
isColliding = true;
}
else if (isColliding)
{
isColliding = false;
}
// Second, check for wall collision.
return isColliding;
}
The Stop() method:
public void Stop()
{
this.x = lastX;
this.y = lastY;
}
Here is a gif that I have uploaded so that you can see the behavior of the player with the maze walls. Notice how he slides through the walls and repeatedly gets stuck.
My question is how do I get this player to stop sticking and actually be able to slide and move with the walls? I've tried multiple collision patterns, and used last night's (very helpful) answer, but he won't stop sticking to the walls! Let me know if you need any other details/information.
EDIT: The Input code, as requested by Dan-o: http://pastebin.com/bFpPrq7g
Game design is a complicated subject. There are some pretty well documented strategies for implementing 2D maze games. The most anyone can do here is to make general suggestions based on what you've done knowing that a commercial game is not what trying to make here. So anyhow, I'll throw in my two cents. I've only done anything like this in OpenGL, not with Windows Forms.
The first thing I see is that your sprite doesn't stay centered in it's lanes. Calculating that will ultimately make things easier because there are always only 4 possible directions that the player can be moving. North, South, East, and West don't mean as much when diagonal is also possible.
Design your gameboard so that your walls and your lanes are the same width. Use evenly spaces rows and columns. Since you're using a fixed board size, this should be as simple as dividing your width and height by the number of rows and columns you want.
Once you've done this, you will always know what lane you're in based on your x and y coordinates. If your columns are 80 pixels wide, for instance, and you're at x = 160 then you know you're in column 2 (1 in a 0-based array). This is the only way you are going to be able to put enemies on the board and be able to programmatically track where they are going. Also, you'll be able to size your players and enemies appropriately for the lanes.
Let's say your rows and columns are 80Wx60H pixels (they can be whatever you like best). Now let's say that your player is moving East starting from 0, 160. And, he can move North and South when he gets to column 3 (2 in a zero-based model), according to your map. Meaning, when he gets to 160, 160. Remember, if x = 0 starts column 1, then 80 starts column 2, and so on.
Forgetting collisions for a moment, you could use logic like this.
RectangleF playerRectangle = new RectangleF();
int COLUMN_WIDTH = 80;
int ROW_HEIGHT = 60;
if (playerRectangle.IntersectsWith(wall)){
int column = playerRectangle.X / COLUMN_WIDTH;
//----------------------------------------------
// This will return false if the player
// is not positioned right at the column. The
// result of % will contain decimal digits.
// playerRectangle.X has to be a float though.
//----------------------------------------------
if(column % 1 == 0){
//--------------------------------------------
// do this based on your keyboard logic. this
// is pseudo-code
//--------------------------------------------
if(keys == keys.UP){
// Move up
}
else if(keys == keys.DOWN){
// Move down
}
}
}
Another thing you'll want to do is to store an orientation property with your walls. That way when you collide with one, you'll know what your options are for movement. If the wall you collide with is WallOrientation.NorthSouth, you can only move North or South once you hit it. Or, you can go in reverse to the direction you hit it from. If no keys are pressed you sit still. Some thing like this will do.
public enum WallOrientation{
NorthSouth,
EastWest
}
None of this code is tested. I just wrote it on the fly so there could be mistakes. I'm just trying to give you some ideas. I hope this advice helps you out some.
I'm developing a 2D game with XNA game studio 4.0 and I need to make my "Hero" sprite shoot a shot sprite, which is a rectangle.
When I press left control to shoot, the shot is starting from my player. So far, it's ok, but the problem is that it never stops - its position never goes to theVoid.
Here is my code for shooting:
protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.LeftControl) && isShotted == false)
{
isShotted = true;
shotPosition = playerPosition;
}
if (isShotted == true && (shotPosition.X <= shotPosition.X+150) )
{
shotPosition.X += shotSpeed.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
else
{
isShotted = false;
shotPosition = theVoid;
}
}
Some clarification:
playerPosition is my "Hero" sprite position.
theVoid is Vector2 (700,700), when I set shotPosition = theVoid the shot dissapears.
The shot never disapears because you are updating shotPosition.x every update. You are checking:
if (isShotted == true && (shotPosition.X <= shotPosition.X+150) )
And then inside that if you increment shotPosition.X:
shotPosition.X += shotSpeed.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
One option to fix this would be to check shotPosition.X against the player position - per #jonhopkins comment. If the player can move near the same speed as the shot though, they could just follow it and then the shot would never disappear, this may or may not be what you want.
Your other option is to store the position of where the shot was fired, and compare something like:
if (isShotted == true && (shotPosition.initialX+150 >= shotPosition.currentX) )
Make sure you think about this in terms of how your players and objects move around the coordinate system though. If your player is always stationary in regards to the x-axis that could simplify things compared to if they can run around the screen..
I'm trying to make jumping functionality in my Movement test. My character jumps and comes back down, but it's very choppy and not smooth at all.
What happens is he juts up to his max height, then comes down smoothly.
I can spot the problem, the for loop doesn't want to play nicely with the code. However, I don't know how to circumvent this. Is there any way to keep the button press and have him jump up nicely?
Code:
if (leftStick.Y > 0.2f && sprite.Position.Y == position.Y || isPressed(Keys.Up) == true && sprite.Position.Y == position.Y)
{
if (wasLeft == true)
{
sprite.CurrentAnimation = "JumpLeft";
}
else if (wasLeft == false)
{
sprite.CurrentAnimation = "JumpRight";
}
//This for loop is my issue, it works but it's jumpy and not smooth.
for (movement.PlayerHeight = 0; movement.PlayerHeight < movement.PlayerMaxHeight; movement.PlayerJump())
{
sprite.Position.Y -= movement.PlayerJump();
}
}
sprite.StartAnimation();
}
else
{
leftStick = NoInput(leftStick);
}
private Vector2 NoInput(Vector2 leftstick)
{
if (sprite.Position.Y < position.Y) //(movement.PlayerSpeed > 0)
{
sprite.Position.Y += movement.PlayerHeight;
movement.PlayerHeight -= movement.Player_Gravity;
//sprite.Position.Y += movement.PlayerSpeed;
//movement.PlayerSpeed -= movement.Player_Decel;
}
else
{
sprite.Position.Y = position.Y;
}
}
Movement class:
public float PlayerMaxHeight = 15f;
public float PlayerHeight = 0;
public float Player_Gravity = 0.01f;
private const float Player_Jump = 0.35f;
public float PlayerJump()
{
PlayerHeight += Player_Jump + Player_Gravity;
if (PlayerHeight > PlayerMaxHeight)
{
PlayerHeight = PlayerMaxHeight;
}
return PlayerHeight;
}
The best way to do jumping I found is to implement a property that will deal with acceleration.
A brief list of what to do:
Create a property that stores the current Y velocity.
Increment the Y velocity by a set amount each step - generally represented by a gravity property somewhere.
Increment1 the Y position by the Y velocity each step.
When you jump, simply subtract1 a said amount from the Y velocity - which will cause your player to jump up in an easing-out motion (start fast and slow down as he reaches the high of the jump). Because you're always incrementing the Y velocity, you will eventually reverse direction and return back to the surface.
When touching a surface, reset the Y velocity to zero.
1 Pretty sure that the Y axis is inverted in XNA (I work in Flash), so where I say increment the Y velocity you may need to decrement it instead - same deal for subtracting from it to jump.
My general approach to get a jump really quickly is to use a bleed off value to make slightly smoother looking movement. I can't look at any code/xna right now but my first thought would be something like below.
Define variables:
float bleedOff = 1.0f;
bool jumping = false;
Input update:
if(input.JumpKey())
{
jumping = true;
}
Jumping update:
if(jumping)
{
//Modify our y value based on a bleedoff
//Eventually this value will be minus so we will start falling.
position.Y += bleedOff;
bleedOff -= 0.03f;
//We should probably stop falling at some point, preferably when we reach the ground.
if(position.Y <= ground.Y)
{
jumping = false;
}
}
bleedOff = MathHelper.Clamp(bleedOff, -1f, 1f);
Obviously the bleedOff value should be calculated with a bit more randomness, probably using a gravity value, to it to make it look right but this will give the illusion of acceleration/decceleration with the jump as they rise and fall.
Rising very fast to begin with and slowing down and eventually starting to fall again and that will speed up. The clamp at the bottom will be your maximum vertical velocities.
I just wrote this off the top of my head at work so apologies if it's not quite what your looking for but I tried to keep it a bit more general. Hope it helps.