I'm a starter in XNA and I'm trying to make a pong-game.
I've been able to make a pong game, but all the code was in one class. So I wanted to try to add a little bit of OOP and I've made a class for the ball and one for the pad.
The ball moves perfectly, but I don't seem to able to make the ball bounce from the pads.
These are the codes I use:
To move the pads
Game1.cs
#region left
if (_KBS.IsKeyDown(Keys.W) || _KBS.IsKeyDown(Keys.Z))
Left.MoveUp();
else if (_KBS.IsKeyDown(Keys.S))
Left.MoveDown();
#endregion
#region right
if (_KBS.IsKeyDown(Keys.Up))
Right.MoveUp();
else if (_KBS.IsKeyDown(Keys.Down))
Right.MoveDown();
#endregion
pad.cs
public void MoveUp() {
if (!paused)
RecPad.Offset(0, -speed);
CheckBorders();
}
public void MoveDown() {
if (!paused)
RecPad.Offset(0, speed);
CheckBorders();
}
private void CheckBorders() {
MathHelper.Clamp(recPad.Y, borders.Top, borders.Bottom - recPad.Height);
}
To check if the ball bounces
ball.cs
public void CheckBounce() {
if ((myBounds.Intersects(left) && movement.X < 0) || (myBounds.Intersects(right) && movement.X > 0))
movement.X *= -1;
}
public void Draw(SpriteBatch sBatch, Texture2D texture, Color color, Rectangle left, Rectangle right) {
this.left = left;
this.right = right;
Move();
sBatch.Begin();
sBatch.Draw(texture, myBounds, color);
sBatch.End();
}
pad.cs
public Rectangle RecPad {
get { return recPad; }
private set { recPad = value; }
}
Game1.cs
Ball.Draw(spriteBatch, ball, Color.White, Left.RecPad, Right.RecPad);
I seemed to get the pads back to work
The problem seems to be solved by using the originel recPad instead of the constructor RecPad
Now I only need to get my boundries working, because the MathHelper.Clamp doesn't seem to work
See my code for more info
This code now fixed my border-problem
private void CheckBorders() {
if (recPad.Top < borders.Top)
recPad.Location = new Point(recPad.X, borders.Top);
if (recPad.Bottom > borders.Bottom)
recPad.Location = new Point(recPad.X, borders.Bottom - recPad.Height);
}
This immediately stands out to me (from CheckBounce):
movement.X *= 1;
Could be a copying error, or you forgot to put a '-'.
Also, consider using the Rectangle.Contains/Intersects method(s) to streamline some collision code, and MathHelper.Clamp to keep your paddles in bounds. This is more just for future reference, since your methods work, but it's nice to take advantage of the helpful tools in XNA.
Edit: Concerning those 'helpful tools':
The Rectangle class has the methods Intersect and Contains, which can tell you if that rectangle is intersecting another rectangle or containing a certain point, respectively. You say your ball is just a top left position and a texture, but I see in your collision checking you also check for the radius of the ball. I think you'd have an easier time defining a Rectangle bounding area for your ball and using the Intersects method to check for collision. That simplifies your collision code to:
public void CheckBounce()
{
if (myBounds.Intersects(LeftPaddle.Bounds) || myBounds.Intersects(RightPaddle.Bounds))
movement.X *= -1;
}
Fairly simple, but not entirely safe-- if the ball manages to move far enough into a paddle that one frame of movement wouldn't free it from that paddle's bounds, you'd be stuck perpetually inverting the X velocity, producing a 'stuck ball' effect. So we can add just a bit more checking code to avoid that:
public void CheckBounce()
{
if ((myBounds.Intersects(LeftPaddle.Bounds) && movement.X < 0) ||
(myBounds.Intersects(RightPaddle.Bounds) && movement.X > 0))
movement.X *= -1;
}
I apologize if the inline conditionals are a little too dense. What this means is, if the ball is moving left and hits the right paddle, invert the X. Likewise, if it's moving right and hits the left paddle, invert the X. This removes the 'sticking'.
Now, as for MathHelper.Clamp, in your situation I would use it to restrict paddle movement. MathHelper.Clamp simply clamps a value between an upper and lower bound. It's the equivalent of using Math.Min, then Math.Max.
private void CheckBorders()
{
//clamps a value to a min and a max
MathHelper.Clamp(recPad.Y, borders.Top, borders.Bottom - recPad.Height);
}
This clamps the Y position of your rectangle between the top of your borders, and the bottom of your borders minus the height of the rectangle. That last bit keeps the bottom of your rectangle from clipping the bottom of the borders by taking the height into account.
Related
Still pretty new to Unity and C#. I can usually find my way around when it comes to setting/changing the position of an object, but when it comes to rotation it's still mind boggling to me, with quaternion, eulerangles etc..
I'm trying to check if an object (a head) is facing a certain direction, if it's not facing that direction it needs to set it to that direction depending which way it goes (up, down, left or right). This is for a snake game (like the old top down nokia game) I'm currently developing.
I tried to look it up, but none of the answers are specific to what I'm trying to do.
As of right now this is what I have for player input (a snippet of code):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left)
{
snakeHeadRotation.transform.Rotate(0, 180, 0); // Keeps rotating when left is pressed
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As you can see the snakeHeadRotation.transform.Rotate(0, 180, 0); is getting called each time the player presses the left key for instance, thus turning the head of the snake even when the snake is still traveling to the left.
My logic would be to store the rotation value somewhere (type Quaternion/bool maybe) and check to see if that rotation value matches up with the current rotation value set to left (which is snakeHeadRotation.transform.Rotate(0, 180, 0);) If that's the case, don't do anything. If it isn't, set it to snakeHeadRotation.transform.Rotate(0, 180, 0); for instance.
the code would look something like (writing it out):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left && rotation is not equal to snakeHeadRotation.transform.Rotate(0, 180, 0))
{
snakeHeadRotation.transform.Rotate(0, 180, 0);
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As said, I'm very unfamiliar with setting or storing rotation values. How can I achieve this? Is this even the right approach or is there a more logical one? I can't figure this one out myself..
I hope I explained it right, I'm usually terrible at explaining things.
Using Transform.Rotate(x) will rotate your head by x degrees relatively to your current head direction.
What you want I suppose is to set the absolute rotation of your head depending on the arrow key you pressed. Thus, even if you press multiple times the same arrow key, the head will stay in the correct direction.
To do that you can create 4 rotations for each head direction and so use them as needed.
For example, if your head is facing forward by default:
// create your directions
private static Quaternion forwardDirection = Quaternion.Identity;
private static Quaternion rightDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.right);
private static Quaternion leftDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.left);
private static Quaternion backDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.back);
...
private int GetAxisRaw(Axis axis)
{
...
if (left)
{
// set the absolute direction of your head to the left
snakeHeadRotation.transform.rotation = leftDirection;
return -1;
}
if (right)
{
// set the absolute direction of your head to the right
snakeHeadRotation.transform.rotation = rightDirection;
return 1;
}
...
}
I would just create 4 quaternions for each direction and set the rotation according to the key pressed (so like hear.rotation = leftQ). This is the simplest way.
I am away from my PC currently but I will try to find another solution when I can
You could compare the current rotation to the known rotations for the 4 directions.
But don't go that way, it's bad practice for several reasons:
what if later you want to add animations, e.g. the head turns in .5 seconds, not instantly?
comparing 4 floats only to check one of 4 possible directions is inefficient.
Create a enum instead:
public enum Direction
{
Up,
Down,
Left,
Right
}
Add a member of this type to your behavior, to keep track of where your snake head is going.
Something like:
Direction direction;
...
private int GetAxisRaw(Axis axis)
{
switch (direction)
{
case Direction.Up:
if (left && !right)
direction = Direction.Left;
... turn snake left ...
else if (right && !left)
direction = Direction.Right;
... turn snake right ...
// ignore inputs for up or down if going up
break;
... repeat for the other directions and possible inputs ...
}
...
}
(Before you read, I'm very nervous about posting here since my previous question got a lot of negative responses... Please try and be nice. I am a student and can't write "Perfect" code yet)
I'm currently trying to figure out how to make a mosquito (Texture2D) swoop downwards and then come back up to it's original position.
Currently the mosquitoes simply move left and right within the screen bounds, with no Y movement.
I've stepped it through a debugger and observed it's Y coordinate. My mosquitoes are indeed swooping down and then back upwards again... However, they do it so quickly that it isn't visible to the human eye.
Therefore I need a way to smoothly swoop them down so that it's actually visible.
I am currently unable to embed images into my posts, however I have made a GIF demonstrating the effect that I want.
Here's a link to my GIF
Things I've tried:
Copied the first line of my movement code (seen below) and changed it so that it would only affect the Y coordinates.
Result: Mosquitoes were still swooping too fast to see.
Changing my mosquitoe's velocity.Y to include a Y that isn't 0, so that my movement code will also change the Y position instead of just the X position.
Result: Game was stuck in an infinite loop since my movement code is found in the Update() function, and the code never got out of the loop so that it could update the position...
Here's the movement code I have in my Update.
The mosquitoes move all the way to the right, then all the way to the left.
internal void Update(GameTime GameTime)
{
position += velocity * (float)GameTime.ElapsedGameTime.TotalSeconds;
// Reverses the direction of the mosquito if it hits the bounds
if (position.X <= gameBoundingBox.Left ||
position.X + animationSequence.CelWidth > gameBoundingBox.Right)
{
velocity.X *= -1;
}
}
And here's the actual Swoop() code that I have...
internal void Swoop(GameTime gameTime)
{
float originalYPosition = position.Y;
bool currentlySwooping = true;
while (currentlySwooping)
{
position.Y++;
if (this.BoundingBox.Bottom >= gameBoundingBox.Bottom)
{
currentlySwooping = false;
}
}
while (!currentlySwooping && position.Y > originalYPosition)
{
position.Y--;
}
}
I don't ask questions on here too often, so I'm sorry if I'm using an incorrect format or if I've given too little information.
If you need to know anything else then please let me know.
There are many ways of implementing this, but this should do it.
float originalYPosition = position.Y;
int swoopDirection = 0;
internal void Update(GameTime GameTime)
{
/* Same update code */
if (swoopDirection != 0)
{
position.Y += swoopDirection;
if (swoopDirection == 1 && this.BoundingBox.Bottom >= gameBoundingBox.Bottom)
{
swoopDirection = -1;
}
else if (swoopDirection == -1 && position.Y <= originalYPosition)
{
swoopDirection = 0;
}
}
}
internal void Swoop()
{
swoopDirection = 1;
}
Basically, we have a variable swoopDirection that changes depending on the state of the swoop (1 when going down and -1 when going up). In the update method we check that swoopDirection != 0 (we are swooping). If we are swooping we add the direction to the Y axis and check that we aren't out of bounds. If we touch the bottom we set swoopDirection to -1 so we go up. If we are at the original position swoopDirection is set to 0 and we stop swooping.
I have created two Rectangles, you can move and jump with one of them, and the other one is standing still on the Form as an obstacle.
I want the obstacle to work as an obstacle(or a wall if you will), basically I want the movable rectangle to stop when its right side is colliding with the obstacle's left side(and so forth).
I found this code how to NOT detect collision(because it's apparently easier to not detect collision)between two rectangles in an article:
OutsideBottom = Rect1.Bottom < Rect2.Top
OutsideTop = Rect1.Top > Rect2.Bottom
OutsideLeft = Rect1.Left > Rect2.Right
OutsideRight = Rect1.Right < Rect2.Left
//or
return NOT (
(Rect1.Bottom < Rect2.Top) OR
(Rect1.Top > Rect2.Bottom) OR
(Rect1.Left > Rect2.Right) OR
(Rect1.Right < Rect2.Left) )
But I'm not sure how to implement it. I have a bool called "player1.left" which becomes true when I press 'A' on the keyboard('D' to move to the right, and 'W' to jump)which when true will move the rectangle 10 pixels to the left(in the Timer_Tick event).
EDIT:
"rect1.IntersectsWith(rect2)" works to detect collision. But how would I use that(what should go inside the if-statement) if I want the movable rectangle to stop moving to the right(but still be able to jump and move to the left)if its right side collides with the obstacle's left side(and so forth)?
//UPDATE
Let's say you have the class PlayableCharacter which inherits from Rectangle.
public class PlayableCharacter:Rectangle {
//position in a cartesian space
private int _cartesianPositionX;
private int _cartesianPositionY;
//attributes of a rectangle
private int _characterWidth;
private int _characterHeight;
private bool _stopMoving=false;
public PlayableCharacter(int x, int y, int width, int height)
{
this._cartesianPositionX=x;
this._cartesianPositionY=y;
this._chacterWidth=width;
this._characterHeight=height;
}
public bool DetectCollision(PlayableCharacter pc, PlayableCharacter obstacle)
{
// this a test in your method
int x=10;
if (pc.IntersectsWith(obstacle)){
Console.Writeline("The rectangles touched");
_stopMoving=true;
ChangeMovingDirection(x);
StopMoving(x);
}
}
private void ChangeMovingDirection(int x)
{
x*=-1;
cartesianPositionX+=x;
}
private void StopMoving(int x)
{
x=0;
cartesianPositionX+=x;
}
}
In the code I`ve given you, in a situation when the character is going to the right, the x value which is positive, the character will face the other direction. If he moves on the left, if he collides with an obstacle, he will then face the other direction.
With StopMoving, even though you make a script which runs over time in a loop for example, it will never make the character move.
I think this should give the basis for your work. If there are any issues, please comment on the solution I wrote and I'll do my best to help you out if it's in my reach.
I have to make a game with Windows Forms for school. My game consists of a user having to get through a maze. I'm trying to prevent my user from going straight through the walls using collision detection, but am getting stuck because of the varying shape of the rectangles being used to represent walls. Here's an image of the game. This question may be similar to this one, however with my movement I believe that it is quite different, as I don't have a grid system or graphical map laid out.
As you can see, the walls are fairly thick. Each wall is represented by a C# Rectangle, as is my Player image (the little yellow ghost). I know how to determine if the player is crossing through these walls using C#'s IntersectsWith(Rectangle r) method, but I'm not exactly sure how to use this information in order to handle the collision and STOP the player from moving through the walls at all.
Here's what I've tried:
This is my actual movement code. Because the game is built in WinForm, the movement is triggered by keyboard events such as OnKeyPressed and OnKeyUp
public void Move(Direction dir)
{
HandleCollision(); // Handle collision while player is trying to move.
if (dir == Direction.NORTH)
{
this.y -= moveSpeed;
}
if (dir == Direction.SOUTH)
{
this.y += moveSpeed;
}
if (dir == Direction.EAST)
{
this.x += moveSpeed;
}
if (dir == Direction.WEST)
{
this.x -= moveSpeed;
}
}
This is my collision method, HandleCollision():
private void HandleCollision()
{
// First, check to see if the player is hitting any of the boundaries of the game.
if (this.x <= 0)
{
this.x = 0;
}
if (this.x >= 748)
{
this.x = 748;
}
if (this.y <= 0)
{
this.y = 0;
}
if (this.y >= 405)
{
this.y = 405;
}
// Second, check for wall collision.
foreach (Rectangle wall in mazeWalls)
{
if (playerRectangle.IntersectsWith(wall))
{
if (player.X > wall.X) { player.X += wall.Width; }
else if (player.X < wall.X) { player.X -= wall.Width; }
else if (player.Y > wall.Y) { player.Y += wall.Height; }
else if (player.Y < wall.Y) { player.Y -= wall.Height; }
}
}
}
Now this code above kind of works. However, because the player's coordinates are having the wall's width/height added to it, this makes some weird collision teleportation across the map where the player ends up bouncing around. So what would be the most efficient way of going about implementing a collision detection system which can replace all the code within the if (playerRectangle.IntersectsWith(wall)) { block?
In the move, save the current position, perform the move, check for collision, and if true, restore the old position.
For this to work, HandleCollision would return a Boolean value, true for each successful test (successful=collision detected), false at the end if no condition was met. The method would not modify any x or y value at all. To reflect its new function, this method should be renamed to CheckCollision.
From the gif in your other post it seems that your playing field is divided into squares, and walls and sprite are composed of multiple squares? Then your move should proceed in increments of one square until the intended increment is met. For each square the collision has to be checked.
For a more advanced method with only one check per move you need some linear mathematics. The collision check will need the current point P and the increment d of the move. You need to check if between P and P+d the wall is crossed. This is usually indicated by a sign change in a linear condition. For a vertical wall W to the left of P, if d points to the wall, P.x+d.x-(W.x+W.width) will be negative. Determine s with W.x+W.width<=P.x+s*d.x. For horizontal walls, you have to do the same check in the y-coordinate. Take the minimal s over all objects hit and P+s*d as the new position.
i'm working on a small RPG game, and what i currently have is:
*Character with animation and basic collision;
*A 32x32 box which i test the collision on;
And thats all for now..xD
My character is 32xPixels wide, and 48xPixels tall, 32x38.
And i have Two rectangles, playerRectangle(for collision and movement);
And rectangleAnimation, for the character animation.
Well, my question is, how do i make a collision rectangle that only covers half of the character? Right now my rectangle is 32x48, so how do i make it 32x24, WITHOUT cropping the image?
It gives the illusion of being infront of something, for example a rock, or a tree.
I tried to make a seperate sprite, which is 32x24 and made a rectangle of it and drew two sprites in my player class, but that didn't work...
And my last question, should i make a seperate class, like CollisionHandler.cs for all my solid things, like walls, and boxes?
Here's my movement code for the player(without attempting to make a diffrent sized rectangle):
if (keyState.IsKeyDown(Keys.Left))
{
playerRectangle.X -= playerSpeed;
movingLeft = true;
movingRight = false;
}
else if (keyState.IsKeyDown(Keys.Right))
{
playerRectangle.X += playerSpeed;
movingRight = true;
movingLeft = false;
}
if (keyState.IsKeyDown(Keys.Up))
{
playerRectangle.Y -= playerSpeed;
movingUp = true;
movingDown = false;
}
else if (keyState.IsKeyDown(Keys.Down))
{
playerRectangle.Y += playerSpeed;
movingDown = true;
movingUp = false;
}
(I'm sorry that the code looks messy, i'm new to this site and i don't know how to structure it :/)
And my collision check(Which is in Game1.cs):
if(isColliding())
{
if (player.movingRight)
{
player.playerRectangle.X -= player.playerSpeed;
}
else if (player.movingLeft)
{
player.playerRectangle.X += player.playerSpeed;
}
if (player.movingUp)
{
player.playerRectangle.Y += player.playerSpeed;
}
else if(player.movingDown)
{
player.playerRectangle.Y -= player.playerSpeed;
}
}
I have a method in Game1.cs that's called "isColliding", which checks for collision in another class rectangle and my playerRectangle..
I'm sorry this is so long, but thank you in advance, and tell me if you need to know something else :)
i will write from my head... add this into your sprite class, it will be universal
public Rectangle BoundingBox
{
get { return new Rectangle((int)Position.X + offsetX, (int)Position.Y + offsetY, Texture.Width - offsetWidth, Texture.Height - offsetWidth); }
}
simply changing offsetX, offsetY, offsetWidth, offSetHeight you can adjust collision box. so if you wish collision on top left corner for 5x5 pixels. then offsetX=0, offestY=0, offsetWidth=5, offsetHeight=5
and collide code
foreach (Sprite brick in bricklist)
{
if (player.BoundingBox.Intersects(brick.BoundingBox))
{
// do some stuff
}
}
If you want to change the height of the player rectangle by half, all you do is just divide the playerRectangle.Y value by 2.
Use Vector2.Distance(playerPosition, objectToCheck);
if(Vector2.Distance(playerPosition,objectToCheck < player.rectangle.width/2){
//If the player is too close to the object you wish to check
//Do something...
}
For the collisions, i'd just check in your player's update method whether or not 'he' has collided with anything - using vector.distance^^
You're movement logic is bad:
You should be using velocity.
var velocity = new Vector2(0,0);
var moveSpeed = 5; //Whatever you want
if(player presses right){
velocity.X += moveSpeed;
}
if(player presses up){
velocity.y += moveSpeed;
}
this.position += velocity * Gametime.elapsedGametime; << Think Gametime is correct...
Var origin = new vector2(rectangle.width /2, rectangle.height/2); This will set the origin of the sprite to the center of the rectangle, as opposed to the top left.
Say we have two rectangles both 50x50... to check for a collision between the two you'd do:
if(Vector2.Distance(rectangle1.position, rectangle2.position < rectangle1.width/2){
//If the distance between the two rectangles (from the middle of the rectangle(the origin we just set is less than then half of the width of the rectangle... we have a collision.
}
Haven't used XNA in a few months, so you might have to update the origin in the update method of your classes...