avian3d/physics_transform/
helper.rs

1use bevy::{
2    ecs::{
3        entity::Entity,
4        system::{Query, SystemParam, lifetimeless::Write},
5        world::Mut,
6    },
7    transform::{
8        components::GlobalTransform,
9        helper::{ComputeGlobalTransformError, TransformHelper},
10    },
11};
12use thiserror::Error;
13
14use crate::{
15    math::AdjustPrecision,
16    prelude::{Position, Rotation},
17};
18
19/// A system parameter for computing up-to-date [`Position`] and [`Rotation`] components
20/// of entities based on their [`Transform`]s.
21///
22/// This can be useful to ensure that physics transforms are immediately updated after changes
23/// to the [`Transform`], before transform propagation systems are run.
24///
25/// Computing the global transform of each entity individually can be expensive,
26/// so it is recommended to only use this for specific entities that require immediate updates,
27/// such as right after teleporting an entity.
28///
29/// [`Transform`]: bevy::transform::components::Transform
30#[derive(SystemParam)]
31pub struct PhysicsTransformHelper<'w, 's> {
32    /// The [`TransformHelper`] used to compute the global transform.
33    pub transform_helper: TransformHelper<'w, 's>,
34    /// A query for the [`Position`] and [`Rotation`] components.
35    pub query: Query<'w, 's, (Write<Position>, Write<Rotation>)>,
36}
37
38impl PhysicsTransformHelper<'_, '_> {
39    /// Computes the [`GlobalTransform`] of the given entity from its [`Transform`] and ancestors.
40    ///
41    /// [`Transform`]: bevy::transform::components::Transform
42    pub fn compute_global_transform(
43        &self,
44        entity: Entity,
45    ) -> Result<GlobalTransform, ComputeGlobalTransformError> {
46        self.transform_helper.compute_global_transform(entity)
47    }
48
49    /// Updates the [`Position`] and [`Rotation`] components of the given entity based on its
50    /// [`Transform`] and ancestors.
51    ///
52    /// Returns a mutable reference to the updated [`Position`] and [`Rotation`] components.
53    ///
54    /// [`Transform`]: bevy::transform::components::Transform
55    pub fn update_physics_transform(
56        &mut self,
57        entity: Entity,
58    ) -> Result<(Mut<'_, Position>, Mut<'_, Rotation>), UpdatePhysicsTransformError> {
59        use ComputeGlobalTransformError::*;
60
61        // Compute the global transform.
62        let global_transform = self
63            .transform_helper
64            .compute_global_transform(entity)
65            .map_err(|err| match err {
66                MissingTransform(e) => UpdatePhysicsTransformError::MissingTransform(e),
67                NoSuchEntity(e) => UpdatePhysicsTransformError::NoSuchEntity(e),
68                MalformedHierarchy(e) => UpdatePhysicsTransformError::MalformedHierarchy(e),
69            })?;
70
71        // Update the physics transform components.
72        let Ok((mut position, mut rotation)) = self.query.get_mut(entity) else {
73            return Err(UpdatePhysicsTransformError::MissingTransform(entity));
74        };
75        #[cfg(feature = "2d")]
76        {
77            position.0 = global_transform.translation().truncate().adjust_precision();
78            *rotation = Rotation::from(global_transform.rotation().adjust_precision());
79        }
80        #[cfg(feature = "3d")]
81        {
82            position.0 = global_transform.translation().adjust_precision();
83            rotation.0 = global_transform.rotation().adjust_precision();
84        }
85
86        Ok((position, rotation))
87    }
88}
89
90/// Error returned by [`PhysicsTransformHelper::update_physics_transform`].
91#[derive(Debug, Error)]
92pub enum UpdatePhysicsTransformError {
93    /// The entity or one of its ancestors is missing either the [`Transform`], [`Position`], or [`Rotation`] component.
94    ///
95    /// [`Transform`]: bevy::transform::components::Transform
96    #[error(
97        "The entity {0:?} or one of its ancestors is missing either the `Transform`, `Position`, or `Rotation` component"
98    )]
99    MissingTransform(Entity),
100    /// The entity does not exist.
101    #[error("The entity {0:?} does not exist")]
102    NoSuchEntity(Entity),
103    /// An ancestor is missing.
104    /// This probably means that your hierarchy has been improperly maintained.
105    #[error("The ancestor {0:?} is missing")]
106    MalformedHierarchy(Entity),
107}