Trouv / bevy_ecs_ldtk

ECS-friendly ldtk plugin for bevy, leveraging bevy_ecs_tilemap
Other
682 stars 78 forks source link

Example how to get entity object from FieldValue::EntityRef #165

Closed Alekssasho closed 1 year ago

Alekssasho commented 1 year ago

How is the supposed way to get an Entity Bevy id from EntityRef loaded from LDtk file ? I have linked EntityInstances and I am not sure how to link them after loading into the ecs of bevy ?

Trouv commented 1 year ago

Currently there's no convenience features built in for entity references. I want to improve it eventually: #70.

What you have to do now is match them up yourself. The plugin does provide all the details you need to do so. LDtk entities can be spawned in bevy with the EntityInstance component, which stores the metadata of that LDtk entity, including its fields. The EntityRef will be the value of one of these fields, and will contain the entity_iid of the LDtk Entity it's pointing to. From there it's just a matter of figuring out which of the entities spawned by the plugin is associated with that entity_iid, information also contained in the EntityInstance component.

There's lots of ways to implement this. Off the top of my head, you could do something like this:

#[derive(Component, Deref, DerefMut)]
struct UnresolvedPointer(String);

impl From<EntityInstance> for UnresolvedPointer {
    fn from(value: EntityInstance) -> Self {
        if let FieldValue::EntityRef(Some(iid)) = value
            .field_instances
            .iter()
            .find(|f| f.identifier == "MyEntityRefFieldIdentifier".to_string())
            .unwrap()
            .value
            .clone()
        {
            UnresolvedPointer(iid.entity_iid)
        } else {
            panic!();
        }
    }
}

#[derive(Component)]
struct Pointer(Entity);

#[derive(Bundle, LdtkEntity)]
struct PointerBundle {
    #[from_entity_instance]
    pointer: UnresolvedPointer,
    // other components..
}

#[derive(Component, Deref, DerefMut)]
struct EntityIid(String);

impl From<EntityInstance> for EntityIid {
    fn from(value: EntityInstance) -> Self {
        EntityIid(value.iid)
    }
}

#[derive(Bundle, LdtkEntity)]
struct PointedAtBundle {
    #[from_entity_instance]
    entity_iid: EntityIid,
    // other components..
}

fn resolve_pointers_system(
    mut commands: Commands,
    pointer_query: Query<&UnresolvedPointer>,
    pointed_at_query: Query<(Entity, &EntityIid)>,
) {
    for pointer in &pointer_query {
        if let Some((pointed_at_entity, _)) =
            pointed_at_query.iter().find(|(_, id)| id.0 == pointer.0)
        {
            commands
                .entity(pointed_at_entity)
                .remove::<UnresolvedPointer>()
                .insert(Pointer(pointed_at));
        }
    }
}
Alekssasho commented 1 year ago

Thanks a lot for the example. I think it would be a good idea to add it as an example file in the repo.

adtennant commented 1 year ago

@Trouv The example above only works if the level containing the other entity is loaded.

I've come up with something for when the other level isn't loaded yet, e.g. for a warp destination, but it feels messy.

I'm using the FieldInstanceEntityReference to find the LayerInstance and then EntityInstance in the LdtkAsset and finally using calculate_transform_from_entity_instance to get the Transform. Is there a better way of doing this?

Trouv commented 1 year ago

Is there a better way of doing this?

Not currently no, if it's not spawned then doing it through the asset is your only option. I have been considering lately creating a resource that maps ldtk entity iids to their indices in the asset (level index, layer index, then entity index) so users can easily get level and layer data for a given entity, and that would help in your use case too.