Best Collision Detection in Monogame - c#

I tried creating an Arknoid game using XNA some years ago. Unfortunately, with the busy life I didn't manage to finish it. I found my old code and tried importing it to Monogame. The problem I'm having is the Collision detection. As you can see the ball is supposed to be deflecting outside the bar but for some reason my code is not working
I have a class called Ball.cs and Below is the code I use to check for Collision. I then pass the rectangle of the bar.
public bool CheckCollision(Rectangle rect)
{
Rectangle rOut;
rOut = Rectangle.Intersect(this.rect, rect);
if (rOut.Width > 0 && rOut.Height > 0)
{
ChangeDirection(false);
//isGoingLeft = !isGoingLeft;
//isGoingUp = !isGoingUp;
return true;
}
return false;
}
UPDATE: Here's the code where it performs the Collision Detection. ballPlayer is the Ball, barPlayer is the blue bar. Brick are bricks. So when it moves
if (ballPlayer.isBallDead == false)
{
ballPlayer.CheckCollision(barPlayer.Rect);
ballPlayer.Draw(spriteBatch);
ballPlayer.hasTouched = false;
for (int x = 0; x < _bricks.Count; x++)
{
if (_bricks[x].Visible == true)
{
if (_bricks[x].CheckCollision(ballPlayer))
{
_bricks[x].ChangeFace(_textures[_bricks[x].BrickLife]);
}
}
_bricks[x].Draw(spriteBatch);
}
}

Related

Unity - Game of connecting water pipes. Problem with checking if the pipes objects are connected to the central source

I`m trying to make a puzzle game where the player is presented with a grid of pipes with one water source. The player has to rotate the pipes in order to connect them all to each other and, of course, to the water source.
The problem I`m stuck with is this:
Step 1:
Step 2:
Step 3:
As shown in the pictures, when I rotate a pipe that is connected to a group of other pipes, the connection to the water isn`t broken in the remaining group, it only works in a single adjacent pipe.
This is happening because of the way I`m doing the connections using a group of colliders in each exit of the pipes, as shown in the pictures below.
Colliders - Scene:
Colliders - Hierarchy:
If the collided object is connected to water (by a bool), it adds the collided object to a list. If the list is greater than 0, activates the blue sprite. If the list is empty, deactivates the blue sprite. As shown in the code sample below.
Triggers that check if the object is connected to water:
private void OnTriggerEnter2D(Collider2D other) {
if (other.GetComponent<FlipPipes2>() != null) {
isFlowingWater = other.GetComponent<FlipPipes2>().flowingWater;
}
if (other.CompareTag("WaterPipe")) {
if (other.name == "Filled_Cross" || isFlowingWater) {
myFlipPipes.flowingWater = true;
}
TestingScoreStatic.connectedPipes++;
}
}
private void OnTriggerStay2D(Collider2D other) {
if (!inList) {
myFlipPipes.collidedObjectsList.Add(other.gameObject);
inList = true;
}
if (other.name == "Filled_Cross" || isFlowingWater) {
myFlipPipes.flowingWater = true;
}
}
private void OnTriggerExit2D(Collider2D other) {
if (other.CompareTag("WaterPipe")) {
myFlipPipes.collidedObjectsList.Remove(other.gameObject);
myFlipPipes.flowingWater = false;
isConnectedToWater = false;
TestingScoreStatic.connectedPipes--;
inList = false;
}
}
Code sample of the actual change of sprite:
private void Update() {
if (collidedObjectsList.Count > 0) {
filledPipe.SetActive(true);
} else {
filledPipe.SetActive(false);
}
}
Even if the tile connected to the source of water loses its connection, the others don't, because they are still connected to water tiles.
How can I check if there is a valid path to the source of water? Or any other way to do what I need?
Here are some other code samples that may help:
The method that creates the board:
private void SetUp() {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Vector2 tempPosition = new Vector2(i, j);
int tempIndex = levelManager.levelOne[pipeIndex];
GameObject pipe = Instantiate(pipes[tempIndex], tempPosition, Quaternion.identity);
pipe.transform.parent = transform;
allPipes[i, j] = pipe;
if (!pipe.CompareTag("WaterPipe")) {
shuffleSpawnedPipes.pipes.Add(pipe);
}
pipeIndex++;
}
}
shuffleSpawnedPipes.ShufflePipes();
}
(Terrible) Attempt to check every pipe with a double for loop:
void IsConnectedToWater() {
if (column > 0 && column < board.width - 1) {
GameObject leftPipe1 = board.allPipes[column - 1, row];
GameObject rightPipe1 = board.allPipes[column + 1, row];
if (leftPipe1.tag == this.gameObject.tag && rightPipe1.tag == this.gameObject.tag) {
leftPipe1.GetComponent<Pipe>().connectedToWater = true;
rightPipe1.GetComponent<Pipe>().connectedToWater = true;
connectedToWater = true;
}
}
}
Of course this does not work because it is not checking if the current object is colliding. Even with the collision check, the resuls is the same. I'm in a loop of doing many different attempts resulting in the same thing.
What you need is a pathfinding algorithm.
Create a recursive function that will check paths to your watersource. Instead of checking each element if it is connected to water check if your watersource object is in a list of connected objects of that checked pipe. If its not save that pipe object into list of checkedObjects and go to next connected object, repeat till you checked all pipe objects connected to each other. If none of the objects had your watersource in the list of connected objects it means all the objects in checkedObjects path are not connected to water and you can disable the water sprite on all of them.

Box collider doesn't catch mouse button press event

I have a cupboard with 2 colliders - one for cupboard and one for it's box. When I press on the box, I want to open/close it. It worked fine, but now by some reason it only work when I press on the edge on the box. When click on the center, it don't work.
Video: https://youtu.be/OozsAi7KNzs
Here is the code, which play animation (open/close cupboard), when I press on the box:
public Animation[] animations;
public string[] animationName;
public bool playOneDirection; // should revert animation speed after second playing?
public AudioSource myAudioOpen;
public AudioSource myAudioClose;
private bool isDoorClosed;
private bool isAimationReadyToPlay = true;
private Collider thisCollider;
public void Start()
{
thisCollider = GetComponent<Collider>();
}
void Update ()
{
if (Input.GetButton("Fire1"))
if(DoPlayerLookAtButton() && isAimationReadyToPlay)
OpenCloseDoor();
}
bool DoPlayerLookAtButton()
{
RaycastHit _hit;
Ray _ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
bool isHit = Physics.Raycast(_ray, out _hit, 1.5f);
if (isHit && _hit.collider == thisCollider) return true;
else return false;
}
public void OpenCloseDoor()
{
if (!isDoorClosed) // Play animation with normal speed
{
myAudioOpen.Play();
for (int i = 0; i < animations.Length; i++)
{
animations[i][animationName[i]].speed = 1.0f;
animations[i].Play();
}
}
if(playOneDirection)
return;
if (isDoorClosed) // Play animation with revert speed
{
myAudioClose.Play();
for (int i = 0; i < animations.Length; i++)
{
animations[i][animationName[i]].speed = -1.0f;
animations[i][animationName[i]].time = animations[i][animationName[i]].length;
animations[i].Play();
}
}
StartCoroutine("DelayBetweenAnimations");
isDoorClosed = !isDoorClosed;
}
IEnumerator DelayBetweenAnimations()
{
isAimationReadyToPlay = false;
yield return new WaitForSeconds(0.5f);
isAimationReadyToPlay = true;
}
Your cupboard has 2 colliders, but you are only checking for one of them. If there is some kind of overlap then it could be fiddly to click the correct one. If you just want to be able to click anywhere on the game object change your code like so...
//From
//if (isHit && _hit.collider == thisCollider) return true;
//To
if (isHit && _hit.transform.gameObject == this.gameObject) return true;
Add a layer mask for your player and ensure your Physics.Raycast excludes that layer, to avoid the cast from hitting yourself. See here
I've made the main camera starting from the center of the player, so raycast hits player's collider. I made it trying to fix the bug when camera can go throuth the wall like on the screen below.
Raycast can be seen passing through the player and did not reach the box

Remembering a direction, moving to a point, then changing direction. XNA

I'm making a pacman clone in XNA.
So far I've drawn the tile map using 2D array, added the pills using another 2D array and made a 2D array that allows movement of pacman.
In the actual game you can press right whilst moving up, and it will wait until you're able to move right and the turn.
I have a system in place that allows a turn only when the spritePosition % 32 = 16.
This means the sprite will be centred between the walls.
I need the program to remember the last key pressed or move to the right position before turning, but i cant find a way of doing it.
Here is a bit of the code that covers what I'm trying.
public void MovementCheck()
{
presentKey = Keyboard.GetState();
spritePosition = spriteVelocity + spritePosition;
pacManRec = new Rectangle((int)spritePosition.X, (int)spritePosition.Y, pacManTex.Width, pacManTex.Height);
spriteOrigin = new Vector2(pacManRec.Width / 2, pacManRec.Height / 2);
//Press Right
if (presentKey.IsKeyDown(Keys.Right) && pastKey.IsKeyUp(Keys.Right))
{
Right();
}
}
private void Right()
{
direction = "right";
//if the next block in tile map to the right is a 1, and the sprite is centred - allow a turn
if (inputMap[(int)Math.Floor(spritePosition.Y / 32), (int)Math.Floor(spritePosition.X / 32) + 1] == 1 && (spritePosition.Y % 32 == 16))
{
rotation = ((float)Math.PI / 180);
spriteVelocity.X = 0;
spriteVelocity.Y = 0;
spriteVelocity.X = movementSpeed;
}
}
Only the right key is shown, the others are similar but the directions all change and the checks to the tile map are changed accordingly. (+1 on the X here)
ive tried things like
while (spritePosition.Y % 32 != 16)
{ spritePosition = spriteVelocity + spritePosition; }
but that just makes the sprite shoot up the screen, (kinda obviously) :(
and I tried a new Method before the Right() call
bool RightCheck()
{
if ( CONDITIONS MET HERE )
return true
else
{
//dont remember if I used this line, but something similar
spritePosition = spriteVelocity + spritePosition;
RightCheck()
}
return false; //allows program to build
}
Just an causes infinite recursion.
One solution is adding a int counter = 0; which you update in your gameloop (with counter++;) every frame/time-step. Set to 0 everytime you make a valid input and save that input.
Outlined code:
public class GameClassWhereUpdateIsDone
{
private enum Directions { None, Up, Down, Left, Right };
private int counter = 0;
private Directions currentDirection; // Current movement-direction
private Directions lastInput; // Last direction from input
public void Update(...)
{
var keyboardState = Keyboard.GetState();
if(keyboardState.IsKeyPressed(Keys.Right))
{
counter = 0;
direction = Directions.Right;
}
if(currentDirection != lastInput && counter < 5) // Allow turning 5 updates ahead.
{
// Player want to turn
if(AllowedToTurn(lastInput)
{
currentDirection = lastInput;
}
}
MoveDirection(currentDirection);
counter++;
}
private bool AllowedToTurn(Directions direction)
{
if(direction == Directions.Right)
{
return RightCheck();
}
}
}
The key idea is to keep track of movement direction and last direciton that was input...
In the original Pac-Man "pre-turning" was actually used, meaning you would start moving diagonally if you turned ahead of a corner according to: http://home.comcast.net/~jpittman2/pacman/pacmandossier.html which is an interesting read.

having trouble implementing physics to ball collision program

I'm currently working on a simple ball collision program in C#, where an indeterminate amount of balls bounce around the screen, each being added to the program on a button click. So far I've gotten everything working, and I have a basic collision system. Now, I'm trying to add more realistic physics to the balls.
The thing is, I have little understanding of physics. I've been reading a bit about vector physics and I get the gist of it, but I'm not quite making the connection between what I've read and how I'm supposed to implement these concepts programmatically. Could anybody provide me some resources on how one is actually supposed to implement vector physics to something like a simple ball collision program like I've written?
Here's what I have right now. It's basically just two booleans determining whether my ball goes in one of four directions. When two balls collide, they just pass their directions to each other.
public void newball(PaintEventArgs e, int panelWidth, int panelHeight)
{
//Detects walls of form. if it detects a wall or another ball, it bounces
if (X >= panelWidth)
{
bounceX = true;
}
else if (X <= 25)
{
bounceX = false;
}
if (Y >= panelHeight )
{
bounceY = true;
}
else if (Y <= 25)
{
bounceY = false;
}
//balls only go in four directions right now have to implement vector physics.*/
if (bounceX == false)
{
X = X+2;
}
else if (bounceX == true)
{
X = X -2;
}
if (bounceY == false)
{
Y = Y+2;
}
else if (bounceY == true)
{
Y = Y-2;
}
//this draws the ball on the panel.
Pen clsPen = Pens.Black;
Color color = Color.Black;
SolidBrush brush = new SolidBrush(color);
e.Graphics.DrawEllipse(clsPen, X - 25, Y -25, 25, 25);
e.Graphics.FillEllipse(brush, X-25, Y-25, 25, 25);
}
And here are some of the things I've read:
2D Collisions, Vectors

Collision Detection, player correction

I am having some problems with collision detection, 2 have 2 types of objects aside from the player it self. Tiles and what I Call MapObjects.
The tiles are all 16x16, where the MapObjects can be any since, but is my problem case are they as well 16x16.
When my player runs apon the mapobjects or tiles dose it get very jaggy. The player is unable to move right, and will get warped forward when moving left.
I have found the problem, and that is my collision detection will move the player left/right if colliding the object from the side, and up/down if collision from up/down.
Now imagen that my player is sating on 2 tiles, at 10,12 and 11,12 and the player is mostly standing on the 11,12 tile. The collision detection will first run on then 10,12 tile, it calculates the collision depth, and finds that is is a collision from the side, and therefore more the object to the right. After will it do collision detection with 11,12 here will it move the character up. So the player will not fall down, but are unable to move right. And when moving left will the same problem make the player warp forward.
This problem have been bugging me for a few days now, and I just can't find a solution!
Here is my code that dose the collision detection.
public void ApplyObjectCollision(IPhysicsObject obj, List<IComponent> mapObjects, TileMap map)
{
PhysicsVaraibales physicsVars = GetPhysicsVariables();
Rectangle bounds = ((IComponent)obj).GetBound();
int leftTile = (int)Math.Floor((float)bounds.Left / map.GetTileSize());
int rightTile = (int)Math.Ceiling(((float)bounds.Right / map.GetTileSize())) - 1;
int topTile = (int)Math.Floor((float)bounds.Top / map.GetTileSize());
int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / map.GetTileSize())) - 1;
// Reset flag to search for ground collision.
obj.IsOnGround = false;
// For each potentially colliding tile,
for (int y = topTile; y <= bottomTile; ++y)
{
for (int x = leftTile; x <= rightTile; ++x)
{
IComponent tile = map.Get(x, y);
if (tile != null)
{
bounds = HandelCollision(obj, tile, bounds, physicsVars);
}
}
}
// Handel collision for all Moving objects
foreach (IComponent mo in mapObjects)
{
if (mo == obj)
continue;
if (mo.GetBound().Intersects(((IComponent)obj).GetBound()))
{
bounds = HandelCollision(obj, mo, bounds, physicsVars);
}
}
}
private Rectangle HandelCollision(IPhysicsObject obj, IComponent objb, Rectangle bounds, PhysicsVaraibales physicsVars)
{
// If this tile is collidable,
SpriteCollision collision = ((IComponent)objb).GetCollisionType();
if (collision != SpriteCollision.Passable)
{
// Determine collision depth (with direction) and magnitude.
Rectangle tileBounds = ((IComponent)objb).GetBound();
Vector2 depth = bounds.GetIntersectionDepth(tileBounds);
if (depth != Vector2.Zero)
{
float absDepthX = Math.Abs(depth.X);
float absDepthY = Math.Abs(depth.Y);
// Resolve the collision along the shallow axis.
if (absDepthY <= absDepthX || collision == SpriteCollision.Platform)
{
// If we crossed the top of a tile, we are on the ground.
if (obj.PreviousBound.Bottom <= tileBounds.Top)
obj.IsOnGround = true;
// Ignore platforms, unless we are on the ground.
if (collision == SpriteCollision.Impassable || obj.IsOnGround)
{
// Resolve the collision along the Y axis.
((IComponent)obj).Position = new Vector2(((IComponent)obj).Position.X, ((IComponent)obj).Position.Y + depth.Y);
// If we hit something about us, remove all velosity upwards
if (depth.Y > 0 && obj.IsJumping)
{
obj.Velocity = new Vector2(obj.Velocity.X, 0);
obj.JumpTime = physicsVars.MaxJumpTime;
}
// Perform further collisions with the new bounds.
return ((IComponent)obj).GetBound();
}
}
else if (collision == SpriteCollision.Impassable) // Ignore platforms.
{
// Resolve the collision along the X axis.
((IComponent)obj).Position = new Vector2(((IComponent)obj).Position.X + depth.X, ((IComponent)obj).Position.Y);
// Perform further collisions with the new bounds.
return ((IComponent)obj).GetBound();
}
}
}
return bounds;
}

Categories