Trouv / bevy_ecs_ldtk

ECS-friendly ldtk plugin for bevy, leveraging bevy_ecs_tilemap
Other
627 stars 73 forks source link

Transform is offset incorrectly #292

Closed y0Phoenix closed 5 months ago

y0Phoenix commented 5 months ago

Background

I'm making a 2d Platformer Metroidvania game using LDtk to design the game world.

I'm using bevy 0.10.1 bevy_ecs_ldtk 0.7.0 and LDtk 1.4.1

There are multiple levels like this Screenshot 2024-01-14 at 11 12 42 AM

I have a basic enemy implementation where the AI will patrol a certain range without hitting a wall or walking off the edge of a platform.

The problem is when there is a level placed below the level with the AI in it, the AI's Transform is different than its GlobalTransform. This broke my AI implementation which reads from the Transform component. Screenshot 2024-01-14 at 11 20 26 AM

This only happens when levels are below one another. So levels that only have ones above don't have the same issues.

This used to happen to the player also until I added the Worldly component to it.

If I removed the level below the current one, the problem would go away! Screenshot 2024-01-14 at 11 24 38 AM

Expected Behavior

I would expect the Transform to not be offset incorrectly based on neighboring levels' positions in LDtk and for internal functionality to not break from a simple cosmetic change.

Actual Behavior

Entities that are children of levels who have neighboring ones strictly with a lower y-position have Transforms which are invalid.

Workarounds

Read from the GlobalTransform instead of the invalid Transform.

Root problems

My code might have a problem with the way it loads the entities from LDtk however, I'm having a hard time finding anything that would affect the Transform when I compare my code with the platformer example.

There may be a problem with LDtk itself and how it saves entity positions into the .ldtk file.

A problem with the internals of bevy_ecs_ldtk. I haven't dived into the source code of this library to figure out a root problem with translating LDtk to in-game position however, this is a possibility also.

Trouv commented 5 months ago

Thanks for the detailed issue.

The problem is when there is a level placed below the level with the AI in it, the AI's Transform is different than its GlobalTransform. This broke my AI implementation which reads from the Transform component.

If you have LevelSpawnBehavior::UseWorldTranslation enabled in LdtkSettings, the levels will be spawned with translations based on their location in the LDtk world. Then, the transforms naturally propagate and the GlobalTransforms of the level's children are transformed.

The behavior you're describing sounds sort of like a mix between GlobalTransform and Transforms working as intended, and a bug that has been fixed in bevy_ecs_ldtk version 0.8.0 (#207). You will necessarily observe entities inside a level have GlobalTransforms and Transforms differing w/ LevelSpawnBehavior::UseWorldTranslation enabled, since the level's translation most likely won't be 0. However, The fact that you're observing different values based on whether or not there's a level below the level you're interested in (presumably without moving the level around in the world in any way) sounds to me like it could be related to the bug fixed in #207.

If you upgrade to version 0.8, you may see the difference in transform between stacked/non-stacked levels go away, but it sounds to me like your AI logic might need to use GlobalTransform regardless. Especially if the AI logic cares about the worldly player's transform too. Its worldly-ness dictates that its transform is not relative to the level while the enemy's transform is.

y0Phoenix commented 5 months ago

Thanks for the thorough response! It's always a pleasure to see maintainers who care.

If you have LevelSpawnBehavior::UseWorldTranslation enabled in LdtkSettings, the levels will be spawned with translations based on their location in the LDtk world. Then, the transforms naturally propagate and the GlobalTransforms of the level's children are transformed.

I am using this feature because of the way I want my map designed.

The behavior you're describing sounds sort of like a mix between GlobalTransform and Transforms working as intended, and a bug that has been fixed in bevy_ecs_ldtk version 0.8.0 (#207).

So the Transform is relative to the level for children and the GlobalTransform is relative to the world? If so, that does make sense. I just wasn't aware that was the intended behavior.

That's great it's been fixed. I would love to upgrade but I'm using kayak_ui which doesn't currently have a bevy 0.11.x release.

but it sounds to me like your AI logic might need to use GlobalTransform regardless. Especially if the AI logic cares about the worldly player's transform too. Its worldly-ness dictates that its transform is not relative to the level while the enemy's transform is.

I sort of realized this was the case. I changed my logic around to read from the GlobalTransform instead and everything's working great.