2
\$\begingroup\$

I'm building a platformer in cocos2d, my first game project. I'm working on movement and collision detection. I'm using a tilemap with a "meta" layer of invisible blocks that are designated collidable.

Everything seems to work pretty well except for one minor detail: when the character jumps from above into a platform, he does not fall but clips through it. If he jumps from below, it recognizes it and resets his Y velocity accordingly. I know that it's because of how I'm detecting the collisions, and just doing a simple comparison of the origin.y's, but I'm unsure of how to refactor this code better:

- (void) update: (ccTime) dt {
    CGPoint scaledVelocity = ccpMult(leftJoystick.velocity, 35.0f);
    CGPoint newPosition = ccp(player.position.x + scaledVelocity.x * dt, player.position.y);
    CGPoint oldPosition = [player position];

    float velocityX = player.Velocity.x + scaledVelocity.x * dt;
    float velocityY = player.Velocity.y;

    BOOL isCollision = NO;
    BOOL isCollisionBelow = NO;
    BOOL isCollisionRight = NO;
    BOOL isCollisionLeft = NO;

    NSMutableArray* collisionSprites = [[NSMutableArray alloc] init];

    if (oldPosition.x > newPosition.x) {
        player.flipX = YES;
    }
    else if (oldPosition.x < newPosition.x) {
        player.flipX = NO;
    }

    for (CCSprite* sprite in gameplayLayer.collidableTiles) {
        if (CGRectIntersectsRect(player.boundingBox, sprite.boundingBox)) {
            isCollision = YES;
            [collisionSprites addObject:sprite];
        }
    }

    // Figure out which direction collision is in
    if (isCollision) {
        for (CCSprite* collisionSprite in collisionSprites) {
            if (collisionSprite.boundingBox.origin.y < player.boundingBox.origin.y) {
                isCollisionBelow = YES;
            }
            if ((collisionSprite.boundingBox.origin.x > player.boundingBox.origin.x) && (collisionSprite.boundingBox.origin.y >= player.boundingBox.origin.y)) {
                isCollisionRight = YES;
            }
            if ((collisionSprite.boundingBox.origin.x < player.boundingBox.origin.x) && (collisionSprite.boundingBox.origin.y >= player.boundingBox.origin.y)) {
                isCollisionLeft = YES;
            }
        }
    }

    if (isCollisionLeft) {
        velocityX = 0.5f;
    }
    else if (isCollisionRight) {
        velocityX = -0.5f;
    }

    if (isCollision && !isCollisionBelow && player.characterState != kStateFalling) {
        velocityY = 0;
        velocityY -= 0.8f;
        [player setCharacterState:kStateFalling];
    }
    else if (!isCollisionBelow) {
        velocityY -= 0.7f;
    }
    else if (isCollisionBelow) {
        velocityY = 0;
        [player setCharacterState:kStateIdle];
    }

    if (rightButton.active) {            
        if (player.characterState != kStateJumping) {
            velocityY += 15.0f;
            [player setCharacterState:kStateJumping];
        }
    }

    // Ground friction
    if (player.Velocity.x > 0) {
        if (player.Velocity.x < 0.5f) {
            velocityX = 0;
        }
        else {
            velocityX -= 0.5f;
        }
    }

    if (player.Velocity.x < 0) {
        velocityX += 0.5f;
    }

    [player setVelocity:ccp(velocityX, velocityY)];
    [player setPosition: ccp(player.position.x + player.Velocity.x, player.position.y + player.Velocity.y)];
    [gameplayLayer setViewpointCenter:newPosition];
    [player setOldPosition: player.position];
}

How can I refactor this code to work properly from all angles and reduce the complexity (I feel that there's too many booleans, and they all get a bit confusing).

\$\endgroup\$
1
  • 2
    \$\begingroup\$ I take it this code is example code from somewhere? There's already logic to detect collisions from the bottom and sides; mirror those code blocks for a new boolean isCollisionAbove, making the necessary changes to the logic. \$\endgroup\$ Commented Sep 5, 2011 at 4:36

2 Answers 2

1
\$\begingroup\$

As far as I know the Boundingbox rect has two properties origin and size. to check if some obejct is colliding from any of the sides you have to check both values: so I suggest you change your code like following :

if (CGRectMaxY(collisionSprite.boundingBox) < CGRectMinY(player.boundingBox)) {
    isCollisionBelow = YES;
}
if (CGRectMaxX(collisionSprite.boundingBox) < CGRectMinX(player.boundingBox)) {
    isCollisionRight = YES;
}
if (CGRectMinX(collisionSprite.boundingBox) > CGRectMaxX(player.boundingBox)) {
    isCollisionLeft = YES;
}
\$\endgroup\$
0
\$\begingroup\$

I'm afraid I dont have an answer in cocos2d, but i can give it to you in pseudocode.

Summary
The point with doing collision detection is to stop the player before it collides with an object. To do this you simply need to check if the players new position collide (or intersects) with the object, and then change the players position.

How to do this:
To do this you create a function which you call every time you're about to change the players coordinates. This function simply calculates the new position then passes it to a another function which then check if the new position collides. Then if the new position does not collide, change the players position, and voila collision detection and reaction!

Example:
If the player presses left -->
Calculate players new coordinats -->
New_position.x = player.x - (movement_speed * delta-time) -->
Check new_position.x for collision -->
If new_position.x collides - do nothing -->
If new_position.x does not collide - change players.x to new_position.x -->

This way the collision check never returns true for the players actual position, thus allowing the player to move freely even if it is on top of the object (theoretically colliding with the object).

If you want the collision to be more true to real physics you could of course always change the players position, but if it collides change the force (or movement speed) to zero.

There are of course other ways of doing this. For example one could check the players position according to the object (left_zone, right_zone, top_zone, bottom_zone), but this would be much slower and requires a lot more code.

Basic collision detection function (written in javascript, but should be easy to understand):

function collision(obj_1, obj_2) {
    var collision = Boolean(
        obj_1.x <= (obj_2.x + obj_2.width)
        && obj_2.x <= (obj_1.x + obj_1.width)
        && obj_1.y <= (obj_2.y + obj_2.height)
        && obj_2.y <= (obj_1.y + obj_1.height + height_margin)
    );

    // return true or false
    return collision;
}
\$\endgroup\$

You must log in to answer this question.

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