EntityX + Box2D


#1

Hi folks,

I’m having a little trouble with EntityX and Box2D - particularly with assigning a fixture’s userData to an Entity.

Here’s the code I have when I create a new entity that has a Body/Fixture:

Entity myEntity = _entities.create();
myEntity.assign<Body>( pos );    //assign a component to the entity

//set up fixture etc
b2FixtureDef * fixtureDef = new b2FixtureDef();
b2CircleShape * circle = new b2CircleShape();
circle->m_radius = 0.15f;
fixtureDef->shape = circle;

....

//here I tell the fixture to point to the 'owner' entity
fixtureDef->userData = &myEntity;

...

The issue is that, when I later use

 Entity * entityA = static_cast<Entity*>(contact->GetFixtureA()->GetUserData());

inside my Contact Listener, the resulting entity pointer counts an ‘invalid’ entity - so I cant do anything with it - EntityX will just crash if I try and check for components, or check it’s id.

As far as I can tell, it’s because the initial “myEntity” object is a temporary object - so I’m actually assigning a pointer to a useless temporary object. I can’t think of anyway to get around this, however. “entities.create()” is just the way that EntityX creates entities.

Any ideas how I can get around this? It’s possible I’m just approaching this the wrong way entirely!


#2

Does Entity have a copy constructor? If so (and it looks like it), you could try this:
Entity *myEntity = new Entity( _entities.create() );

You may have to call myEntity->invalidate() to disassociate it from the EntityManager, as explained here.

Note that I have never used EntityX myself, I only just heard about it.

-Paul


#3

Hi,

I haven’t used EntityX either, so I might be quite wrong about this. But from what I understand, the Entity object is very thin, just capturing the id of a particular object. Instead of storing a pointer to the Entity object, you might be better off storing the id as the userData in the Box2D object. Or even better, store the information regarding the contact in a Component in your Entity. Then your System can query the bunch of entities based on this particular Component.

Let us know how your experiment goes!

Bala.


#4

Thanks for the help guys - I think I (finally) got somewhere with this.

I tried a whole bunch of approaches, but I think the flaw in the system is that Box2D requires a pointer to an object for a fixture’s userData, and EntityX doesn’t seem to have particularly concrete data structure. I couldn’t assign entities or entity IDs, as they both counted as temporary objects. I could assign a pointer to a component - but once it came to accessing the entity at the point of collision, the components .entity() method would point to a null (invalid) entity.

So instead, I added a new a uint64_t field to b2Body which could store the owner entity’s ID itself. Then I could just look up the entity via ID at the point of collision, and send events out from there.


#5

Box2D does not touch userData at all. Its use and interpretation is completely upto us.
I was under the impression that you could extract the entitys id as uint64_t through something like id().id(). But on the whole, your solution seems to be what I had in my mind. Thank you for updating us!


#6

That’s what I tried! Unfortunately if I try
fixture.userData = &entity.id().id();
I get the error
cannot take the address of an rvalue of type 'uint64_t'

However…
fixture.userData = new uint64_t(entity.id().id());
does work. I wonder if this is a better option? Or could this result in memory leaks?


#7

Thats because you are trying to use the address of a value ( not a variable ).

Originally, I was thought you could store the value itself in userData, not a pointer. But now I realize that it is possible only if you are using a 64 bit build, since the id is of type uint64_t. For a 32 bit build, size of void * would be 32 bits, and a uint64_t would not fit in there.

While you could create a pointer to a uint64_t and store it in userData, you would have to delete it yourself, or you will leak memory. But this doesn’t sound like the ECS way of doing things. You might be better off sticking the id ( the uint64_t value ) in a collision Component which you can process from your System. Please check out the example here.

Bala.