1
\$\begingroup\$

I've been working on a 2D (tile based) 2D platformer for iOS and I've got basic entity collision detection working, but there's just something not right about it and I can't quite figure out how to solve it.

There are 2 forms of collision between player entities as I can tell, either the two players (human controlled) are hitting each other side-to-side (i. e. pushing against one another), or one player has jumped on the head of the other player (naturally, if I wanted to expand this to player vs enemy, the effects would be different, but the types of collisions would be identical, just the reaction should be a little different).

In my code I believe I've got the side-to-side code working: If two entities press against one another, then they are both moved back on either side of the intersection rectangle so that they are just pushing on each other.

I also have the "landed on the other player's head" part working.

The real problem is, if the two players are currently pushing up against each other, and one player jumps, then at one point as they're jumping, the height-difference threshold that counts as a "land on head" is passed and then it registers as a jump. As a life-long player of 2D Mario Bros style games, this feels incorrect to me, but I can't quite figure out how to solve it.

My code: (it's really Objective-C but I've put it in pseudo C-style code just to be simpler for non ObjC readers)

void checkCollisions() {

    // For each entity in the scene, compare it with all other entities (but not with one it's already compared against)
    for (int i = 0; i < _allGameObjects.count(); i++) {

            // GameObject is an Entity
            GEGameObject *firstGameObject = _allGameObjects.objectAtIndex(i);


            // Don't check against yourself or any previous entity
            for (int j = i+1; j < _allGameObjects.count(); j++) {


                    GEGameObject *secondGameObject = _allGameObjects.objectAtIndex(j);

                    // Get the collision bounds for both entities, then see if they intersect
                    // CGRect is a C-struct with an origin Point (x, y) and a Size (w, h)

                    CGRect firstRect = firstGameObject.collisionBounds();
                    CGRect secondRect = secondGameObject.collisionBounds();

                    // Collision of any sort
                    if (CGRectIntersectsRect(firstRect, secondRect)) {

                            ////////////////////////////////
                            //                            //
                            // Check for jumping first (???)
                            //                            //
                            ////////////////////////////////
                            if (firstRect.origin.y > (secondRect.origin.y + (secondRect.size.height * 0.7))) {

                                    // the top entity could be pretty far down/in to the bottom entity....
                                    firstGameObject.didLandOnEntity(secondGameObject);

                            } else if (secondRect.origin.y > (firstRect.origin.y + (firstRect.size.height * 0.7))) {

                                    // second entity was actually on top....
                                    secondGameObject.didLandOnEntity.(firstGameObject);

                            } else if (firstRect.origin.x > secondRect.origin.x && firstRect.origin.x < (secondRect.origin.x + secondRect.size.width)) {

                                    // Hit from the RIGHT

                                    CGRect intersection = CGRectIntersection(firstRect, secondRect);

                                    // The NUDGE just offsets either object back to the left or right
                                    // After the nudging, they are exactly pressing against each other with no intersection
                                    firstGameObject.nudgeToRightOfIntersection(intersection);
                                    secondGameObject.nudgeToLeftOfIntersection(intersection);


                            } else if ((firstRect.origin.x + firstRect.size.width) > secondRect.origin.x) {
                                    // hit from the LEFT

                                    CGRect intersection = CGRectIntersection(firstRect, secondRect);
                                    secondGameObject.nudgeToRightOfIntersection(intersection);
                                    firstGameObject.nudgeToLeftOfIntersection(intersection);


                            }
                    }

            }
    }
}

I think my collision detection code is pretty close, but obviously I'm doing something a little wrong. I really think it's to do with the way my jumps are checked (I wanted to make sure that a jump could happen from an angle (instead of if the falling player had been at a right angle to the player below).

Can someone please help me here? I haven't been able to find many resources on how to do this properly (and thinking like a game developer is new for me). Thanks in advance!

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

The simplest way to get you approximately correct beahviour would be to have your CheckCollisions() function consider the relative velocities of the two objects, in addition to their current positions.

Basically, you do this:

Vector3 RelativeVelocity = secondVelocity - firstVelocity;

And then only treat it as a "didJumpOnEntity" event if the resulting RelativeVelocity.y is less than zero. (And reverse this when you're testing the other case, whether SecondRect has jumped on top of FirstRect).

Conceptually, what this is doing is checking whether the objects are moving toward or away from each other on the y axis at the time of collision. If they're moving away from each other (as is the case when objects are bumping into each other from the side, and one jumps), then it doesn't trigger the "jump on" event. It'll only trigger once the first entity starts descending toward the second one, which matches what you see in most platformers. This approach also works in the (much less common) "I'm still moving upward, but the other entity is moving up faster than me, and hits me from beneath" situation, which a naive "am I moving up or down" test would fail on.

A correct solution for this would require a much more complicated set of collision checks. But just adding this simple velocity check should be more than enough for a standard 2D platformer.

\$\endgroup\$
1
\$\begingroup\$

What you could do is set a flag when you collide with a sprite that contains what kind of collision you are currently involved in. Then, if the jumping while touching sideways situation happens, you can tell because the sideways collision flag is still up, which means you can safely ignore the top down collision effect with that sprite.

Make sure to drop whichever flag you aren't using at the moment (so if you are pressing sideways and you jump above the other sprite, once you are no longer colliding sideways a top down collision should be possible) otherwise you could get some non-registering collision going directly from one to the other. You can also add a delay or dead-zone to making the flag reset depending on how much clearance you want before the changed collision behavior.

All of this would have the benefit of having whichever collision side happens first override the others, regardless of the number of sides you have unique behavior for.

Another option would be to make sure that sideways collision always takes precedent over top down collision, so if both are true it acts like a sideways collision, but how effective this would be would depend on the collision logic. It could easily flip your problem to counting as side collision when on top if you are not careful.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .