Tile Engine Collision - c#

Okay, so, I am making a small tile-based digging game, now I want to do collision. How would I go about doing this correctly? I know how to check if the player collides with a tile, but I don't know how to actually make the player stop when it hits a wall.
This is the game, I got 20x20 tiles here.
This is the code I'm using atm:
foreach (Tile tiles in allTiles)
{
if (ply.rect.Intersects(tiles.rect))
{
if (tiles.ID != -1 && tiles.ID != 1)
{
if (ply.X > tiles.X)
{
Console.WriteLine("Right part.");
ply.X = tiles.pos.X + 30;
}
if (ply.X <= tiles.X)
{
Console.WriteLine("Left part.");
ply.X = tiles.pos.X - 30;
}
if (ply.Y > tiles.Y)
{
Console.WriteLine("Bottom part.");
ply.Y = tiles.pos.Y + 30;
}
if (ply.Y <= tiles.Y)
{
Console.WriteLine("Upper part.");
ply.Y = tiles.pos.Y - 30;
}
}
}
}

What type of collision detection are you using?
If your using Rectangles and the '.intersects' method you can always declare a bool to make sure your character is touching the floor. If he isn't you apply a Gravity Vector to make it fall to the next Tile with a different Rectangle so when he hits it he's going to stop falling.
If you want to block him from side to side just test to see which side of the rectangle he is touching and block him from moving on the 'X' axis.
E.g if he is going right and intersects with the left part of a rectangle, block is 'GoingRight' movement.
if(myCharacterRectangle.Intersects(tileRectangle)
{
if(myCharacterPosition.X > (tilePosition.X)
{
//You know the character hits the Right part of the tile.
}
if(mycharacterPosition.X <= tilePosition.X)
{
//You know the character hits the Left Part of the tile.
}
}
And same goes for the Position.Y if you want to test the Top or Bottom.
If you want to use Pixel by Pixel collision detection using Matrices I know a good tutorial here.
The detection will return a 'Vector2(-1,-1)' if there is no collision.
If there is a one the method will return the coordinates of the collisions which makes it even easier to determine what part of the tile your character is touching.
Hope this helps. Good Luck with your game.

Related

Mouse drag direction tolerance

I'm working on a 2D game where the player can drag tiles around. It works in a way that the player clicks and hold a tile and depending in which direction the player moves the mouse from then on, the drag direction is decided.
The problem however is that this is overly sensitive. It might often be the case that the player starts dragging and wanted to drag vertically but due to the mouse sensitivity it turns out to drag horizontally (or vice versa).
Does anyone have an idea how to add a tolerance threshold to this dragging behavior? The relevant part in my code looks basically like this:
private void Update()
{
if (_isMouseDown && sourceTile != null)
{
_isDraggingTile = true;
/* Determine drag direction and drag target cell. */
Vector3 dragDistance = Input.mousePosition - _originMousePosition;
dragDistance.Normalize();
if (_dragDirection == DragDirection.None)
{
float f = Vector3.Dot(dragDistance, Vector3.up);
/* Mouse up drag. */
if (f >= 0.5f)
{
_dragDirection = DragDirection.Up;
_dragTargetCell = sourceTile.gridCell.upNeighbor;
}
/* Mouse down drag. */
else if (f <= -0.5f)
{
_dragDirection = DragDirection.Down;
_dragTargetCell = sourceTile.gridCell.downNeighbor;
}
else
{
/* Mouse right drag. */
f = Vector3.Dot(dragDistance, Vector3.right);
if (f >= 0.5f)
{
_dragDirection = DragDirection.Right;
_dragTargetCell = sourceTile.gridCell.rightNeighbor;
}
/* Mouse left drag. */
else if (f < -0.5f)
{
_dragDirection = DragDirection.Left;
_dragTargetCell = sourceTile.gridCell.leftNeighbor;
}
}
}
if (_dragTargetCell != null)
{
// Take care of moving the dragged tile!
}
}
}
Simply delaying the calculation of dragDistance by some frames doesn't turn out to work very well. I think what is needed is a solution to figure out the mouse movement and decide on which axes it moves farthest. Determining the drag direction as above will probably never work out well.
The problem with any collection of information is noise. In your case, the noise is defined by the wrong movement of the user. Nonetheless, it should be possible to minimize the effect of noise by averaging the values.
There are advanced algorithms used in DSP but I guess a basic averaging of the info should do in your case.
What you could try is that instead of moving in Update at once like you do, collect movement over several frames, then average all those frames and see if it goes better:
IEnumerator GetAverageMovement(Action<Vector3> result)
{
int frames = 0;
List<Vector3>list = new List<Vector3>();
while(frames < 30f) // half a second appr
{
list.Add(GetDirection());
frames++;
yield return null;
}
result(AverageAllValues());
}
GetDirection is just returning the delta between current and previous position, AverageAllValues simply adds all values in list and divides by list.Count (aka 30).
This should fix cases when the user move all the way right but a bit up at the end. The last bit should be canceled by the large right movement.
If that is still not enough, then you could add some logic within the method that if a value is too far gone from the average, discard it. I don't think you need this in there.
I think you should create a Queue of positions with limited size right after dragging .
by comparing final value of Queue and first value you can find the direction.
If you want to get better results you can get Variance of positions and get better results.

Simple platformer collision algorithm is glitching

I'm having a glitch with this simple collision algorithm. I'm doing the rpg game tutorial from codingmadeeasy, and the collision is much alike any algorithm collision I've done in previous platformer style games. I've had this problem with previous games as well, just don't remember the fix. I have a map, it has lots of tiles, some are flagged as solid. their Update method checks for collision with the player.
If the tile is solid, it should stop the player and move him to a valid location.
Problem is when I go up or down, stick to the tile, and press a left or right movement key.
For example:
I'm pressing the Down key, I'm on top of the solid tile, and I press to go Left. at that moment, the player is relocated to to the Right of the tile.
This wouldn't happen if I go left or right and press up or down because of the if order in the collision method.
public void Update(GameTime gameTime, ref Player player)
{
if (State != TileState.Solid) return;
Rectangle tileRect =
new Rectangle((int)Position.X, (int)Position.Y,
SourceRect.Width, SourceRect.Height);
Rectangle playerRect =
new Rectangle((int)player.Sprite.position.X, (int)player.Sprite.position.Y,
player.Sprite.SourceRect.Width, player.Sprite.SourceRect.Height);
HandleCollisionWithTile(player, playerRect, tileRect);
}
/// <summary>
/// Repositions the player at the current position after collision with tile
/// </summary>
/// <param name="player">the player intersecting with the tile</param>
/// <param name="playerRect">the rectangle of the player</param>
/// <param name="tileRect">the rectangle of the tile</param>
private static void HandleCollisionWithTile(Player player, Rectangle playerRect, Rectangle tileRect)
{
// Make sure there's even collosion
if (!playerRect.Intersects(tileRect)) return;
// Left
if (player.Velocity.X < 0)
player.Sprite.position.X = tileRect.Right;
// Right
else if (player.Velocity.X > 0)
player.Sprite.position.X = tileRect.Left - player.Sprite.SourceRect.Width;
// Up
else if (player.Velocity.Y < 0)
player.Sprite.position.Y = tileRect.Bottom;
// Down
else if (player.Velocity.Y > 0)
player.Sprite.position.Y = tileRect.Top - player.Sprite.SourceRect.Height;
// Reset velocity
player.Velocity = Vector2.Zero;
}
Obviously, If I switch the two first ifs with the last, that would happen if I come from the side and press up or down.
What exactly am I missing?
As you've noted, this issue occurs because of the order in which you check the condition for collision. In particular, while visually your player sprite appears to be above a tile, your code is detecting the sprite as intersecting, and since the first check is for horizontal collisions, the player sprite's position is moved to the right of the tile's sprite (above and to the right, actually, so you only adjust the coordinate in the axis of the detected collision).
It seems to me that this exposes a fundamental flaw in the code: while one might argue that having resolved the collision that occurred in the vertical direction, by placing the player sprite at the top of the tile sprite, now the player sprite should not be in a collision state with the tile sprite, instead your code detects this condition as a collision.
Without a better code example, it's hard to know what the best fix might be. But an obvious quick-and-dirty solution would be to ensure that when you resolve a collision, you are not putting the player sprite in a position that intersects with the tile sprite. For example:
private static void HandleCollisionWithTile(Player player, Rectangle playerRect, Rectangle tileRect)
{
// Make sure there's even collosion
if (!playerRect.Intersects(tileRect)) return;
// Left
if (player.Velocity.X < 0)
player.Sprite.position.X = tileRect.Right + 1;
// Right
else if (player.Velocity.X > 0)
player.Sprite.position.X = tileRect.Left - player.Sprite.SourceRect.Width - 1;
// Up
else if (player.Velocity.Y < 0)
player.Sprite.position.Y = tileRect.Bottom + 1;
// Down
else if (player.Velocity.Y > 0)
player.Sprite.position.Y = tileRect.Top - player.Sprite.SourceRect.Height - 1;
// Reset velocity
player.Velocity = Vector2.Zero;
}
In other words, give yourself a 1 pixel margin around the tile into which the player sprite may not enter.
An alternative would be to "deflate" the tile rectangle (i.e. call Inflate() with negative values) by 1 pixel before checking for the intersection that defines a collision. Then when a collision does occur, the placement will be as before (i.e. without the +1 and -1 adjustments shown above), but that placement will not itself be detected as a collision.
There are many other ways to handle collision detection, but a) without more specifics about your scenario it's hard to know whether and which of those might be better choices, and b) given the apparently simple nature of your current implementation, a relatively simple fix as I've proposed above seems more likely to be warranted and useful.

XNA: check collision on list of bricks

I am having a big struggle implementing a collision detect on a list of bricks.
I have a game which randomly drops bricks that are supposed to stack up at the bottom of the screen. I managed to make them stop at the bottom of the screen but they don't stack.
I have this in my update function:
if (r.Next(0, 50) == 8)
{
_bricks.Add(new NormalBrick(this, new Vector2(r.Next(0, 700), 100)));
}
foreach(Brick b in _bricks)
{
b.move(GraphicsDevice.Viewport);
}
My move() function has the following code:
public void move(Viewport viewport)
{
if (_position.Y == (viewport.Height - _texture.Height ))
{
_position = new Vector2(_position.X, _position.Y);
}
else
{
_position = new Vector2(_position.X, _position.Y + _speed);
}
}
How can I make sure that the bricks don't all stop at the bottom of the screen, instead the brick has to check if there is a brick beneath it?
I have checked other questions on here but I couldn't find my answer and I have tried several things to get it fixed. Any help would be appreciated.
I would create Rectangles for each of your Bricks (unless you have already done so). Then in the Update() method, use something like brick.Rectangle.Intersects(anotherBrick) after movement. If true, then position the current brick above the intersecting bottom brick.
Make sure to move the brick's rectangle each time the brick moves.
I hope this helps. Let me know if you require any further assistance.

Weird sticky collision detection in 2D Windows Form game

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.

My player collision is not working correctly

The colision is working but its something wrong with theese lines since the player is just standing still at the start position x10 y10
for (int i = 0; i < sprites.Length; i++)
{
if (player.Top > sprites[i].Top && player.Bottom < sprites[i].Top) //Checking for intersection at the top of the player
{
player_Collision1 = true; //Found collision
}
else if (player.Bottom > sprites[i].Top && player.Bottom < sprites[i].Bottom) //Checking for intersection at the bottom of the player
{
player_Collision2 = true; //Found collision
}
else if (player.Left > sprites[i].Right && player.Left < sprites[i].Left) //Checking for intersection at the left of the player
{
player_Collision3 = true; //Found collision
}
else if (player.Right > sprites[i].Left && player.Right < sprites[i].Right)//Checking for intersection at the right of the player
{
player_Collision4 = true; //Found collision
}
}
Im using the XNA's rectangle and player is name of one rectangle and sprites is an array of all the rectangles that player can collide with, The XNA rectangle lets you get the cordinates of the sides of the rectangle as you se me do: player.Top player.Bottom etc etc.
The logic is really messed up. The first if will never be true as the player.top cant be below the sprite.top AND the player.bottom above the sprite.top. What I think you meant was if (player.Top > sprites[i].Top && player.Bottom < sprites[i].Bottom) Even still, it will evaluate to true when they don't collide. Look at this picture as to why. You also need to check the X axis to determine a proper collision.
The other ifs have the same issue as the first. Both in comparing bottom/top and top/bottom and returning true when there is no collision. That being said, I you are trying to do a poor-man "line of sight" scenario where you just want to see if the player is standing in a location that the sprite could "see", I am wrong.
I suggest taking a look at the platformer starter kit provided on the App Hub site as well as some documention on it. Specifically, look at the collision detection (in Player.cs CheckCollision()...or HandleCollision() I foget the exact name. It should be obvious.) You will be able to see how they determine which direction the player is colliding. They use it to see if a player can jump through a platform, but not fall through it.

Categories

Resources