Animation using SpriteFrameCache affects physics body?

I tried to load a spritesheet.png file using a plist file for animation. My problem is using SpriteFrameCache affects physics body. I expected the physics body would not move during animation, but the animation seems putting some force to the physics body that I attached to mSprite. It doesn’t make sense to me. So I tried to do it with individual png file using SpriteFrame and it works fine. Anyone has idea why SpriteFrameCache affects physics body? Your help would be appreciated. :slight_smile:

//Animations using SpriteFrameCache (affects physics body)
Vector<SpriteFrame*> frames;
Size playerSize = mSprite->getContentSize();
auto frame1 = Sprite::createWithSpriteFrameName("fly1.png");
auto frame2 = Sprite::createWithSpriteFrameName("fly2.png");
frames.pushBack(SpriteFrameCache::getInstance()->spriteFrameByName("fly1.png"));
frames.pushBack(SpriteFrameCache::getInstance()->spriteFrameByName("fly2.png"));
auto animation = Animation::createWithSpriteFrames(frames, 0.2f);
mSprite->runAction(CCAnimate::create(animation));

//Animations using SpriteFrame only (work as expected)
Vector<SpriteFrame*> frames;
Size playerSize = mSprite->getContentSize();
frames.pushBack(SpriteFrame::create("fly2.png", Rect(0, 0, playerSize.width, playerSize.height)));
frames.pushBack(SpriteFrame::create("fly1.png", Rect(0, 0, playerSize.width, playerSize.height)));
auto animation = Animation::createWithSpriteFrames(frames, 0.2f);
mSprite->runAction(CCAnimate::create(animation));

I am having the same problem.

The physics body will change and start moving my sprite in weird directions when the animation its running. I think the key problem here is that the rect of the spriteframes returned from SpriteFrameCache are different one another (because of the way sprites are packed in the spritesheet). This difference in the Rects of the sprites returned from SpriteFrameCache seems to make my physicsbody size change making the sprite move erratically.

I think the physicsbody size shouldn’t change with the animation but hadn’t figured out a way to prevent that from happening yet.

So after debugging a bit more I figured out that the size of the physics body wasn’t a problem as it wasn’t changing but had something to do with the anchor point. See if the images are different in size the physics boy would be moving with the anchor point that was changing because the different sizes.

I solved my problem with either one of two solutions.

Solution A was changing the physics body from Box to circle.

Solution B (the one I am using) its to assign anchor point 0,0 to the SpriteFrames given by SpriteFrameCache.

The sprites i’ve been using for the animation arent one another followed in the name by numbers or anything so I took them one after the other from SpriteFrameCache and added them to a Vector<SpriteFrame*>

Here is some code:

auto physicsBody = PhysicsBody::createBox(Size(this->getContentSize().width,this->getContentSize().height),PhysicsMaterial(0.1f, 1.0f, 0.0f));

//Add the physics body
    this->addComponent(physicsBody);

//Create an animation for the character for jumping
    //We will do a 4 frames animation as follows : stand => ready => stand => jump and keep jump until touching something
    char buff[100] = {0};
    //Generate the frames using the spritnf to change between female(genderNum=2) an male(genderNum=1)
 
    //Stand
    snprintf(buff, sizeof(buff), "bunny%d_stand.png", genderNum);
    auto frameStand = SpriteFrameCache::getInstance()->getSpriteFrameByName(buff);
    
    //Ready
    snprintf(buff, sizeof(buff), "bunny%d_ready.png", genderNum);
    auto frameReady = SpriteFrameCache::getInstance()->getSpriteFrameByName(buff);
    
    //Jump
    snprintf(buff, sizeof(buff), "bunny%d_jump.png", genderNum);
    auto frameJump = SpriteFrameCache::getInstance()->getSpriteFrameByName(buff);
    
    /*
     * Fix for the weird physics animation behaviour with SpriteFrameCache.
     **/
    //SpriteFrameCache does something really weird with the physic body when animating. We move the anchor point to 0,0 to avoid
    //the physics body from moving if the rect of the animated sprites have not the same size.
    frameStand->setAnchorPoint(Vec2(0, 0));
    frameReady->setAnchorPoint(Vec2(0, 0));
    frameJump->setAnchorPoint(Vec2(0, 0));
    
    //Now we just need to add them to animJump Frames
    Vector<SpriteFrame*> animJumpFrames;
    animJumpFrames.reserve(4);
    //Add the frames
    animJumpFrames.pushBack(frameStand);
    animJumpFrames.pushBack(frameReady);
    animJumpFrames.pushBack(frameStand);
    animJumpFrames.pushBack(frameJump);
    
    //Create the animation that we will play
    jumpAnim = Animation::createWithSpriteFrames(animJumpFrames, 0.05f);

Then after this when I call my jump() function I just call :

this->runAction(Animate::create(jumpAnim)); 

The key part of code that fixed my problem was changing the frames I created from SpriteFrameCache to have an anchor point set to 0,0 like this:

frameStand->setAnchorPoint(Vec2(0, 0));
frameReady->setAnchorPoint(Vec2(0, 0));
frameJump->setAnchorPoint(Vec2(0, 0));

Hope this is useful in case someone stumbles upon the same problem trying to animate with actions an sprite with a physics body attached.

I know this topic is pretty old, but I had the same problem and found a workaround.
The problem is that the position of PhysicsBody is internally set with reference to AnchorPoint(0,0), but the sprite is supposed to be anchored at (.5,.5).
To obtain a pointer to the lower left corner of the body, the class uses the private member Vec2 _ownerCenterOffset.
From CCPhysicsBody.cpp, PhysicsBody::beforeSimulation(…)

// set position
auto worldPosition = _ownerCenterOffset;
nodeToWorldTransform.transformVector(worldPosition.x, worldPosition.y, worldPosition.z, 1.f, &worldPosition);
setPosition(worldPosition.x, worldPosition.y);

If you change the SpriteFrame to another one which has got a different size, _ownerCenterOffset is not updated,
As a workaround, you can call PhysicsBody::onAdd after you change the SpriteFrame. PhysicsBody::onAdd updates _ownerCenterOffset:

void PhysicsBody::onAdd()
{
_owner->_physicsBody = this;
auto contentSize = _owner->getContentSize();
_ownerCenterOffset.x = 0.5f * contentSize.width;
_ownerCenterOffset.y = 0.5f * contentSize.height;
ecc,

Hope this may help someone; it may be a good idea to fix this once for all, adjusting _ownerCenterOffset every time the Sprite changes its size.