Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

First of all, this will be Open Source, and you will be mentioned for the help.


We've been programming a Super Mario Remake for the iPhone with Cocos2d for school.
It looks pretty great, and we decided to make it Open Source, because we can't get any profit because of Copyright issues.

We programmed the physics on our own, but it has a few issues.

Collisions are constantly checked, and this is how I do it:


Collision Handling

- (void)updateCollisions:(ccTime)delta {
    for (STGameObject *child in self.gameObjects) {
        for (STGameObject *child2 in self.gameObjects) {
            // Don't check same object
            if (child == child2) {
                break;
            }

            if (STRectIntersect(child.boundingBox, child2.boundingBox)) {
                // Position objects
                STRectEdge edge1 = [self updateCollisionOfGameObject:child withGameObject:child2 delta:delta];
                STRectEdge edge2 = [self updateCollisionOfGameObject:child2 withGameObject:child delta:delta];

                // Send notifications
                [child collisionWithGameObject:child2 edge:edge1];
                [child2 collisionWithGameObject:child edge:edge2];
            }
        }
    }
}

- (STRectEdge)updateCollisionOfGameObject:(STGameObject *)gameObject
                           withGameObject:(STGameObject *)gameObject2
                                    delta:(ccTime)delta {

    STRectEdge rectEdge;
    float edgeLeft = (gameObject.boundingBox.origin.x - gameObject2.boundingBox.origin.x - gameObject.boundingBox.size.width) * -1;
    float edgeRight = (gameObject.boundingBox.origin.x + gameObject2.boundingBox.size.width - gameObject2.boundingBox.origin.x);
    float edgeTop = (gameObject.boundingBox.origin.y + gameObject.boundingBox.size.height - gameObject2.boundingBox.origin.y);
    float edgeBottom = (gameObject.boundingBox.origin.y - gameObject2.boundingBox.size.height - gameObject2.boundingBox.origin.y) * -1;

    float offset = 0.0;
    if (edgeLeft < edgeRight) {
        rectEdge = STRectEdgeMinX;
        offset = edgeLeft;
    } else {
        rectEdge = STRectEdgeMaxX;
        offset = edgeRight;
    }

    if (edgeTop < edgeBottom) {
        float cached = edgeTop;
        if (cached < offset) {
            rectEdge = STRectEdgeMaxY;
            offset = cached;
        }
    } else {
        float cached = edgeBottom;
        if (cached < offset) {
            rectEdge = STRectEdgeMinY;
            offset = cached;
        }
    }

    if (gameObject.bodyType != STGameObjectBodyTypeStatic) {
        if (gameObject2.bodyType != STGameObjectBodyTypeStatic) {
            offset /= 2.0;
        }

        if ([gameObject bodyType] != STGameObjectBodyTypeNonColliding && [gameObject2 bodyType] != STGameObjectBodyTypeNonColliding) {
            switch (rectEdge) {
                case STRectEdgeMinX:
                {
                    [gameObject move:ccp(offset, 0)];
                }
                    break;
                case STRectEdgeMaxX:
                {
                    [gameObject move:ccp(-offset, 0)];
                }
                    break;
                case STRectEdgeMinY:
                {
                    [gameObject move:ccp(0, offset)];
                }
                    break;
                case STRectEdgeMaxY:
                {
                    [gameObject move:ccp(0, -offset)];
                }
                    break;
            }

            if (rectEdge == STRectEdgeMinY && gameObject.velocity.y < 0) {
                gameObject.velocity = ccp(gameObject.velocity.x, 0);
            }
            if (rectEdge == STRectEdgeMaxY && gameObject.velocity.y > 0) {
                gameObject.velocity = ccp(gameObject.velocity.x, 0);
            }
        }
    }

    return rectEdge;
}

It basically checks which collision edge of the rect is the smallest (easiest to adjust).
The problem now is the following

enter image description here

As you can see, if Mario collapses a brick at it's bottom edge, it will look like there was a collision at the minimum Y edge. However, this is not the case. This causes to constantly destroy blocks, even though there was no real collision at this edge, but rather at the minimum X edge.

Any ideas how to resolve this?

share|improve this question
1  
Its worth noting that being Open Source doesn't make you immune from copywrite infringement. –  Richard Tingle May 21 '13 at 12:00
    
@RichardTingle Thanks, I'll change the sprites before making it Open Source –  NSAddict May 21 '13 at 12:01
    
A hacked solution would be to check whether a block is "eligible" to be destroyed based upon if its lower neighbour exists –  Richard Tingle May 21 '13 at 12:03
    
@RichardTingle Unfortunately not, because it can be destroyed with a Koopa-Armor... –  NSAddict May 21 '13 at 12:23

3 Answers 3

up vote 2 down vote accepted

Ok, i'm assuming your existing collision detection has determined that a corner collision has occurred and the only question that remains is "Which edge hit first".

This is equivalent to the question "which has been inside the edge the least time". This is because if you imagine the edge as being an infinite length then for a collision to occure between object A and object B BOTH edges of object B must be inside the infinite lines of A. The second component to achieve this "makes" the collision and is the colliding edge. Note that for a usual collision on say just the low X edge the object has been inside the infinite Y edges for a very long time (maybe the entire game)

TimeInsideObject=CollisionDepthOnAxis/RelativeVelocityOnAxis

(eg if something is 1 metre inside and is travelling at 10m/s it has been inside for 0.1 seconds)

So if the penitration depths are:

 x=0.1m and y=0.2m 

and the relative velocities are:

 xV=10m/s and yV=0.1m/s 

the times inside the object (on each axis) are:

X: TimeInsideobject=0.1/10=0.01 seconds
Y: TimeInsideobject=0.2/0.1=2 seconds

So axis X "made" the collision so its a low or high X edge that actually had the collision.


Before Collision:
Before Collision
After Collision:
enter image description here

You should also resolve your collisions in the order they occure based upon this value, for example, mario collided with the lower bricks low X side before he collided with the upper bricks low Y side, if the lower brick collision is resolved first then no collision with the upper bricks occures

share|improve this answer
    
I guess that could mostly solve my problem. Our physics constantly apply a force of gravity to all objects. This is why the y and x collision could, when walking, occur at the same time, which could cause the exact same problem. I'll have to try it out to make sure. Thanks so far! –  NSAddict May 21 '13 at 15:32
    
@NSAddict Are you dealing with collision response and contact response seperately? To get satisfactory behaviour out of the physics engine im making I had to go on each tick: collision response, apply gravity, contact response, move. This means that an object in contact with the floor isnt constantly falling into the floor and being reset upwards. Admittedly this was for a 3D engine and might be unnessisary for your application. –  Richard Tingle May 21 '13 at 16:23
    
The physics are really easy. Every game object has a velocity, and the gravity adds a bit y velocity every update. Then, the object is moved by its velocity. Then, all the collisions are being resolved. –  NSAddict May 21 '13 at 17:38
    
And presumably all the collisions are inelastic; no bouncing. Then what you're doing will all work well. When you have elastic collisions you can get an unpleasent jittering at rest unless you're careful –  Richard Tingle May 21 '13 at 18:18
    
Sorry for not coming back to you, I completely forgot. this fixed my issue –  NSAddict Jul 3 '13 at 12:07

Now that you are implementing the physics on your own, as well as the collision code, I think the best answer lies within this tutorial. After the effect from the collision is calculated, you just consider the closest edge based on the average X position.

share|improve this answer
    
I had a quick look and this might help. I'll try it out tonight. Thanks! –  NSAddict May 21 '13 at 15:30

Don't collide against inner edges. A simple pass over your tiles can determine if the tile borders an open space on any particular edge. Set a glag for each edge in a given tile marking if its an outer edge (collideable) or inner edge (ignored).

One nice trick while testing this and other physics is to have a debug draw mode that renders all outer edges. If you turn off regular tile drawing you should see the outline of your world.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.