Skip to content

Fix ignored entity pivot#294

Draft
Koopa1018 wants to merge 39 commits into
Trouv:mainfrom
Koopa1018:fix/entity-pivot
Draft

Fix ignored entity pivot#294
Koopa1018 wants to merge 39 commits into
Trouv:mainfrom
Koopa1018:fix/entity-pivot

Conversation

@Koopa1018

Copy link
Copy Markdown
Contributor

This PR modifies calculate_transform_from_entity_instance to use plain ldtk_pixel_coords_to_translation rather than the pivoted version. It also updates the function's test suite to reflect the different results it now gives.

Theoretically this PR will fix #290, but I've never worked with this codebase, so I have no idea if what I've done here is enough to actually fix the bug, nor do I know what else may need updating. (I know the enemies in the Platformer example show up displaced now.)

This is the basic ingredient needed to fix Trouv#290. There's a lot of things which will potentially break if this is done in isolation, so there's a lot more work ahead.
Comment it up, and also disambiguate use of the word "pivot."

It always helps to do this when I'm first trying to understand a codebase.
Feels kind of silly testing behavior that's just "match the coordinates and flip the Y," but then again, the scale is worth testing.

@Trouv Trouv left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

nor do I know what else may need updating

No worries. The examples definitely should be updated to match their previous behavior.

Also, since this is a breaking change, a new section in the migration guide is in order (at book/src/how-to-guides/migration-guides/migrate-from-0.8-to-0.9.md)

Comment thread src/utils.rs Outdated
Match usually implies multiple values need to be handled explicitly. When it's like this--everything or nothing--an if let expresses the intent better.
Now *this* is how the pivot should be used! Not placing the entity--placing the sprite *relative* to the entity :)
The whole point of this one is that it doesn't use the code I fixed. Have to apply the fix manually, then. (Confirmed working now.)
@Koopa1018

Copy link
Copy Markdown
Contributor Author

Arright, I've done what I can with the examples. Most appear fine, and traitless was easy enough to fix, but I have no idea where to even look in the platformer example....
My only leads are that when the example begins running, the skulls slowly rise into the air as they move, until they reach this height:
image
and that the boss (who does not move) has a lowered hitbox:
image

Migration guide is going smoothly. Not totally sure how to demonstrate the changed translation/pivot behavior in a code example, though....

Also: I notice the CI format check failed. I've never contributed to a project with CI, so I'm not sure what that means, even after checking the details. Are these formatting issues that I need to fix?

To ease migration process, a function which produces the same value as translation() did before the anchor/pivot fix.
This is a breaking change, and thus must be noted in the 0.8-0.9 migration guide. Draft because I can't quite figure out what to put in the first example.
@Trouv

Trouv commented Jan 20, 2024

Copy link
Copy Markdown
Owner

Also: I notice the CI format check failed. I've never contributed to a project with CI, so I'm not sure what that means, even after checking the details. Are these formatting issues that I need to fix?

Yeah it'll need to be fixed before this is merged. For the format check in particular, it's just a matter of running rustfmt: https://github.com/rust-lang/rustfmt

Though it's something small enough that I'd do it myself if that was the only issue preventing merge.

BTW, some unsolicited advice: I recommend setting up your editor to automatically run rustfmt on save. It's so handy writing a ton of dirty code then hitting save and watching it prettify before your eyes

I'm looking at what it did so I can update my coding style. I'm seeing:
- Rust Analyzer's in-line type display misled me about how long line 88 and fn entity_center's definition were. They did not need to be shortened.
- Indents -> spaces. (Imma let rustfmt handle that one 😅)
- Ah, so *that's* how you split long if-lets up...
- Do indent end-of-line comments to the same point. (In GDScript that's a no-no, so I'm used to avoiding that....)
@Koopa1018

Copy link
Copy Markdown
Contributor Author

Can I get some help with the platformer example? It seems like to get the colliders to spawn right, it would require wider-reaching redesigns than I signed on for (that or I'm just totally missing a property that would fix everything...).

@Trouv Trouv left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for your work on this. The scope has definitely increased a lot, sorry about that.

One of the comments in this review does address a particular strategy for updating the platformer example. However, it's a lot of work, so I don't expect you to do it necessarily. I can happily pick up wherever you leave off.

Comment thread examples/traitless.rs Outdated
Comment thread book/src/how-to-guides/migration-guides/migrate-from-0.8-to-0.9.md Outdated
Comment thread book/src/how-to-guides/migration-guides/migrate-from-0.8-to-0.9.md Outdated
Comment thread book/src/how-to-guides/migration-guides/migrate-from-0.8-to-0.9.md Outdated
Comment thread book/src/how-to-guides/migration-guides/migrate-from-0.8-to-0.9.md
Comment thread examples/traitless.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Koopa1018 and others added 16 commits January 30, 2024 03:40
Hate it when I miss this.
Co-authored-by: Trevor Lovell <trevorlovelldesign@gmail.com>
The anchor calculation depends on using the macro, as Trouv pointed out. And looking at my work later, I figured out how to make the center function clearer.

Co-Authored-By: Trevor Lovell <trevorlovelldesign@gmail.com>
Because I can't imagine doing the conversions by hand every time would be at all easy on my sanity.

Replace both existing instances of this code with calls to the new function, too, because code sharing is tight.

Co-Authored-By: Trevor Lovell <trevorlovelldesign@gmail.com>
Doesn't do anyone any good unless they know it's there (or can guess, which, you never know...).
Co-Authored-By: Trevor Lovell <trevorlovelldesign@gmail.com>
Co-authored-by: Trevor Lovell <trevorlovelldesign@gmail.com>
For now, I'm putting the file in repo with the other image, knowing that it can be moved later.

The image itself needs to be updated for clarity--I tested on my brother, and he was so distracted by the bright shiny object that he totally missed the tiny pivot dot.
Completely overlooked this function! Yes, these should probably also respect the pivot point.

Understanding the macro it's used in took a bit, but thankfully the change wasn't too hard to apply once I did.
More indent->space fixes. I should probably change that in VSCode.
Asserts that
1. each axis is remapped from (0 - 1) to (-0.5 - 0.5) as is required for Bevy
2. Y is properly inverted.
uh...ok? Clearing out unneeded whitespace from a blank line....
Glad to find there actually was precedent for this, found in the book's tutorials folder.
Less contrasting entity graphic, bigger and more contrasting position reticule.
Missed 0.9, so it's gonna have to be in 0.10.
@Koopa1018 Koopa1018 marked this pull request as ready for review July 21, 2024 21:59
THIS. EXAMPLE. IS. A MESS! How can anyone learn from a haphazard jumble like this? It's like grabbing an armful of notes from a physicist's desk and calling it a textbook....

Anyway. I need to be able to sort through this, so I just decided to go ape. No logic was changed--it's just organized intuitively now.
@Koopa1018 Koopa1018 marked this pull request as draft July 22, 2024 00:02
@Koopa1018

Copy link
Copy Markdown
Contributor Author

Arright, so I reorganized the platformer example so I could make heads or tails of it. Now there's a concrete problem in front of me: I'm not sure how to spawn colliders as children within the LdtkEntity framework. If I had an Entity ref, no problem, but...

Think I'm gonna have to leave this as draft until #177 finally hits. Until then, might as well submit the reorganized platformer example as a separate PR so it doesn't become hell to merge down the road....

Trouv pushed a commit that referenced this pull request Aug 12, 2024
The components and systems in the platformer example are confusingly
sorted: components are stored in a separate file from the systems that
use them, and within those files the components & systems are jumbled
chaotically with little rhyme or reason. In a crate example this
complex, this organization scheme makes understanding the code a real
challenge (which is a major reason I couldn't update this example in
#294).

This PR reorganizes the platformer example into modules sorted roughly
by "part of the game" or "game system" (camera system, climbing,
inventory, etc.). Each module contains both the components and ECS
systems specific to that part, to present a complete picture of what the
game system does and how it works.

No game logic has been changed. This PR focuses purely on code quality.
@Trouv Trouv added A-Spawning-LDtk Area: regarding the systems around spawning LDtk worlds/levels/layers, etc. D-Modest Difficulty: new features, refactors, and challenging bug fixes. C-Bug Category: unexpected or incorrect behavior. S-Needs-Review Status: this PR needs reviewer attention to move forward. labels Nov 27, 2025
@Trouv

Trouv commented Dec 25, 2025

Copy link
Copy Markdown
Owner

Thanks for all your hard work on this. I have a lot more time for this project lately and I'm still interested in getting this PR in. Sorry for the wait.

It could use a rebase, your platformer refactor has gotten in in the meantime, and some of the migration guide stuff will need to refer to newer versions of the crate (0.13 and 0.14 instead of 0.9 and 0.10).

Now there's a concrete problem in front of me: I'm not sure how to spawn colliders as children within the LdtkEntity framework. If I had an Entity ref, no problem, but...

I'm not 100% sure what this has to do with this PR, is it a blocker? normally for things like this you do have to do post-processing instead of just LdtkEntity + macros

@Trouv Trouv added S-Waiting-On-Author Status: the author needs to make changes to this PR before it can be approved. and removed S-Needs-Review Status: this PR needs reviewer attention to move forward. labels Dec 25, 2025
Koopa1018 added 4 commits May 30, 2026 23:04
with some rather intense custom merging 😖 Some spots need further editing to reimplement their ldtk_pivot_to_anchor calls...some of those edits rather structural in nature. Would rather finish the merge first before beginning on those....
One down...two breaking API changes to go.
In such a big merge, it's no wonder things were lost.
Feels like a stopgap fix, an ugly hack that should work better.... Would love to write a test suite for this, but I don't know where I'd put that.
@Koopa1018

Koopa1018 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Hey, sorry I took so long to get back to this!

I've been out of the Bevy ecosystem for a while. The new Sprite architecture (Anchor being a requirement of Sprite, rather than a field) threw me for a loop, since the anchor can no longer be assigned in Sprite's bundle_entity function...

I'm inclined to think the simplest way to make this work would be with a post-process function (that overwrites any freshly Added<Anchor>), but that also has the drawback of overwriting user-defined Anchors. I could design it to only overwrite non-default() Anchors, but that'd still overwrite any Anchor which is equal to default(). That'd be a weird, counterintuitive edge case; definitely not ideal to put that burden on users.
(EDIT: That'd also be undesirable for traitless spawning, wouldn't it....)

Branch currently has the post-process system, but I'd be happy to look into the derive macro and see what I can do to make anchors even more automatic.

(I also want to write a test case to verify that this postpro system actually works, but integration testing is something I'm not super comfortable with yet.)


W.r.t. spawning colliders as children. I believe the problem was that Rapier colliders couldn't be offset to align with pivot-adjusted sprites except by spawning them as children. I'll try post-processing systems.

Holy heck this never got removed. I split its functionality out into `ground_detection` and `colliders`, but...I guess the file itself never got removed?
@Koopa1018

Copy link
Copy Markdown
Contributor Author

Dug into the macro. Looks like the best I can do is add an #[anchor] field attribute like the #[sprite] one. Would've liked to add a warning if a sprite has no anchor, but alas, manual compiler warnings are a nightly-only feature....

I suppose that might be for the best. If users have to manually tag their anchor fields, this whole pivot feature is effectively opt-in, which makes backwards compatibility lots easier.

Thinking of updating the macro and removing the post-process function. Thoughts?

@Trouv

Trouv commented Jun 3, 2026

Copy link
Copy Markdown
Owner

That's an interesting problem. There are some design decisions in this plugin that really need some rethinking since the introduction of required components.

I did think about your idea. If we were to do something like that I'd suggest a smaller change, adding some

pub fn ldtk_entity_anchor(entity_instance: &EntityInstance) -> Anchor {
    todo!()
}

to be used like

#[derive(Bundle, LdtkEntity)]
struct MyBundle {
    #[sprite_sheet]
    sprite: Spite,
    #[with(ldtk_entity_anchor)]
    anchor: Anchor,
}

However, I realized technically Transforms have a similar problem. You don't include them on the bundle but the system that spawns your entity adds it anyway, and assumes that you would want the transform to just be its position in the LDtk world (with some nasty adjustments). It could be made to make a similar assumption and add an Anchor. I suppose ideally it would only add the anchor if the entity already had a sprite component, but that might not be possible to tell in the position it's in. It would overwrite user-defined anchors, just like user-defined transforms, but that's not immediately a dealbreaker in my mind. Lmk your thoughts on this, I'll think about it some more too.

@Koopa1018

Copy link
Copy Markdown
Contributor Author

Hmm, I hadn't considered using the existing annotations 😆

Looks like if we insert the anchor during spawn_level but before the call to evaluate(...), we leave users the ability to overwrite anchors by defining them in bundle_entity. Spriteless entities will still get an Anchor, which is ugly, but probably harmless? This is the method I'd prefer--it's automatic until the user needs to think about it, then steps out of their way when they need to override it.

Wondering if it might be worth it to make a custom EntityCommand to remove unneeded Anchor components...a post-process system would work, but it's difficult to guarantee that our post-process system would run Entirely Before or Entirely After user post-process systems.

If we don't want to do this, then it'd probably be easiest for users if we just impl LdtkEntity for Anchor and have them flag it #[ldtk_entity]:

#[derive(Bundle, LdtkEntity)]
struct MyBundle {
    #[sprite_sheet]
    sprite: Spite,
    #[ldtk_entity]
    anchor: Anchor,
}

...rather than making them remember which exact function to plug into #[with(...)].

Actually, I may end up doing impl LdtkEntity for Anchor anyway, just in case a user wants to be really explicit for some reason.

Koopa1018 added 2 commits June 4, 2026 21:51
I don't think this post-process system is It. Even if it is, traitless.rs is about stepping *outside* of this system. Actually, traitless.rs is what clued me in that this method might be wrong....
Forgot to remove the entity_instance reference from this invocation when I removed it from the signature.
@Trouv

Trouv commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Looks like if we insert the anchor during spawn_level but before the call to evaluate(...), we leave users the ability to overwrite anchors by defining them in bundle_entity.

Great point. I'm okay with this, and actually, it may make sense in the future to do that for Transforms as well. After all, the main reason I did transforms post-evaluate instead of pre-evaluate in the first place was because the old SpriteSheetBundle had a Transform on it. But now that it's a mere required component instead of part of the bundle added in evaluate, it too could be added pre-evaluate, and the plugin becomes slightly more flexible.

There may be some places in documentation where the behavior of Transforms is explained, may be worth explaining how Anchor also has similar behavior (but pre-evaluate). No need to make the change for Transform in this PR though, I'd prefer that as a separate small PR.

Koopa1018 added 3 commits June 5, 2026 22:14
There we go! Adds anchor automatically, without making the user opt in, but lets the user override the Anchor if wanted.

Update LdtkEntity::bundle_entity documentation to explain how Anchor is added, and that it can be overridden. Trouv says we may want to move Transform to being added pre-bundle as well so it can be overridden, but definitely not in this PR.
Well that's infuriating. Looks like merging from main undid most of my changes to this file 😖 Let's get that back in order....

The examples that existed when I started (platformer notwithstanding) are all in working order now. I don't remember tile_based_game or collectathon...may need to update those ones.
The pivot code invalidates this example's assumption that the entity position is at the center of the grid.

I could fix this by changing the pivots in the LDtk project...but it seems better to demonstrate how to adjust it when the pivot is wrong. I want to fix it properly by making a pivot-aware grid-position function, but that's a fair bit more complex than I can handle tonight...might even be impossible to handle in a generally-correct way.

Update code references in add-gameplay-to-your-project.md as well. Need to update the textual content of this document to address the changed behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Spawning-LDtk Area: regarding the systems around spawning LDtk worlds/levels/layers, etc. C-Bug Category: unexpected or incorrect behavior. D-Modest Difficulty: new features, refactors, and challenging bug fixes. S-Waiting-On-Author Status: the author needs to make changes to this PR before it can be approved.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Loaded entity position ignores pivot

2 participants