1
\$\begingroup\$

I am currently developing a simple Super Mario Bros. like platformer in Cocos2d-x v2. I am now at the step of doing wall sliding with two axis-aligned bounding boxes (tiles) but I've not quite found the right way to do it without any issues.

I've tried two methods:

1- Calculating at which position the player would be at the next frame based on a fixed run speed

2- Checking the overlap on both axis and checking which overlap is smaller/bigger

I decided to stick with option no. 2 as the movement system of the game relies on acceleration and not a fixed speed. However, not everything is perfect as while trying to implement the system in my project, it wouldn't work as intended. The player, while walking left or right to a wall, will get stuck on the wall when pressing the jump key or simply not detect when the player has touched the top side when on a one tile tall wall, video explaining everything as well as a visual of which keys I'm pressing/releasing: https://streamable.com/qw4abu

Here is the Collision and Overlap code:

float PlayerObject::RvsRXOverlap(float hX, float hW)
{
    float lov = (this->getPositionX() + this->getContentSize().width) - hX;
    float rov = (hX + hW) - this->getPositionX();

    if (lov <= 0 || rov <= 0)
        return 0.f;

    if (lov < rov)
        return -lov;
    else
        return rov;
}

float PlayerObject::RvsRYOverlap(float hY, float hH)
{
    float bov = (this->getPositionY() + this->getContentSize().height) - hY;
    float tov = (hY + hH) - this->getPositionY();

    if (bov <= 0 || tov <= 0)
        return 0.f;

    if (bov < tov)
        return -bov;
    else
        return tov;
}

bool PlayerObject::CollisionPrediction()
{
    if (pPlayLayer->GetHazards().size() <= 0)
        return false;

    for (GameObject* hazard : pPlayLayer->GetHazards())
    {
        if (hazard->boundingBox().intersectsRect(this->boundingBox()))
        {
            float dtx = this->RvsRXOverlap(hazard->getPositionX(), hazard->getContentSize().width);
            float dty = this->RvsRYOverlap(hazard->getPositionY(), hazard->getContentSize().height);

            if (fabsf(dtx) <= fabsf(dty)) {
                pXAxis = 0;
                this->setPositionX(this->getPositionX() + dtx);
            } else {
                if (dty == 0)
                    pIsOnGround = true;
                
                pYAxis = 0;
                this->setPositionY(this->getPositionY() + dty);
            }
               
        }
    }
    return true;
}

Here is where CollisionPrediction(); is called:

void PlayerObject::update(float dt)
{

    // INPUT DETECTION
    // ...

    if (pIsOnGround) {

        if (GetAsyncKeyState(VK_SPACE) && pYAxis <= 0)
            pYAxis = 1.f * pJumpHeight;
        
        // this is where i think the issue is from...
        if (pYAxis > 0)
            pIsOnGround = false;

    } else {
        pYAxis -= dt * pFallSpeed;
    }

    // APPLYING PYAXIS AND PXAXIS TO THE X-Y COORDINATES
    // ...

    CollisionPrediction();
}
\$\endgroup\$
0

1 Answer 1

0
\$\begingroup\$

I fixed This issue!

The solution: Cast a ray from the player's feet and check if it collides with anything and if it's the case, set the player to be grounded. Like this:

if (fabsf(dtx) <= fabsf(dty)) {
    this->setPositionX(this->getPositionX() + dtx);
} else {
    if (pGCRay.intersectsRect(hazard->boundingBox(), 2.f)) {
        pIsOnGround = true;
    }

    this->setPositionY(this->getPositionY() + dty);
}
\$\endgroup\$

You must log in to answer this question.

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