Collision detection

I have recently started to learn SFML and decided to make a brick breaker game to practice with. I have managed to get a paddle, ball and bricks to appear. I can move the paddle just fine and the ball moves and collides with the paddle, and the edges of the wall as well as the the bricks, however when the ball collides with the bricks I am having difficulties with changing the bricks or even removing them.

the ball calculates the collisions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void cBall::Collision(cPaddle player, cBlocks &blocks)
{
	.....
	//Need to add collision with blocks (tricky!)
	std::vector<cBlock> squares = blocks.GetSquares();
	for (int i = 0; i < squares.size(); i++)
	{
		if (ball.getPosition().y >= squares.at(i).GetPosition().y && ball.getPosition().y <= squares.at(i).GetPosition().y + 25)
		{
			if (ball.getPosition().x >= squares.at(i).GetPosition().x && ball.getPosition().x <= squares.at(i).GetPosition().x + 100)
			{
			blocks.GetSquares().at(i).SetHit();
			yVelocity = yVelocity * -1.0;
			std::cout<<"Hit block "<<i<<"\n";

			}
		}

	}


	........
}


The blocks are each an object further contained in a blocks object.

1
2
3
4
5
6
7
8
9
class cBlocks
{
private:
	std::vector<cBlock> squares;
public:
	cBlocks();
	void Draw(sf::RenderWindow& window);
	std::vector<cBlock> GetSquares();
};


The actual block is a class as follows:

1
2
3
4
5
6
7
8
9
10
11
class cBlock
{
private:
	sf::RectangleShape rectangle;
	bool hit;
public:
	cBlock(float x, float y);
	sf::RectangleShape GetRect();
	sf::Vector2f GetPosition();
	void SetHit();
};


the set hit function which should be called changes the colour of the rectangle as follows:

1
2
3
4
5
void cBlock::SetHit()
{
	hit = true;
	rectangle.setFillColor(sf::Color::Red);
}


when I call blocks.GetSquares().at(i).SetHit(); in the collision detection nothing happens. I do not know why as the ball actually collides and the console outputs the correct block number being hit.
The problem may be the return type of the GetSquares function.
std::vector<cBlock> it is returning a copy.
Return a reference instead:
std::vector<cBlock>& GetSquares( return squares; );
Yep that's done the trick. Can't believe I did not notice that.
I have run into another problem I have added a new function that returns if a block has been hit already. I use this to determine if the block needs to be removed or not.

I have changed the code in the ball collision at line 12 to be:

1
2
3
4
if (!blocks.GetSquares().at(i).GetHit())
    blocks.GetSquares().at(i).SetHit();
else
    blocks.GetSquares().erase(blocks.GetSquares().begin()+i);


However I am getting a error message when the program invokes the else part of the statement of "vector subscript out of range". I do not see how it can be though.
Last edited on
When an element is erased the vector size is reduced.
Line 12 is: for (int i = 0; i < squares.size(); i++) where squares is a copy of blocks.GetSquares() that is made for some reason I can't see.

i will be out of range at squares.size()-1 if any erasures were made.
Try using for (int i = 0; i < blocks.GetSquares().size(); i++) instead so the upper limit for i gets adjusted when erasures occur.

EDIT: I see you are using the local variable std::vector<cBlock> squares to substitute for each cBlock in the collision testing. Consider using just a single cBlock variable for this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void cBall::Collision(cPaddle player, cBlocks &blocks)
{
	.....
	//Need to add collision with blocks (tricky!)
	cBlock square = blocks.GetSquares().at(i);// new
	for (int i = 0; i < blocks.GetSquares().size(); i++)// new
	{
		if (ball.getPosition().y >= square.GetPosition().y && ball.getPosition().y <= square.GetPosition().y + 25)
		{
			if (ball.getPosition().x >= square.GetPosition().x && ball.getPosition().x <= square.GetPosition().x + 100)
			{
			blocks.GetSquares().at(i).SetHit();
			yVelocity = yVelocity * -1.0;
			std::cout<<"Hit block "<<i<<"\n";

			}
		}

	}

	........
}

Or, just use blocks.GetSquares().at(i) directly.
EDIT2: The ninja phenomenon is amazing. Over 2 hours with no reply, then 2 replies in 4 minutes. The frequency of occurrence seems impossible (10 standard deviations from the norm = practically impossible)..
Last edited on
Note that erasing things while iterating over it like that might skip members since everything is shifted; at the end of your loop you increase i unconditionally so if you delete the 2nd to last element, then the last element will become i, but you will increment i when you finish the loop and never loop at it.
Sorry for late reply, lectures have started.

fun2code: I emplemented your suggestion but it still gives me an error about the vector subscript being out of range.

firedraco: I have taken your suggestion as well and moved the deletion to outside the loop. However this still causes error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
	//Need to add collision with blocks (tricky!)
	bool destroy = false;
	int k = 0;
	for (int i = 0; i < blocks.GetSquares().size(); i++)
	{
		cBlock square = blocks.GetSquares().at(i);
		if (ball.getPosition().y >= square.GetPosition().y && ball.getPosition().y <= square.GetPosition().y + 25)
		{
			if (ball.getPosition().x >= square.GetPosition().x && ball.getPosition().x <= square.GetPosition().x + 100)
			{
				if (!blocks.GetSquares().at(i).GetHit())
					blocks.GetSquares().at(i).SetHit();
				else
				{
					k = i;
					destroy  = true;
				}
				yVelocity = yVelocity * -1.0f;
			}
		}

	}

	if (destroy)
	{
		blocks.GetSquares().erase(blocks.GetSquares().begin()+k);
		destroy = false;
	}
Are you sure this error is occurring at line 26? The way you have it now only one erasure can occur. k should be in the range 0 to size-1 so the operation should be legal. I don't see anything wrong in the code provided.
It is definitely line 26 that is causing the issues. I have tried it both ways commented and uncommented. When commented no errors occur and no block is being deleted. When uncommented an error occurs when the block should be deleted.
What is the size of the vector and the value of k at line 26 just before the crash?
It varies but it is always within the vector size. The vector is of size 63 and without doing anything in the game k can be either 56 or 62.
Simply commenting out line 26 doesn't prove that it is where the crash occurs.
A crash could happen because of some following invalid use of blocks.
Example:
1
2
3
4
5
6
7
8
9
10
// code you have posted
if (destroy)
	{
		blocks.GetSquares().erase(blocks.GetSquares().begin()+k);// the line suspected of causing the crashes
		destroy = false;
	}

// just below the posted code
for (int i = 0; i < oldSize; i++)
    blocks.GetSquares().at(i);// boom at i = oldSize-1 (but only if the erasure was made) 

I admit, you are attempting operations that I've never used in practice. Seems like it should work though.
The code that follows checks that the ball is not hitting the boundaries. It has no calls to blocks as it just checks against screen width and height. The code described above is the only time blocks are handled in the entire program except for when they are first initialised.
I have solved the issue fun2code you are right in the blocks draw function I have a for loop that ranges from 0 < 63 rather than 0 < vector.size(). Thank god for breakpoints. I shall mark this as solved.
Topic archived. No new replies allowed.