Detach child from parent but keep transform as is

in my game the player carries a barrel which he can toss at flying enemies. What would be the best way to make the barrel follow the player?
Right now I have the barrel set up as a child to the player so that it will follow him, but when I toss it I have to detach it until the player catches the barrel again. If I do that, the barrel disappears since there are no references to it (i.e. it will be released). So I tried adding the barrel to the scene while being tossed, but then it gets huge since the scale of the player is smaller than the scene’s scale. I could not find a way around this.
My first question is: Should the barrel be a child of the player in the first place?
Second question: If so, how can I keep the barrels transform and if it shouldn’t be a child, how would I go about it instead?

how are you detaching the barrel from the player?

removeFromParent(); or removeFromParentAndCleanup();?

If you need it again…perhaps setVisible(false); and then setVisible(true); when you need it again and reset position, etc before calling?

I use removeFromParentAndCleanup();
Does this make a difference? I thought removeFromParent is just a convenience function which call removeFromParentAndCleanup under the hood, am I wrong here?
When I detach the barrel I still need it to be visible so setVisible() is not an option.
When I hook it up with another parent it gets scaled and moved. I couldn’t find a way to prevent this. If you know of one please let me know.
I could set position, scale, etc. manually when switching parents, but am wondering if there is a better way to do this. It just seems easy to forget a property and produce subtle bugs this way. What do you think of this approach and can you think of another one?

Just out of curiosity, what do you use the scaling for exactly? Is there a specific reason for this, or at least a reason for not having the player and the scene at the same scaling value?

Now, a possible solution is to treat the player and barrel as two different entities. The player can carry the barrel, but the barrel isn’t part of the player, if that makes sense, so it shouldn’t be a child of the player node.

For example, create a new class, like Player, within which you store a reference to the barrel. The barrel should be a child of the scene. That Player class can keep the position of the barrel up-to-date as long as it has a reference to the barrel (meaning the player is still holding on to it). If the Player class inherits from Sprite, then you can override something like the setPosition() method to update the position of the barrel as well (with whatever offsets you need to make sure it’s visible in the right spot on the player).

1 Like

Hi R101,
the different scaling is a good point. I actually don’t have a good reason I just created my art on a higher resolution than necessary, but I could also scale it down before importing it and leave the scale at 1 which would remove at least that part.
To your suggestion of keeping the entities separate I have one question: Would you adjust the position of the barrel in the player’s update method or how would you go about it?

Because I had done that before, but was running into a weird issue that the barrel was drawn lower with respect to the player after I tossed it once than it had been drawn before. So e.g. if the barrel starts on the same height 0 as the player then after a toss it it would be at -10. I couldn’t figure out why this was the case so I wanted to try the approach described here.

There are multiple ways to deal with this, and it comes down to how you want to design your game logic.

You can update it in the update() method, but it’s perhaps not the best idea, because it’s called every single frame, and will call logic with or without anything actually changing. That is the reason I mentioned to only do the updates to the barrel position in functions like setPosition(). This is all assuming you’re going the route of inheriting from Sprite and implementing functionality in that derived class.

For example:

class Player : public cocos2d::Sprite
    void setPosition(const Vec2& pos) override 
        if (_barrel != nullptr)
            _barrel->setPosition(pos + _barrelOffset);
    void setBarrel(cocos2d::Sprite* barrel)
        _barrel = barrel;
        if (_barrel != nullptr)
             _barrel->setPosition(pos + _barrelOffset);
    cocos2d::Sprite* _barrel = nullptr; 
    Vec2 _barrelOffset = Vec2(10, 20);  //example of where the barrel should be relative to the player in the scene, if it is held by the player

If the barrel is thrown, then you just call player->setBarrel(nullptr); to clear it, so the player no longer controls the barrel movement, since you’ll be handling that somewhere else, right?

Given that the barrel would be a child of the scene, and not of the player, then this shouldn’t happen, because the barrel position is always relative to the scene, and not the player. Once you detach the barrel from the player (by calling setBarrel(nullptr) in the example above), the player no longer has control, and the barrel’s parent node hasn’t changed, so it’ll still be where it was when you detached it, until something else affects its movement (like you calling some function to make it look like it’s been thrown etc.).

Is this making any sense?

1 Like

It’s making a lot of sense. I will give it a go if I don’t find a good solution with my current approach. I’m not quiet ready to abandon it yet, since I almost have what I need.
Thanks for your help so far, I will keep you in the loop how it goes.

1 Like

Since I ran into more issues I tried your approach and have encountered a weird bug that should have nothing to do with it.
When I change the order in which I add the barrel and player to the scene (i.e. in this case I add the player first), the player stops colliding with the floor and falls through.
Is there any way that the order in which objects are added to the scene could have this effect?

Can you show us code, please

if (!Scene::initWithPhysics()) 
  return false;

_physicsWorld->setGravity(Vec2(0.0, -800.0));

Background* background = Background::create();

Barrel* barrel = Barrel::create();
Player* player = Player::create(barrel);

This way it works, but if I swap the last two lines the player falls through the floor.

And this is how the floor is set up. The player’s collision category is 0x01 and the barrel’s is 0x02.


const cocos2d::Size bodySize = cocos2d::Size(getContentSize().width, getContentSize().height * 0.4f);
const cocos2d::Vec2 bodyOffset = cocos2d::Vec2(0.0f, -bodySize.height * 0.75f);
cocos2d::PhysicsBody* floorBody = cocos2d::PhysicsBody::createBox(bodySize, cocos2d::PHYSICSBODY_MATERIAL_DEFAULT, bodyOffset);
// There is only one shape attached.

I haven’t used the physics engine much, especially not for something like this, so I can’t really help with this specific issue at the moment.

Are you certain the barrel and player aren’t interacting in some way with regards to their collision categories?

Add CCLOG statements in setPosition to see what’s happening, and once you have an idea of when it’s happening. You can also add a breakpoint on the setPosition methods in Player, and find out when they are getting called unexpectedly to move the player down. You can then back trace from there to see what is causing the movement.

My player is affected by gravity, so that’s where the movement is coming from, but it should be stopped by the collision with the floor. I’ll still test it with the debugging you proposed to double check.
The player and barrel do not interact with one another but I’ll also double check it just to be sure.
Somehow when I change the order the barrel and the player are added to the scene the collision with the floor fails, but only for the player not for the barrel.

@R101 I tried your way today, but it doesn’t work since my movement is physics based and when I apply forces to the player setPosition is never called. I’m not sure how the sprite’s position is adjusted when I call applyImpule which I use for jumping. I’ll try to figure that out next maybe I can still go for a similar approach.

Are you 100% certain you checked all the different setPosition methods in Sprite and PhsyicsBody?

That’s the easy part, just open CCPhysicsBody.cpp/.h and it’s all there. Put a breakpoint on the PhysicsBody::setPosition methods, and follow it with a debugger to see how the Sprite is moved in the scene.

1 Like

You are correct! I had only overwritten setPosition(Vec2 pos) which isn’t called. Additionally I overwrote setPosition(x, y) and now it works… at least kinda. Unfortunately it doesn’t update nearly often enough so the movement is stuttering. Gotta find another solution I guess.

Which object is stuttering? The player, barrel, or both? Find out why before giving up on it.

Only the barrel. I’ll look into it later. Thanks you for all your help so far :smiley:

It looks like setPosition is called more frequently than the barrel sprite is updated or at least it seems that way. When I set a breakpoint in setPosition it is constantly triggered, but when I run the game the barrel only updates every couple hundreds of milliseconds.

I’m a little confused by that statement. Aren’t you calling Barrel::setPosition from the Player::setPosition? That would mean the barrel should be updating its position just as often as the player is. There is no way it can be updating every couple of hundred milliseconds, because the scene is updating at whatever FPS you have it set to, so if it’s at 60 FPS, the movement on the screen is updating approximately every 16.7ms (1/60).

The situation is probably best shown with a short video. I agree with you. It’s supposed to update regularly, but I added a log whenever the barrels position is updated and it is way more frequent than the positional adjustments you can see in the following clip.
Save the Rum 2021-05-04 (868.6 KB)

Also I just had a bug where the barrel stayed in the air when the position was updating correctly and should have moved it down with the player. So there is something else going on here. The position does not seem to be applied correctly or maybe setPosition and the physics interfere with each other somehow?