Closed halx99 closed 10 months ago
Hi,
I have updated the bomb-test project to create two sprites, one with animation and other without animation and left then fall with physics/gravity: https://github.com/paulocoutinhox/axmol-bomb-test
I also make a video to ppl understand:
https://github.com/axmolengine/axmol/assets/395096/fe3bf581-8a59-4551-abac-02b511de56fd
As @rh101 has written: The only thing I've noticed is that a person can get a lot more help if they have shown that a decent amount of effort has been put into tracking down the source of the issue (simply debugging may not be enough).
@paulocoutinhox I have made a small look on it, it needs more time.
It's only a known bug at this moment...may not easy to fix it in short time. So please give some more help.
@paulocoutinhox If you printed out the PhysicsBody variables for each of the two sprites, such as position, rotation etc. etc., you would have noticed that nothing is different between the two objects, except the position.
You could have narrowed the issue down further by checking the Animate
code to see what it actually does, and eventually you would have realised that the only lines that modify the sprite are these 2 lines:
static_cast<Sprite*>(_target)->setSpriteFrame(_origFrame);
static_cast<Sprite*>(_target)->setBlendFunc(blend);
setBlendFunc
doesn't affect anything, so the only thing left to check is setSpriteFrame
. Placing a breakpoint in that method to trace through it and see what code is executed would lead you to the only line that would make sense to affect anything, being:
setTextureRect(spriteFrame->getRect(), _rectRotated, spriteFrame->getOriginalSize());
The contents of the setTextureRect
is:
void Sprite::setTextureRect(const Rect& rect, bool rotated, const Vec2& untrimmedSize)
{
_rectRotated = rotated;
Node::setContentSize(untrimmedSize);
_originalContentSize = untrimmedSize;
setVertexRect(rect);
updateStretchFactor();
updatePoly();
}
For a quick test, one could simply comment out the contents to see what happens, and funny enough, things start working. Uncommenting lines until the issue re-appears would reveal this one line as the culprit:
Node::setContentSize(untrimmedSize);
Putting a breakpoint on that call shows the untrimmedSize
as 97.5, 80.0
.
Now, a few things to note:
1 - The frame sizes are all 78,64
, as shown in the PLIST file.
2 - Your own code has this call: bomb->setContentSize(Vec2(78, 64));
3 - They don't match the untrimmedSize
being set, implying that some kind of content scaling is happening, most likely due to Director::setContentScaleFactor
If you comment out the hard-coded line, bomb->setContentSize(Vec2(78, 64));
, then everything starts working, although your sprite without animation is huge and incorrect:
That is because of this line: bomb->setTexture("bomb.png");
. You're using the entire sprite sheet for the image texture, which is strange. It would have made more sense to call this: bomb->setSpriteFrame("frame1.png");
, but that would require fixing other issues in your code, such as ensuring that the sprite sheet is loaded only once, and not just for the animated sprite.
So, move this code to MainScene::init()
, which is where it belongs:
auto spriteFrameCache = SpriteFrameCache::getInstance();
spriteFrameCache->addSpriteFramesWithFile("animation.plist", "bomb.png");
To unload it, you can do so in the destructor, MainScene::~MainScene
, or when you're done with it (meaning no sprites references those textures).
Now, knowing that removing the call bomb->setContentSize(Vec2(78, 64));
fixes the issue means something else is happening.
Remember the untrimmedSize
as 97.5, 80.0
, well that means things are being scaled. So, based on that, testing with a content scale factor to 1.0, via director->setContentScaleFactor(1.0f);
, then everything also works correctly, regardless of whether the call to bomb->setContentSize(Vec2(78, 64));
is made or not.
The content scale factor of 1 would mean the untrimmedSize
is always 78,64
, so the call to Node::setContentSize(untrimmedSize);
doesn't actually do anything, given the original size is the same as the new size:
void Node::setContentSize(const Vec2& size)
{
if (!size.equals(_contentSize))
{
_contentSize = size;
_anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y);
_transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true;
}
}
The only thing I can think of is that changing the size of the sprite after the physics body has been created is somehow affecting the transforms, which are used in calculations within the PhysicsBody.
So, in summary, what works is any of the following:
Delete the line bomb->setContentSize(Vec2(78, 64));
or
Set it to the correct scaled size (being 97.5,80
) via: bomb->setContentSize(Vec2(78, 64) / AX_CONTENT_SCALE_FACTOR());
or
Only set the content size of the sprite if and only if the sprite is not animating, so calling bomb->setContentSize(Vec2(78, 64));
on the non-animating sprite is fine, since the size never changes.
or
Use a content scale factor of 1.0 via director->setContentScaleFactor(1.0f);
The most important thing to remember is: The content size of a sprite cannot be changed after the physics object is set on a node. This means you should not call setContentSize()
with a different size (directly or indirectly) after you have set the physics body on the sprite. You can scale the sprite, rotate it, or anything else, just do not call setContentSize
once a physics body is attached. This also means sprite frames in an animation must all be the same size.
In the end, this entire issue can be reproduced with a few lines that have nothing to do with Animate
:
auto sprite = Sprite::create("SomeImage.png");
auto physicsBody = PhysicsBody::createCircle(sprite->getContentSize().width / 2, PhysicsMaterial(1.0f, 0.1f, 0.8f));
physicsBody->setMass(1.0f);
sprite->setPhysicsBody(physicsBody);
sprite->runAction(Sequence::createWithTwoActions(DelayTime::create(1.5f), CallFunc::create([sprite]() {
sprite->setContentSize(sprite->getContentSize() / 2); // Problem will show itself after 1.5 seconds, when setContentSize is called with a different size
})));
Added a wiki hint
Hi,
In my code don't have anything changing content size after set physics body. I don't know if you see my code, but i will paste it here:
Sprite *MainScene::createBomb(bool animated)
{
auto bomb = Sprite::create();
if (animated)
{
// create animation
auto spriteFrameCache = SpriteFrameCache::getInstance();
spriteFrameCache->addSpriteFramesWithFile("animation.plist", "bomb.png");
// create sprites
Vector<SpriteFrame *> frames;
char frameName[100];
for (int i = 1; i <= 4; i++)
{
sprintf(frameName, "frame%d.png", i);
SpriteFrame *frame = spriteFrameCache->getSpriteFrameByName(frameName);
if (frame)
{
frames.pushBack(frame);
}
}
// create animation
auto animation = Animation::createWithSpriteFrames(frames, 0.1f);
auto animate = Animate::create(animation);
bomb->runAction(RepeatForever::create(animate));
}
else
{
bomb->setTexture("bomb.png");
}
bomb->setName("shoot");
bomb->setContentSize(Vec2(78, 64));
// setup the physical body
auto physicsBody = PhysicsBody::createCircle(bomb->getContentSize().width / 2, PhysicsMaterial(1.0f, 0.1f, 0.8f));
physicsBody->setMass(1.0f);
bomb->setPhysicsBody(physicsBody);
return bomb;
}
It create a animated or not sprite, and at last, it add physics body.
The line that change something on it and make it work is related to scale as you write above, not content size position in my code, since it is before physics body definition:
bomb->setContentSize(Vec2(78, 64) / AX_CONTENT_SCALE_FACTOR());
or
bomb->setContentSize(Vec2(78, 64) / _director->getContentScaleFactor());
Thanks.
In my code don't have anything changing content size after set physics body. I don't know if you see my code, but i will paste it here:
As already mentioned in my previous post, setContentSize
cannot be called after the physics body is set, whether it is from your own code or indirectly through other code paths. In your case, it is done so via setSpriteFrame
, which is called by via the Animate
action, as I explained in detail in my previous post.
The line that change something on it and make it work is related to scale as you write above, not content size position in my code, since it is before physics body definition:
That is not correct. It is definitely setContentSize
that is causing the issue, and nothing to do with scaling the object. Just because the content scale factor is used doesn't mean the object is being scaled. You're just setting the correct size by using it, which means when the animation is playing, the size is exactly the same, so all calls to setContentSize
made during the animation will not change anything, because of this if (!size.equals(_contentSize))
check in Node::setContentSize
.
If that is still not completely clear, just re-read the post I made above, and follow the code path yourself to see what actually happens.
Hi, relax man, i read it, all lines.
What i do to solve is set content size with original frame size and change only the scale:
bomb->setName("shoot");
bomb->setContentSize(Vec2(78, 64) / AX_CONTENT_SCALE_FACTOR());
bomb->setScale(0.5);
This is wrong?
This is wrong?
Sorry, I'll clarify. This code is correct:
bomb->setContentSize(Vec2(78, 64) / AX_CONTENT_SCALE_FACTOR());
What is not correct is this remark regarding the content size and your usage of it:
The line that change something on it and make it work is related to scale as you write above, not content size position in my code, since it is before physics body definition:
The reason is that the line bomb->setContentSize(Vec2(78, 64)
was setting a content size of 78,64
, but when it is animating, setSpriteFrame
was calling setContentSize
with 97.5, 80.0
, because of the scale factor. As a result of this, whatever parameters are changed within the node end up affecting the physics object. That is why the fix is to divide by the content scale factor when setting the content size, since future calls to the setContentSize
by the Animate code will no longer trigger changes in that node.
Nice. Thanks.
rsrsrsrs
@paulocoutinhox
rsrsrsrs
[Animation]
What is the reason to put it here?
Please close this issue if is "fixed".
I can't, it was not open by me
Discussed in https://github.com/axmolengine/axmol/discussions/1545