Bevy子实体朝向异常求助:子Watcher无法正确看向Marker
解决Bevy中子实体Watcher朝向错误的问题
你遇到的问题核心在于:当Watcher挂载在子实体上时,直接操作子实体的Transform.look_at()会用局部空间坐标计算朝向,但目标是全局位置,导致方向错乱。子实体的Transform是相对于父实体的局部变换,必须先在全局空间计算好正确的朝向,再转换为对应层级的局部变换才能生效。
下面是修改后的代码,适配根实体和子实体两种场景:
use bevy::prelude::*; pub struct Watcher; #[derive(Component)] pub struct Marker; pub fn watch_marker( mut watcher_query: Query<(&mut Transform, &GlobalTransform, Option<&Parent>), With<Watcher>>, marker_query: Query<&GlobalTransform, With<Marker>>, parent_transform_query: Query<&GlobalTransform, Without<Watcher>>, ) { // 获取Marker的全局变换,不存在则直接返回 let Ok(marker_global_transform) = marker_query.get_single() else { return; }; let marker_global_pos = marker_global_transform.translation(); for (mut watcher_transform, watcher_global_transform, parent) in watcher_query.iter_mut() { // 计算全局空间下,Watcher指向Marker的方向向量 let watcher_global_pos = watcher_global_transform.translation(); let direction = (marker_global_pos - watcher_global_pos).normalize(); // 生成全局空间下的目标旋转(朝向Marker,Y轴为上方向) let target_global_rotation = Quat::from_look_at(direction, Vec3::Y); // 根据是否有父实体,转换为局部旋转 let target_local_rotation = if let Some(parent) = parent { // 获取父实体的全局旋转,计算其逆矩阵 let parent_global_rotation = parent_transform_query.get(parent.get()).unwrap().rotation(); // 父全局旋转的逆 × 目标全局旋转 = 子实体需要的局部旋转 parent_global_rotation.inverse() * target_global_rotation } else { // 无父实体时,全局旋转直接作为局部旋转 target_global_rotation }; // 更新子实体的局部旋转,保留原有位置和缩放 watcher_transform.rotation = target_local_rotation; } }
关键逻辑说明
- 全局空间计算朝向:先通过
GlobalTransform拿到Watcher和Marker的全局位置,计算出正确的朝向方向,生成全局旋转。这一步确保不管实体在哪个层级,朝向逻辑都是统一的。 - 局部旋转转换:对于子实体,父实体的全局旋转决定了子实体的局部空间基准。用父全局旋转的逆矩阵乘以目标全局旋转,就能得到子实体需要的局部旋转——应用这个局部旋转后,子实体的全局旋转会正好等于我们计算的目标朝向。
- 兼容根实体:如果Watcher在根实体上,直接使用全局旋转作为局部旋转,和原逻辑保持一致,不影响原有功能。
内容的提问来源于stack exchange,提问作者stevemarvell
相关产品推荐
相关产品推荐

