Bevy Developer Network
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Transform Overhaul This design doc follows [a previous attempt to create a design doc for this work](https://hackmd.io/MGeT-wYJSPSpBM7XGn7qug), changing the structure to provide better context and make the work more actionable. ## Status Quo A `Transform` stores the position, rotation and scale of a single game object. This information is "relative" to any parent that the object might have: these values are propagated down the entity hierarchy and combined into a final `GlobalTransform`, capturing the absolute position, rotation and scale of the object within the game world. Historically, Bevy used `Transform` + `GlobalTransform` everywhere: for 3D, 2D and UI. As a result `Transform` stores a `Vec3` for position, a `Vec3` for scale, and a `Quat` for rotation, while `GlobalTransform` uses a `Affine3A` to represent the combined transform matrix and speed up transform propagation. 2D is handled by rendering objects flat onto the XY plane, and then using an orthographic projection camera pointing at this plane. UI is handled similarly, although the coordinate system is in screenspace (to keep the UI in place while you move the camera) and y-flipped to align with standard top-left origin conventions for UI. In [#16615](https://github.com/bevyengine/bevy/pull/16615), UI was converted to use dedicated `UiTransform` and `UiGlobalTransform` types. ## Problems Bevy has a number of challenging problems to solve when it comes to representing positions! While we should not attempt to solve all of them at once, it is helpful to tackle closely entangled problems in a single design pass. A brief summary of the open thorny problems with transforms follows, along with our consensus for which problems should be tackled in this working group vs put off for now. The problems with `Transform` in 2D are the most pressing, and are laid out in [#7876](https://github.com/bevyengine/bevy/issues/7876) and [#2548](https://github.com/bevyengine/bevy/issues/2548). ### Problem: 2D rotations should not require quaternions Rotating an object in 2D should be easy. In Bevy, it's not. As [demonstrated by our 2D rotation example](https://github.com/bevyengine/bevy/blob/main/examples/2d/rotation.rs), even simple tasks require understanding what a quaternion is and specifying which axis we're rotating around. If users gets this wrong, their objects are stretched, squashed or even disappear in surprising ways. This is also inefficient: a `Quat` requires four f32, when only one would suffice for the vast majority of sprites. ## Problem: 2D objects should not have a z-scale Because we are modelling our 2D game objects with `Transform`, we can change their Z-axis scale as well! This seemingly has no effect, unless you set their z-scale to zero, in which case the object suddently disappears. However, as [#4149](https://github.com/bevyengine/bevy/issues/4149) complains, this actually has a subtle and surprising effect on the relative ordering of sprites by interacting with z-based sorting. ## Problem: Z-based layering is insufficient Classically, 2D games have controlled the relative order of sprites by setting a `z` coordinate: either as part of a fully 3D position / transform or as a seperate field. This is a largely functional and familiar solution, but it comes with a few problems: - encourages magic numbers, requiring discipline on behalf of the developer to maintain consistency - even when constants are used, the meaning of the z-coordinate is unclear when inspecting values in asset files or inspectors - leads to z-fighting bugs, where two sprites share the same z coordinate and fight each other - camera culling can result in arbitrarily not rendering sprites when they fall outside of the orthographic camera's viewing frustum - this is a leaky abstraction, and very hard for beginners to debug [#1275](https://github.com/bevyengine/bevy/issues/1275) discusses a better way: leaning on the concept of "layers" from 2D art tools like Photoshop or Inkscape. [#19463] proposes an initial solution to this problem. ## Out-of-scope: UI layering is confusing and inconsistent Similar problems arise in UI when attempting to control the relative layering for UI nodes that overlap. However, the solution there is based on web standards, works well enough, and has different enough problems that a unified solution should only be attempted after very careful consideration. For now, we'll leave it alone. ## Out-of-scope: transforms for gameplay When you first begin creating games, it makes sense to have a single source of truth for information about where objects are. And for some applications, this works fine! However, this can break down if: - you need to operate using fixed timesteps (improves robustness to frame-time variation, very common for simulation or networking) - simply updating the `Transform` during your fixed update will lead to perceptible choppiness - interpolating correctly is [surprisingly nuanced](https://github.com/Jondolf/bevy_transform_interpolation). - you want to decouple visual animations from in-game position - this is related to but distinct from using simplified colliders rather than using a full mesh for collision detection - you want to model space using discretized steps (common for tilemaps) While these types are easy enough to [create externally](https://docs.rs/avian3d/latest/avian3d/physics_transform/index.html), the current status quo presents a pitfall (and time sink) for beginners, and limits cross-crate compatibility. This problem is not closely tied to resolving 2D transform headaches, and can be tackled seperately. ### Out of scope: generic `Transform` types for floating point or f64 support As discussed in [#1680](https://github.com/bevyengine/bevy/issues/1680), a surprising number of our users are interested in unusual transform types: f64 for very large games, fixed point to try to avoid issues with numerical precision, even more exotic things for supporting 4D game worlds... While supporting these users directly is out of scope, it would be good to have a clearly documented pattern that they can use, allowing them to represent positions in their game world however they want without throwing away Bevy's rendering infrastructure or having to fork the engine and multiple ecosystem crates. ### Out of scope: refactoring how `Transform` is stored and accessed Some users feel that storing position/rotation/scale information on the same component is suboptimal for performance and [ergonomics reasons](https://github.com/bevyengine/bevy/issues/6006). This change is very controversial, and has been ruled out of scope by Cart. ### Out of scope: disable transform propagation [#1780](https://github.com/bevyengine/bevy/issues/1780) and [#9228](https://github.com/bevyengine/bevy/issues/9228) lay out a relatively common request: allow users to opt out of some or all of transform propagation. While this may be reasonable, it doesn't need to be tackled at the same time. ## Solutions ### `GlobalTransform` as a foundational interface At the end of the day, we need to be able to compare objects of all sorts to each other in a unified space. This is most essential for rendering: we cannot possibly hope to rewrite our rendering code to be generic over the choice of transform type in a meaningful way. Instead, we propose using `GlobalTransform` as our lingua franca. If an object wants to be rendered, it must have a `GlobalTransform`, and rendering *only* cares about this type. #### Open question: mixed transform hierarchies There are a number of important cases where users might plausibly have entity hierarchies with differing transforms: - world-space UI, like HUDs - world space UI elements rendered using sprites - Paper Mario-style sprites - decals - billboarding Ideally composing these entities should Just Work in an intuitive fashion, but what does that mean precisely? How can we achieve that in code? ### Dedicated `UiTransform` [#16615](https://github.com/bevyengine/bevy/pull/16615) refactored our UI solution to improve performance, fix bugs with animation and picking and generally reduce tech debt. This also comes with its own `UiGlobalTransform`, to accumulate inherited positions. ### Open question: naming consistency Should this be `TransformUi` and `GlobalTransformUi` instead? ### Dedicated `Transform2D` A dedicated 2D transform type is fairly simple to define: ```rust struct Transform2d { position: Vec2, rotation: f32, scale: Vec2, } ``` This saves data, prevents a number of footguns and avoids the dreaded quaternion. Notably, unlike in many other solutions, we are not storing information about how to sort 2D objects using an integrated `z` element of position. This design is confusing to beginners, and permits a number of meaningless operations, increasing the surface area for errors. This could be a simple `layer: f32` field, but we think that we can do better. See below! #### Open question: `GlobalTransform2d`? Like with UI, we probably need a way to be able to check the absolute position of 2D elements. However, simply using `GlobalTransform` is probably sufficient: the primary challenges with `Transform` come from setting and modifying values, not reading them. Omitting a `GlobalTransform2d` eases migration, reduces code and saves memory on every sprite. However, adding it might speed up transform propagation as we could do this entirely in 2D. ### `Transform` -> `Transform3d` rename Why should 3D get all the nice things? If `Transform2d` is good enough for 2D users, shouldn't we use `Transform3d` for consistency? Pros: - steers users towards `Transform2d` - consistent - makes 2D feel like an equal first-class citizen - makes it very clear whether objects are intended to be positioned in 2D or 3D Cons: - slightly longer - requires a breaking change #### Open question: naming conventions We could pick a different, still consistent naming convention and get all of these benefits! Note that we cannot pick `2dTransform` or similar: Rust will not let you start identifiers with numbers. ### Better 2D layering Moving away from simple z-coordinates for 2D layering resolves a number of problems: no more surprising culling, clearer semantics, reduced z-fighting. But it also unlocks a number of more sophisticated new features! [#19463](https://github.com/bevyengine/bevy/pull/19463) proposes a complex but comprehensive design to support a number of distinct ordering solutions. These are designed as optional components, rather than as a field on `Transform2d`. This allows for users to select their preferred solution, and allows us to #### Z-Index The most basic sorting strategy, relying on a signed integer for manually ordering objects. The use of integers prevents surprising problems with floating points' limited precision, although it can force users to suddenly have to "shift" coordinates if too many intermediate layers are added. #### Y-Sort tie breaking 2D games with pseudo-perspective commonly use the Y axis to make objects higher in the world appear behind objects that are lower. This is insufficient as a tie breaker, but it is a very helpful tool in its own right. #### Sort bias An arbitrary bias that is either added to Y transform or used as a secondary sort value. This allows for small "relative" tweaks to be made to quickly resolve problems. #### Open question: hierarchy and rendering order Should children always appear above their parents by default? This is how it works with UI, and generally aligns with artist expectations and has a major benefit of completely eliminating Z fightning within a hierarchy.. Engines like Godot use this as a tie breaker between items in the same layer by rendering each children in order, depth first, before rendering to the next item. User friendliness and predictability mostly depends on the child ordering API and quality of our inspection tooling. #### Open question: ZIndex naming This currently conflicts with `ZIndex` used in `bevy_ui`. #### Open question: unification with UI Can UI use the same paradigm? Should they? The existing solution mostly works (relative) #### Open question: usage in 3D When rendering objects onto the same plane in 3D (e.g. for diagetic HUDs), the same problems arise. Can we design a solution that Just Works there too? ## Implementation strategy This section lays out the PRs that should be made in order. This describes a tree data structure: PRs are only dependent on each other if they are nested. - [x] [seperate UI transforms](https://github.com/bevyengine/bevy/pull/16615) - [ ] docs PR explaining that `GlobalTransform` is the shared rendering interface - [ ] create a `Transform2d` type - [ ] migrate rest of the engine to `Transform2d` - [ ] rename `Transform` to `Transform3d` - [ ] 2D layering solution - [ ] rename `UiTransform` and `UiGlobalTransform` to match chosen naming convention - [ ] create an `TransformF64` example teaching users how to use `GlobalTransform` as a common interface

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully