My collision is daftly slow - please help.

Hello. My Collision code is *very* slow I guess. The more objects I add the slower the game gets.

Collision:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void CheckCollision(std::vector<GameObject*>& objects)
{
	for (auto it = objects.begin(); it != objects.end(); it++)
	{
		if ((*it)->object_type != GameObject::tile)
		{
			if ((*it)->lastPosition != (*it)->shape.getPosition())
			{
				int iteratorindex1 = std::distance(objects.begin(), it);
				for (auto it2 = objects.begin(); it2 != objects.end(); it2++)
				{
					int iteratorindex2 = std::distance(objects.begin(), it2);
					if ((*it)->shape.getGlobalBounds().intersects((*it2)->shape.getGlobalBounds()) && iteratorindex1 != iteratorindex2)
					{
						//checks left
						if ((*it)->lastPosition.x > (*it)->shape.getPosition().x)
							(*it)->shape.move((*it)->speed, 0);
						//checks right
						if ((*it)->lastPosition.x < (*it)->shape.getPosition().x)
							(*it)->shape.move(-(*it)->speed, 0);

						//checks bottom
						if ((*it)->lastPosition.y < (*it)->shape.getPosition().y)
						{
							if((*it)->shape.getPosition().x > (*it2)->shape.getPosition().x && 
								(*it)->shape.getPosition().x < (*it2)->shape.getPosition().x + (*it2)->shape.getGlobalBounds().width)
							{
								(*it)->shape.move(0, -(*it)->velocity.y);
								(*it)->velocity.y = 0;
								(*it)->canjump = true;
							}
							if ((*it)->shape.getPosition().x + (*it)->shape.getGlobalBounds().width >(*it2)->shape.getPosition().x &&
								(*it)->shape.getPosition().x + (*it)->shape.getGlobalBounds().width < (*it2)->shape.getPosition().x + (*it2)->shape.getGlobalBounds().width)
							{
								(*it)->shape.move(0, -(*it)->velocity.y);
								(*it)->velocity.y = 0;
								(*it)->canjump = true;
							}

						}
						//checks top
					}
				}
			}
		}
	}
}


I thought I'd be smart and check if an object 1): can move, and 2): moved since last frame. I thought it'd be a lot faster than actually checking each object each time if it collided with something.. What am I doing wrong?

Full code if you actually want to look.. http://pastebin.com/cZeK5fgF

end project .exe: http://www.filedropper.com/sfmlgame1

if you're willing to tell me a better way without reading go ahead - i don't blame you to sort out my code.
I tihnk you can change line 10 to
1
2
auto it2 = it;
for (it2++; it2 != objectts.end(); it2++) {

In other words, when checking object i, you only need to see if it collides with objects i+1 to N. That means you can remove && iteratorindex1 != iteratorindex2 at line 13, which means you don't need lines 9 or 13 at all.
What do you mean you only have to check i+1 to N? I don't understand.

I also don't understand that for loop. That will check if the current object isn't checking itself?
Last edited on
You are doing something like:

for (x = 1 to 3)
for (y = 1 to 3)
  if (collides( x, y ))

producing test collisions

1 1 (does object collide with itself?)
1 2
1 3
2 1 (already did this)
2 2 (self?)
2 3
3 1 (already did this)
3 2 (already did this)
3 3 (self?)

Change your loop:

for (x = 1 to N)
for (y = x+1 to N)
  test...


The general rule is to reduce the number of collisions you need to test. If you have a lot of stuff on the screen, keep track of which 'quadrant' of the screen your object is in. After that, you can keep the testing down to only those things near it.

Hope this helps.
Unless I'm blind that's not what it's doing? It only runs the second loop (and checks for collision) if the object from the first is moving AND is a tile. Your example is checking every object against every object for collision regardless of what type or if it's moving.
Last edited on
Generally for any collision detection algorithm you can reduce the required processing time by reducing the amount of objects it has to check.

There is a bunch of different techniques which one can use to do this which range from simply grouping objects so that they don't test for collisions that can't possibly happen (For example testing static objects which don't move against other static objects) all the way to complex techniques like using Quad-Trees to reduce the amount of collision checks for a certain entity.

Without knowing your what your current game/real-time system is and the details about what your collision detection needs it is hard to suggest the best path for you to take and to provide concrete examples on what you should do. But I will say look for simple ways of reducing the amount of objects you will need to run your actual collision detection algorithm on.

This could be something simple like knowing that your static trees that are part of your environment will never need to check for collision against other static trees.

Or maybe for every object you do a simple distance test first (By running a Bounding Sphere Check possibly) and only run your expensive but more precise collision detection algorithm on those objects which collided in the first test.

A lot of the time games do something similar to this. They first run less complex algorithms to "thin down" the number objects and then after that simple collision detection algorithm is run they will run a more complex and expensive algorithm to get more precision. For example Bounding Sphere/Bounding Box checks first then Pixel Perfect Checks after.

Then of course there is more complex systems to set up like Quad-Tree testing which divides you game screen in sections that can only contain a certain amount of objects (If the number of objects goes over that set amount it then splits up that section into 4 smaller sections). It will then only test for collisions for objects that are in the same section. There is plenty of information out there on how to implement Quad-Trees and that explain how they work better then I can.

But any way you can reduce the amount of times your collision detection algorithm needs to be run the faster it will get. Hopefully this helps give some idea.
Last edited on
Wow, that's a lot of useful information! I've heard about checking only what's on the screen (or in a given area), but then I'd have to "load" each level in different sections which sounds way too involving right now. (because I can't have other objects fall though the floor on parts of the level that isn't "loaded" yet.

Or I can do what you also said and use cheaper "collision" to see if any objects that can collide are close.

You said "For example testing static objects which don't move against other static objects" isn't that what I was doing? I make it so it only checks when the object isn't a "GameObject::tile" and it's moving.
Last edited on
The point we're trying to make is that you are using an n² iteration with expensive checks for every object you've got.

You need to thin it out -- use inexpensive checks and some state information to reduce the number of expensive checks you must do. Then, only when it is possible for something to collide do you need bother with the expensive tests.
I'm sorry for being ignorant on this topic, Duoas, and I promise i'm not trying to be condescending, but isn't that what I'm doing? Isn't checking if an object is a certain type cheap? And AFTER that, checking if the object that isn't moving cheap as well before any actual collision is being calculated?
Last edited on
LOL, I haven't spent a whole lot of time with your code. But the problem is you are still checking every object with every other object.

1
2
3
4
5
6
for every object x:
  if x is not a piece of the gameboard:
    if x has attempted to move:

      for every object y:
        did x and y collide?

Presumably you have only a relatively few xs compared to the number of objects, but you haven't really thinned the board by that much.

Can an x on the far left side of the screen collide with any of the ys on the right side of the screen? (Or, having run your game to see, can your blue box collide with anything not visible on the screen?)

Clearly no, but you are still testing every one for the possibility.

You should look into ways to limit the total number of collisions to those that are even remotely possible because they are in the general area of the moving object. Do this by dividing your map up into sections and keeping track of what section every object is in. (Obviously, only moving objects can change sections.) After that, only check to see if your moving object is colliding with objects in the same section (or sections) that it is in.

Hope this helps.
All of that definitely does help! But I have a follow up question. Why does this have such an impact on my program already? Even 5 objects vs 10 objects it's a lot slower. How can computers can handle 3d collision but this makes it go so slow? Is the reason why it does that because it goes as fast as it can and basically kills itself performance wise? Or am I missing something? Even if I make it so it only checks around itself.. It'll still have to check more than a couple objects, right? But a couple objects are already making it terribly slow.

Which brings me to my next question.. Why does it make the pace of the game slower, not the actual frame rates? I know it doesn't have to do with drawling anything on the screen but I've never seen a game go "slower" in the sense that the speed of everything is slowed down.
Last edited on
To be fair, I was wondering that when I played (briefly) with your game. (It was so slow I didn't try to move around much further than climbing halfway up that one wall a little to the right.)

While it does impact performance significantly, I'm not convinced this is the only issue.

Maybe if I get a chance I'll download your code and look at it.
1
2
3
4
5
6
	sf::Vector2f gravity(0, .0001);

	Player()
	{
		object_type = player;
		speed = .1;
perhaps those values are too small.


By the way, GameObject destructor must be virtual as you do have another virtual member function.


Also, it may pay to separate the movable objects.
Wait a minute. Looking at your code, it appears that there is only one object that can move - the player. So why are you doing an N2 of all objects? Just see if the player has collided with any of the tiles.

Even with this change, what makes you think that the collision code is the problem? It looks like you are redrawing every object after every move, even if they move less than a pixel. Don't you only need to redraw the player?

GameObject needs a constructor that initializes loopcount, canjump and speed. Right now those members are uninitialized for Tiles.

Why do you have speed and velocity members? It looks to me like you should remove speed and use velocity.x in its place.

I may misunderstand the heart of the collision code but it seems odd to me. Here it is with my comments/questions. Note that I've created some references to make it less verbose:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// You should check iteratorindex1 != iteratorindex2 first since that's
// much cheaper
if ((*it)->shape.getGlobalBounds().
    intersects((*it2)->shape.getGlobalBounds())
    && iteratorindex1 != iteratorindex2) {
    // references shorthand
    Sf::Vector2f &lastPos((*it)->lastPosition);
    Sf::Vector2f &itPos((*it)->shape.getPosition());
    Sf::Vector2f &it2Pos((*it2)->shape.getPosition());
    // checks left
    if (lastPos.x > itPos.x)  // if it moved left
        (*it)->shape.move((*it)->speed, 0);  // move it right
    // checks right
    // and if it moved right, move it left.
    if (lastPos.x < itPos.x)
        (*it)->shape.move(-(*it)->speed, 0);
    // If you use velocity.x to represent the x velocity then
    // the above if statements should collapse down to
    (*it)->shape.move(-(*it)->velocity.x, 0);

    // What about *it2?  What if it's moving?  If you want this to be general
    // collision code then you should update both objects and take my advice
    // and Duoas's and check each pair only once.

    // checks bottom
    if (lastPos.y < itPos.y) { // if it's moving down(?)
        // If it's left edge is within it2's bounds
        if (itPos.x > it2Pos.x &&
            itPos.x < it2Pos.x + (*it2)->shape.getGlobalBounds().width) {
            (*it)->shape.move(0, -(*it)->velocity.y); // undo the last move
            (*it)->velocity.y = 0;   // Stop moving in y. So you land on the tile?
            (*it)->canjump = true;  // And it can jump? 
        }

        // Now check the right edge. Note that if both right and left edges are
        // within the it2 then you'll move twice.
        if (itPos.x + (*it)->shape.getGlobalBounds().width > it2Pos.x &&
            itPos.x + (*it)->shape.getGlobalBounds().width < it2Pos.x + (*it2)->shape.getGlobal\
Bounds().width) {
            (*it)->shape.move(0, -(*it)->velocity.y);
            (*it)->velocity.y = 0;
            (*it)->canjump = true;
        }

         // I'm not sure you need all this. You already know that the objects
         // intersect, so it's probably sufficient to know that the it's moving down:
         if ((*it)->velocity.y < 0) {
            (*it)->shape.move(0, -(*it)->velocity.y);
            (*it)->velocity.y = 0;
            (*it)->canjump = true;
        }

    }
    // checks top
 }
Topic archived. No new replies allowed.