[SOLVED] Changing physics body during animation?

I’m putting together a project in Cocos2d-x, and I’m trying to have a sprite change its physics body with each frame of the sprite sheet.

I know that this is remarkably inefficient, but it’s more a proof-of-concept than anything at this point, and the app isn’t that intensive. The problem is that after the physics body is set during the scene’s init method, I cannot seem to reassign it. I can remove it, but not update it.

Relevant code:

bool MainScene::init()
{
    //Super init
    if ( !Layer::init() )
    {
        return false;
    }


    // Init physics, create physics body for dog and assign

    shapeCache = PhysicsShapeCache::getInstance();
    shapeCache->addShapesWithFile("corgiPhysics.plist");
    shapeCache->setBodyOnSprite("corgi-2", dogSprite);
}

The above is loading a plist made in Physics Editor and assigning a specific body.

void MainScene::update(float dt)
{
    Layer::update(dt);

    if (dog->getDirection() == Direction::Down)
    {
        Animate* animation = dog->getAnimation("up");
        int frame = animation->Animate::getCurrentFrameIndex();
        int fileref = frame + 9;
        const std::string filename = "corgi-" + std::to_string(fileref);
        dogSprite->getPhysicsBody()->removeFromWorld();
        shapeCache->setBodyOnSprite(filename, dogSprite);
    }
    if (dog->getDirection() == Direction::Up)
    {
        Animate* animation = dog->getAnimation("down");
        int frame = animation->Animate::getCurrentFrameIndex();
        int fileref = frame + 3;
        const std::string filename = "corgi-" + std::to_string(fileref);
        dogSprite->getPhysicsBody()->removeFromWorld();
        shapeCache->setBodyOnSprite(filename, dogSprite);
    }
}

The above is checking to see what the animation frame is and changing the physics body accordingly.

void Dog::moveDogUp()
{
    stopAllActions();
    this->getChildByName("corgiStanding")->stopAllActions();
    auto moveUp = MoveBy::create(1.8f, Vec2(0, 1500));
    auto moveUpEaseIn = EaseOut::create(moveUp, 1.1);
    runAction(moveUpEaseIn);
    this->getChildByName("corgiStanding")->runAction(RepeatForever::create(upAnimation));
}

Finally, the above is one of the animating functions used.

I’ve broken down a lot of the functions to try to find the problem. I’ve even cut out the loading of the .plist physics bodies and tried to assign a rectangle. I’ve confirmed using cout that the update function is calling the frames and building the correct names for the physics bodies in the plist file. The update function is also removing the physics body created in the scene’s init method (visually confirmed using the debug drawer). I’ve tried changing the physics body outside of the init method (by trying to assign it at the beginning of the moveDogUp method) but no dice.

Are there specific rules to when a physics body can be assigned?

I’ve done some more searching and I think I found a reason. Invoking dogSprite->getPhysicsBody()->removeFromWorld() does not remove the physics body from the sprite. I’m still able to return a pointer address for the physics body using dogSprite->getPhysicsBody().

Is this by design? And if so, is there a definite way to destroy the physics body so that a new one can be created?

By the way, I’m using Cocos2d-x 3.10.

Looks like there is a bug with the addComponent implementation of setPhysicsBody. There was a solution posted on GitHub and is accessible here:

I propose that you avoid changing the physics bodies during animation. Try finding a shape that fits all animation frames more or less. The player will most likely not even see any difference.

The reason is that it changes the mass of your object. Consider a sprite that runs. While the arms and legs are in line the surface of the sprite is smaller. This means that he has less weight!

Take a look at this tutorial I creates some time ago for Ray Wenderlich:

The source is cocos2d - but the section about creating the sprites is still valid for cocos2d-x.

@AndreasLoew thank you very much for the advice, and thanks for making such great products!

I would normally 100% agree with you on the downfall of changing physics bodies. I actually saw your tutorial a while ago and learned a lot from it. The only reason I felt comfortable with doing it was because “physics” as implemented in this project is only for collision testing. All motion is done through cocos2dx Actions, and nothing is affected by the physics simulation itself. Therefore, the changing mass doesn’t have an effect in this case!

But I definitely agree: for cases where the physics of the environment is actually being simulated by the physics world, changing the physics body is a no-no.

Ok - sure - that’s no problem.

I know from PhysicsEditor’s support that many people are trying to be too perfect with their collision shapes and wonder about strange behavior in the game… so I try to help them before they waste hours of time :slight_smile:

Jut a couple of thoughts:

  1. Is it practical to have a single physics body with multiple parts to suit the different sprites? e.g. a rag-doll type of effect?

  2. As you say it is just for collision detection, could you have multiple sprites each with its own physics body, and implement your own animation by swapping them? Or even attach all the physics bodies but make them each active/inactive depending on the sprite frame.

(forgive me if I’m barking up the wrong tree - I use Box2d not the inbuilt physics, so not sure of the capabilities.)

Hey @Maxxx, thanks for the feedback!

  1. I’m sure that’s a more elegant solution to deal with the problem. The main problem I’d have with that is that the overall silhouette of the sprite animation doesn’t lend itself to ragdoll-style physics parts. The motion is diagonal in an orthographic projection, so it would be tricky.

  2. I may need @AndreasLoew to step in and correct me if I’m wrong here, but I believe the PhysicsShapeCache class he created that serves as a bridge between Cocos2d-x and his (fantastic) Physics Editor program actually preloads all of the physics bodies in the .plist. So using setPhysicsBody() is a de facto way of doing what you suggested (setting the bodies active/inactive). The setPhysicsBody() method simply tells the engine what physics body (that is already loaded into memory) should be honored on the sprite in question.

The problem came because somewhere around v3.8 or 3.9 the setPhysicsBody() method was changed to integrate more cleanly into the Component model of doing things in Cocos2d-x (at least from what I can tell). The fix I linked to in my earlier comment solves the problem by directing the app to remove an existing physics body when adding a new one.

@matthewlehew PhysicsShapeCache loads the shapes from the plist - that’s right.

I’ll have to see if addComponent is what I should use instead. I have not yet investigated in this direction.

Another thing I found is that I had 2 people complaining about problems on Android. Whereas all other platforms work without problems.

Did you experience problems here?

@AndreasLoew I have not ported to Android so I can’t say for sure. And I don’t think you need to make any changes to your code, Cocos2d-x changed the implementation of setPhysicsBody() so that the method adds the physics body as a component, but didn’t make any allowances for what to do when the sprite already has a physics body attached. There are no notes indicating that getPhysicsBody() is deprecated so I think you’re fine.