alecthomas / entityx

EntityX - A fast, type-safe C++ Entity-Component system
MIT License
2.21k stars 295 forks source link

Passing an entity as a pointer (Question) #238

Open ghost opened 4 years ago

ghost commented 4 years ago

Hi @alecthomas , great library!

I have a scenario in one of my projects, where I need to pass an entity or a component to the physics engine (bullet) as a pointer (void*).

When I am trying to receive the pointer back, if I cast it to an entity for example, I can't access any of the components because It gives me some error like the id is invalid or if I am trying to use the id it tells me that the index is smaller than the component mask (or something close to this).

Is there any other way to pass an entity as a pointer and access it in some other part of the code without being invalid?

Thanks.

alecthomas commented 4 years ago

Can you show me the code? void* will almost certainly not work directly, if that's what you're suggesting.

ghost commented 4 years ago

Hi, I don't know if you know the bullet physics library but in it you can attach to an collision object something called user pointer which is of type void*. This scenario helps you when you have a collision and you want to retrieve that object and do something with it, of course you will have to cast it to your object type.

How I can send a new created entity (ex: auto entity = _ecs.entities.create(); ) to that pointer, without being invalid when I want to retrieve it back.

Thanks.

alecthomas commented 4 years ago

Ah, okay. You'll probably want to use the numeric id of the entity instead, as Entity objects are transient values.

alecthomas commented 4 years ago

Also if that still doesn't help, first rule of bug reports is to provide as much information as possible: sample code, error messages, etc.

ghost commented 4 years ago

Ok, thanks @alecthomas I will come with the back with the code and the messages.

ghost commented 4 years ago

Hi @alecthomas, I got an working solution:

I send the id to the physics in this way: auto body = new btRigidBody(info); body->setUserIndex(entity.id().id()); // this is just an int

And I get it in this way: auto id = _ecs.entities.create_id(hit.m_collisionObject->getUserIndex()); auto entity = _ecs.entities.get(id);

I did not know about that create_id method which is very helpful.

Can I do a pull request with an operator uint64_t for the Entity::Id? it will be nice to avoid this entity.id().id() and use just entity.id() instead.

Thanks.

makiolo commented 4 years ago

I am not sure, is a question: Why you use "uint64_t" instead "Entity::Id" ? Both looks copyable, and second case your call is more readable: "entity.id()" also you don't need use "create_id", you can use "get" directly.

morphogencc commented 3 years ago

Sorry to hop onto this thread, but I think my issue is similar -- I'm using NVIDIA's PhysX as my physics engine. The engine leaves rigid body actors with a field void* userData that can be used to connect PhysX engine objects with user application objects. documentation is here.

Along the same lines as @iamAdrianIusca , I'm trying to link an entity to this void* pointer. Bullet supports setUserIndex which stores an int, so I think an entity.id() makes sense -- but is there any way that I can store a pointer that links back to an entity as well? This would be the same as bullet's setUserPointer function.

It seems like most of EntityX's types are not pointers, but are essentially custom handles, or static IDs that I can't link a pointer to.

alecthomas commented 3 years ago

That is 100% correct. Assuming your machine word size is 64-bit you could store the Entity ID in the void* directly.

morphogencc commented 3 years ago

Ah, got it!

So I should store the integer ID number into the pointer via:

physx_object->userData = (void*)(entity.id().id());

It can't be dereferenced like a true pointer, but can be brought back with:

auto entity = _ecs.entities.get((uint64_t)(physx_object->userData));

thanks!

makiolo commented 3 years ago

Nice morphogencc, I do something different (more complex maybe), and works with 32 bits.

struct IntrospectionComponent : public entityx::Component<IntrospectionComponent>
{
    std::string type;
    entityx::Entity::Id id;
};

Is a component for know, who am I ? (introspection).

In your "PhysX"->userData you can write something like this:

    void set_userdata(PhysXSomeone& node, IntrospectionComponent& introspection)
    {
        node->userData = (void*)&introspection;
    }

You need a system for fill introspection values (only one time, I use PhysicsDescription like "flag component"):

    es.each<Transform, PhysicsDescription, IntrospectionComponent>([&](entityx::Entity entity, Transform& transform, PhysicsDescription& physics_desc, IntrospectionComponent& introspection)
    {
        introspection.id = entity.id();
        bool is_projectile = entity.has_component<ProjectilComponent>();
        if (is_projectile)
        {
            introspection.type = "projectil";
        }
        else  // is enemy
        {
            introspection.type = "insect";
        }

        entity.assign<plague::PhysicsComponent>(.......);  // create physics using "PhysicsDescription"
        entity.component<PhysicsDescription>().remove();
    });

Yo can recover entity:

        auto obj = static_cast<plague::IntrospectionComponent*>(node_physx->userData);
        auto entity = entities.get(obj->id);

And Finally, when I have collisions, for example, now I can read your userData:

        auto objA = static_cast<plague::IntrospectionComponent*>(physx_node->userData);
        auto objB = static_cast<plague::IntrospectionComponent*>(physx_node->userData);

        if (objA->type == "projectil" && objB->type == "insect")
        {
            // do something
        }