The physics contact begin callback's contact shape is the same object with the objects were created several times

The physics contact begin callback's contact shape is the same object with the objects were created several times
0

Hi, the physics contact begin callback’s contact shape is the same object with the objects were created several times.

If you want to see what’s happening, look at the gif at the end of this topic

Missile class will get numbered name when they create.

class Missile : public cocos2d:Node
{
    ....
}

Missile::Missile()
{
    static int num;
    log("missile Num %d", num);
    autorelease();
    setName("Missile" + std::to_string(num++));
}

It sets bitmask. It also contacts map, zombie, zombie_pool.

int bitmask = GameObjectBitmask::MAP | 
    GameObjectBitmask::ZOMBIE |
    GameObjectBitmask::ZOMBIE_POOL;
physicsBody->setCategoryBitmask(GameObjectBitmask::MISSILE);
physicsBody->setCollisionBitmask(bitmask);
physicsBody->setContactTestBitmask(bitmask);

Every missile has a contact event. This event belongs to the missile. When the missile hits the map, it generates smoke effect and destroys self by removing from the parent.

void Missile::CreateContactEvent()
{
    auto contactListener = EventListenerPhysicsContact::create();
    contactListener->onContactBegin = [this](PhysicsContact & contact)->bool 
    {
        auto nodeA{ contact.getShapeA()->getBody()->getNode() };
        auto nodeB{ contact.getShapeB()->getBody()->getNode() };

        log("This : %s", this->getName().c_str());
        log("nodeA : %s", nodeA->getName().c_str());
        log("nodeB : %s", nodeB->getName().c_str());
        log("Missile Contact Position : %f, %f", this->getPosition().x, this->getPosition().y);

        SmokeEffect* smokeEffect{ new SmokeEffect{} };
        smokeEffect->CreateSprite();
        smokeEffect->CreateAnimation();
        smokeEffect->setPosition(this->getPosition());

        this->getParent()->addChild(smokeEffect);
        this->removeFromParent();
        return true;
    };
    _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
}

I added mouse down event to fire the missile in missile’s parent node. It works well.

void GameMode::InitMouseEvent()
{
    auto ms_listener = EventListenerMouse::create();
    ms_listener->onMouseDown = [this](EventMouse::Event* event)
    {
        EventMouse* em = dynamic_cast<EventMouse*>(event);
        this->FireTheMissile();
    };
    ...
}

Fire the missile function. It allocates new Missile and creates sprite, physics body, contactevent(above code), and set direction, add it to parent. So it will move automatically.

void GameMode::FireTheMissile()
{
    Missile* missile{ new Missile{} };
    missile->CreateMissileSprite("BlueMissile.png");
    missile->CreatePhysicsBody("BlueMissile");
    missile->CreateContactEvent();
    missile->setPosition(tank->getPosition());
    missile->SetDirection(tank->GetAimDirection());
    missile->SetVelocity(500.0f);
    addChild(missile);
}

So when I mouse-clicked several times, the missiles will be created and move automatically.

When the first missile contacts edge boxed map, it generates the smoke and will be destroyed. But it also generates other missiles’ smokes which are not yet contacted edge box. And also destorys that time.

I have tried to log the missile number, contact shapes’ name, and position.

This : Missile4
nodeA : BattleMap
nodeB : Missile0
Missile Contact Position : 1191.531494, 83.866493

This : Missile3
nodeA : BattleMap
nodeB : Missile0
Missile Contact Position : 1283.183838, 82.511803

This : Missile2
nodeA : BattleMap
nodeB : Missile0
Missile Contact Position : 1358.172119, 81.403419

This : Missile1
nodeA : BattleMap
nodeB : Missile0
Missile Contact Position : 1441.492554, 80.171883

This : Missile0
nodeA : BattleMap
nodeB : Missile0
Missile Contact Position : 1516.480957, 79.063499

The contact shape’s name is not correct. I cloned contact listener, but it also failed.

Do I miss some concept of physics event?

gif for missile contacting map.

1

can you post your whole missle class please. I have had this same thing happen to me when I was new to physics. I had some scope and creation issues.

Missile.cpp (2.9 KB) Missile.h (756 Bytes)

here’s a missile header and cpp.

Do you store your missles, etc in a std::vector or any other structure?

No, i dont. I just allocate it and add it to parent. Also autorelease

Does onContactBegin() get called for all missiles as soon as one missile hits the boundary? The reason I ask is because you’re using “this” pointer, and not doing any actual checking on the 2 contact objects, so if the method is called for all any time a contact event occurs, it’ll play the smoke and remove each and every missile from the scene.

Question: Why didn’t you just create one onContactBegin() in the scene or parent layer of all objects? I think the issue is that you’re creating the onContactBegin() for each and every missile, which is perhaps not how it’s supposed to work. I just checked my own code, and I’m not having this specific issue, the difference being that onContactBegin() is created only once, and linked to my scene, and not to the actual objects in it.

In the cpp_tests project, file PhysicsTest.cpp, you can see an example of how the onContactBegin() is created, linked to the scene/layer.

In onContactBegin(), you can get the identity of the object via things like getCategoryBitmask(), getTag(), or even getGroup() (if you use groups) to check which object (a or b) is your missile, and then handle it from there. You’d probably want to check what the second object is too (like, is it a wall, a zombie, etc).

1 Like

Thank, very helpful.

Yes, it gets called all missiles’ callback.

I modified what you say, removing this, using contact shapes.

It works a little bit. If the missile’s number goes above 2 before hits boundary, one of the shape, missile, is nullptr and get an exception thrown. Also, if I fire the missile 2 times and they hit the boundary and I fire again and they hit, I get nullptr again.

I create the onContactBegin() in missile because every missile has only missile event. So I could care about only the missile contact event, not zombies, tanks, etc’s event. If I create the event in Scene or Layer, I should check the shape, which is not needed to be there, is a missile, zombies, boundary. So I created every event on every missile.

From a design perspective, consider that a projectile does not have knowledge of what kind of object it will be interacting with. The boundary doesn’t know about a missile, and a missile doesn’t know about the boundary, correct?

If you think about it that way, then you’ll realise that the interaction takes place externally to those objects. Something has to handle it, and it may as well be the scene where those objects reside. If the scene detects that the objects collided, then it can just call “missile->Explode();” for example, and in the Explode() method you show the smoke and remove the missile from the scene.

Imagine that there is a wall that crumbles when a projectile hits it, but the projectile does not get destroyed, it just passes through that wall. You can add that easily in the onContactEvent() of the scene, so you don’t end up calling “missile->Explode();”, you simply call “wall->Crumble();”. It gives you flexibility in how you handle your object interactions. You can handle all of them in that scene::onContactEvent().

1 Like

Thanks a lot. I haven’t thought about that way.

I moved the contact event to layer and it really works very well. I really appreciate.

1 Like