Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Optimizing collision system  (Read 4112 times)

0 Members and 1 Guest are viewing this topic.

SEnergy

  • Newbie
  • *
  • Posts: 20
    • View Profile
Optimizing collision system
« on: June 11, 2012, 06:36:54 pm »
Hello,

I've made my own collision system (only for squares and rectangles) but I have 2 little bugs that I don't know how to fix

1. sometimes it will pass through texture, but max of 1 or 2 pixels // I've tried to fix this by adding 1 pixel for "bonus collision range" but sometimes it will collide even when they aren't touching...
2. when it collide with something you can move only to 2 sides, for example:

when I collide with right side with player (left side of second object) I can;t move down, only up and left (on the right side is second object)

here's screenshot of first problem (object is locared at 100,100), I've collided at x = 102 which should be 100

http://filebeam.com/b7d18239c8bb509c5747b20c0e3ec46e.jpg

here's my code:

Collision function:
bool Animation::IsAt(Animation Collider, char direction)
{
        int AnimW, AnimH, ColliderW, ColliderH;
        float AnimX, AnimY, ColliderX, ColliderY;
       
        AnimX = Animation::GetX();
        AnimY = Animation::GetY();
        ColliderX = Collider.GetX();
        ColliderY = Collider.GetY();

        AnimW = Animation::GetW();
        AnimH = Animation::GetH();
        ColliderW = Collider.GetW();
        ColliderH = Collider.GetH();

        if(direction == 'r')
        {
                if(
                        (AnimX+AnimW >= ColliderX && AnimX+AnimW <= ColliderX+ColliderW && AnimY >= ColliderY && AnimY <= ColliderY+ColliderH) ||
                        (AnimX+AnimW >= ColliderX && AnimX+AnimW <= ColliderX+ColliderW && AnimY+AnimH >= ColliderY && AnimY+AnimH <= ColliderY+ColliderH) ||

                        (ColliderX >= AnimX && ColliderX <= AnimX+AnimW && ColliderY >= AnimY && ColliderY <= AnimY+AnimH) ||
                        (ColliderX >= AnimX && ColliderX <= AnimX+AnimW && ColliderY+ColliderH >= AnimY && ColliderY+ColliderH <= AnimY+AnimH)
                        )
                {
                        return true;
                }
        }

        else if(direction == 'l')
        {
                if(
                        (AnimX >= ColliderX && AnimX <= ColliderX+ColliderW && AnimY >= ColliderY && AnimY <= ColliderY+ColliderH) ||
                        (AnimX >= ColliderX && AnimX <= ColliderX+ColliderW && AnimY+AnimH >= ColliderY && AnimY+AnimH <= ColliderY+ColliderH) ||

                        (ColliderX+ColliderW >= AnimX && ColliderX+ColliderW <= AnimX+AnimW && ColliderY >= AnimY && ColliderY <= AnimY+AnimH) ||
                        (ColliderX+ColliderW >= AnimX && ColliderX+ColliderW <= AnimX+AnimW && ColliderY+ColliderH >= AnimY && ColliderY+ColliderH <= AnimY+AnimH)
                        )
                {
                        return true;
                }
        }

        else if(direction == 'u')
        {
                if(
                        (AnimX >= ColliderX && AnimX <= ColliderX+ColliderW && AnimY >= ColliderY && AnimY <= ColliderY+ColliderH) ||
                        (AnimX+AnimW >= ColliderX && AnimX+AnimW <= ColliderX+ColliderW && AnimY >= ColliderY && AnimY <= ColliderY+ColliderH) ||

                        (ColliderX >= AnimX && ColliderX <= AnimX+AnimW && ColliderY+ColliderH >= AnimY && ColliderY+ColliderH <= AnimY+AnimH) ||
                        (ColliderX+ColliderW >= AnimX && ColliderX+ColliderW <= AnimX+AnimW && ColliderY+ColliderH >= AnimY && ColliderY+ColliderH <= AnimY+AnimH)
                        )
                {
                        return true;
                }
        }

        else if(direction == 'd')
        {
                if(
                        (AnimX >= ColliderX && AnimX <= ColliderX+ColliderW && AnimY+AnimH >= ColliderY && AnimY+AnimH <= ColliderY+ColliderH) ||
                        (AnimX+AnimW >= ColliderX && AnimX+AnimW <= ColliderX+ColliderW && AnimY+AnimH >= ColliderY && AnimY+AnimH <= ColliderY+ColliderH) ||

                        (ColliderX >= AnimX && ColliderX <= AnimX+AnimW && ColliderY >= AnimY && ColliderY <= AnimY+AnimH) ||
                        (ColliderX+ColliderW >= AnimX && ColliderX+ColliderW <= AnimX+AnimW && ColliderY >= AnimY && ColliderY <= AnimY+AnimH)
                        )
                {
                        return true;
                }
        }
        return false;
}

how am I detecting collision in game:

if(right)
                        {
                                if(!aPlayer[0].IsAt(aBuilding[0], 'r'))
                                        aPlayer[0].Move(Vector2f(Speed*ElapsedTime, 0), 'r');
                                lastdir = 0;
                        }
                        if(left)
                        {
                                if(!aPlayer[0].IsAt(aBuilding[0], 'l'))
                                        aPlayer[0].Move(Vector2f(-Speed*ElapsedTime, 0), 'l');
                                lastdir = 3;
                        }
                        if(up)
                        {
                                if(!aPlayer[0].IsAt(aBuilding[0], 'u'))
                                        aPlayer[0].Move(Vector2f(0, -Speed*ElapsedTime), 'u');
                                lastdir = 6;
                        }
                        if(down)
                        {
                                if(!aPlayer[0].IsAt(aBuilding[0], 'd'))
                                        aPlayer[0].Move(Vector2f(0, Speed*ElapsedTime), 'd');
                                lastdir = 9;
                        }

I've been working on this second day, total of like 10 hours and I have no more ideas on how to fix that.. my brain is completelly exhausted ...

thePyro_13

  • Full Member
  • ***
  • Posts: 156
    • View Profile
Re: Optimizing collision system
« Reply #1 on: June 12, 2012, 02:12:26 pm »
You're doing this the hard way IMO. Keep an sf::rect with each of your objects and collide as you move.

In each move function, move the rect first then use rect.intersect() to find if it collides with another objects rect.
If it does, then move the rect back back and abort the move(or launch your collision handler; for deciding how to deal damage or whatever)
Otherwise complete the rest of the move.

This way you avoid doing all the rectangle math yourself and you don't have to do it per direction either.

SEnergy

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Optimizing collision system
« Reply #2 on: June 12, 2012, 08:37:11 pm »
using
IntRect rAnim(Animation::GetX(), Animation::GetY(), Animation::GetW(), Animation::GetH());
        IntRect rCollider(Collider.GetX()-2, Collider.GetY()-2, Collider.GetW()+2, Collider.GetH()+2);

        IntRect result;
        bool b1 = rAnim.intersects(rCollider, result);
        if(b1)
                return true;

now, however I'm still getting those 2 errors (it looks like I created same function as rect... however by adding +2 and -2 to collider it got better, I now collide at right position, or -1 pixel away, which is not that bad, however I can't move when I collide, I don't know how should I fix it... any solutions?

thePyro_13

  • Full Member
  • ***
  • Posts: 156
    • View Profile
Re: Optimizing collision system
« Reply #3 on: June 13, 2012, 08:42:55 am »
The trick is to test collisions before actually moving.

Like this(psudocode):
        2dvector movevalue = //whichever direction you're trying to move in. pass this in from your control function
        IntRect rAnim(Animation::GetX(), Animation::GetY(), Animation::GetW(), Animation::GetH());
        IntRect rCollider(Collider.GetX()-2, Collider.GetY()-2, Collider.GetW()+2, Collider.GetH()+2);

        rAnim.left += movevalue.x;
        rAnim.top += movevalue.y;

        //now rAnim represents where we will be after we move.
       
        IntRect result;
        bool b1 = rAnim.intersects(rCollider, result);
        if(b1) // if we would collide(ie. be inside another object, then we just skip the actual move
                return true; //this way, when we try again next frame, we know that we left our object in a non-collided state

       return false; // no collision; perform the actual move

So your movement step should look something like this:
      if(move) //user pushed one of the movement buttons
      {
             collision = false;
              for every object o
              {
                         if (test_collision(movement_vector, object o))
                                  collision = true;
               }
             
               if(!collision)
                   animation.move(movement_vector);

        }

The trick is to test the collision before you move, otherwise you leave the object in a position that still counts as a collide.

The other way to do it is to try and move the object back after you discover the collision.
But I find it much easier to test and resolve the collisions as part of the movement step.

I hope I explained that properly. :S
« Last Edit: June 13, 2012, 08:51:43 am by thePyro_13 »

SEnergy

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Optimizing collision system
« Reply #4 on: June 14, 2012, 10:31:58 am »
well I'm actually right now doing check BEFORE I move

for example:

                        if(left)
                        {
                                if(!aPlayer[0].IsAt(aBuilding[0], 'l'))
                                        aPlayer[0].Move(Vector2f(-Speed*ElapsedTime, 0), 'l');
                                lastdir = 3;
                        }

so it will check if aPlayer[0] collides with aBuilding[0], if yes then nothing happens, otherwise it will move to that direction... but when it collides, it will be "inside" of second object, therefore even if I'll move up like 1 pixel manually (like in your code), it will be in second object anyway... well something like that:

well here's picture about it in case I explained it wrong

http://filebeam.com/29662fce25416fcf3c7b83b56bce8c6c.jpg

I'm currently at school so I can't try anything right now...

thePyro_13

  • Full Member
  • ***
  • Posts: 156
    • View Profile
Re: Optimizing collision system
« Reply #5 on: June 14, 2012, 01:31:57 pm »
But are you checking the space you are in, or the space you are going to be in after you move?

If you only check the space you are currently in, then you won't catch the collision until the next game loop. and then you won't be able to move because you are colliding.

aBallofWin

  • Jr. Member
  • **
  • Posts: 99
    • View Profile
Re: Optimizing collision system
« Reply #6 on: June 14, 2012, 01:36:57 pm »
If you still haven't solved the problem this may be of help.

I was having problems with this a few days ago and came up with two solutions that on their own didn't really work to well, but together did.

One of them is to detect what way you're moving when you collide with an object and when that happens, (for example I'm moving right) knock out the move right function until you stop colliding with the collision boundary.

The second method I put to use was to push the player back by their speed movement plus 1. This would be achieved by:
Code: [Select]
goback = HeroUnder.GetPosition().y; Hero.SetY(goback - (speed + 1));
Together they pushed the player back a bit, plus if they do start touching collision somehow, then that movement is disabled. It actually pushes them back once, the character will come a few spaces forwards then stop, which actually looks like they've just come to a stop.

I'm not saying that this is the way to go and I know it's not the best method to deal with collision, but it may give you an idea of how to do it :)


EDIT: try thePyro_13's method first. It sounds a lot more efficient

SEnergy

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Optimizing collision system
« Reply #7 on: June 14, 2012, 02:32:22 pm »
nice, it's working, however sometimes there's space between object and player(this bug occurs like 3 of 4 times when I come to object) (1-2 pixels), code:

bool Animation::IsAt(Animation Collider, char direction, float speed)
{
        Vector2f aaa(0,0);
        if(direction == 'r')
                aaa.x = speed;
        if(direction == 'l')
                aaa.x = speed;
        if(direction == 'u')
                aaa.y = speed;
        if(direction == 'd')
                aaa.y = speed;
        FloatRect rAnim(Animation::GetX(), Animation::GetY(), Animation::GetW(), Animation::GetH());
        FloatRect rCollider(Collider.GetX(), Collider.GetY(), Collider.GetW(), Collider.GetH());

        rAnim.left += aaa.x;
        rAnim.top += aaa.y;

        FloatRect result;
        bool b1 = rAnim.intersects(rCollider, result);
        if(b1)
                return true;
        return false;
}

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Re: Optimizing collision system
« Reply #8 on: June 15, 2012, 07:46:00 pm »
That's due to adding (or removing) a value bigger than the gap. E.g. if there's 1 pixel space but you add 2 pixels (due to the speed), you'll collide too early. If you collide, you might want to either check with smaller steps or calculate the distance and modify your speed accordingly.