Bevy 0.8.1中添加PlayerStatus后iter_mut()停止迭代求助
Bevy 0.8.1添加PlayerStatus组件后碰撞检测循环停止运行
问题场景
已实现玩家与地面的碰撞检测,计划通过on_ground变量添加跳跃机制,但在查询中加入PlayerStatus组件后,collision_detection系统内的player.iter_mut()循环完全不执行。
原问题代码:
fn collision_detection( ground: Query<&Transform, (With<Ground>, Without<Player>)>, mut player: Query<(&mut Transform, &mut PlayerStatus), With<Player>>, ) { let player_size = Vec2::new(PLAYER_SIZE_X, PLAYER_SIZE_Y); let ground_size = Vec2::new(GROUND_SIZE_X, GROUND_SIZE_Y); for ground in ground.iter() { for (mut player, mut status) in player.iter_mut() { if collide( player.translation, player_size, ground.translation, ground_size, ) .is_some() { status.on_ground = true; println!("ON GROUND") } else { status.on_ground = false; } if status.on_ground { player.translation.y += GRAVITY; } } } }
仅查询&mut Transform时代码可正常运行:
for mut player in player.iter_mut() { if collide( player.translation, player_size, ground.translation, ground_size, ) .is_some() { player.translation.y += GRAVITY; } }
问题原因
- 实体组件不匹配:玩家实体未同时挂载
PlayerStatus组件。查询(&mut Transform, &mut PlayerStatus), With<Player>要求实体必须同时拥有这三个元素,若玩家实体缺失PlayerStatus,查询结果为空,循环自然不会执行。 - 组件访问冲突:存在其他系统独占访问
&mut PlayerStatus组件。Bevy的ECS会保证同一时间只有一个系统能获取某个组件的可变引用,若其他系统先拿到PlayerStatus的可变权限,当前系统的查询会找不到可用实体。 - 原代码嵌套循环逻辑冗余,多次遍历玩家和地面,且重力逻辑有误(应为不在地面时施加重力)。
解决方案
1. 确保玩家实体添加PlayerStatus组件
生成玩家时,必须同时插入Player、Transform和PlayerStatus组件:
commands.spawn(( Player, Transform::from_xyz(0.0, 5.0, 0.0), PlayerStatus { on_ground: false }, ));
2. 排查组件访问冲突
检查其他系统的查询语句,是否有系统也使用了&mut PlayerStatus。如果有,要么调整系统运行顺序,要么改为使用&PlayerStatus(只读)避免独占冲突。
3. 优化碰撞检测逻辑
由于玩家通常只有一个,使用single_mut()替代iter_mut(),同时修正重力逻辑:
fn collision_detection( ground: Query<&Transform, (With<Ground>, Without<Player>)>, mut player: Query<(&mut Transform, &mut PlayerStatus), With<Player>>, ) { let player_size = Vec2::new(PLAYER_SIZE_X, PLAYER_SIZE_Y); let ground_size = Vec2::new(GROUND_SIZE_X, GROUND_SIZE_Y); // 获取单个玩家实体 if let Ok((mut player_transform, mut status)) = player.single_mut() { // 默认设为不在地面 status.on_ground = false; for ground_transform in ground.iter() { if collide( player_transform.translation, player_size, ground_transform.translation, ground_size, ).is_some() { status.on_ground = true; println!("ON GROUND"); // 找到碰撞地面后跳出循环,避免重复检测 break; } } // 不在地面时施加重力 if !status.on_ground { player_transform.translation.y += GRAVITY; } } }
内容的提问来源于stack exchange,提问作者Kuumin
相关产品推荐
相关产品推荐

