如何在Rust Bevy中利用时间实现玩家重力系统?
在Rust Bevy引擎中实现玩家重力系统的可行方案
你当前的代码存在两个核心问题:一是重复获取Velocity2可变引用会触发panic(同一查询无法同时获取两次可变权限);二是直接将y速度设置为固定值乘时间增量的方式不符合真实物理逻辑(重力应作为加速度持续改变速度,而非直接赋值)。以下是几种更合理的实现方案:
方案1:基于物理加速度的标准实现(手动处理)
将重力逻辑与玩家输入拆分,让系统职责更清晰,同时符合物理规律:
1. 玩家输入控制单独处理
fn player_movement( kb: Res<Input<KeyCode>>, mut query: Query<&mut Velocity2, With<Player2>>, ) { let mut velocity = query.single_mut(); // 水平移动速度设置 velocity.x = match (kb.pressed(KeyCode::A), kb.pressed(KeyCode::D)) { (true, false) => -5.0, // 可根据需求调整移动速度 (false, true) => 5.0, _ => 0.0, }; // 跳跃触发(使用just_pressed防止持续跳跃) if kb.just_pressed(KeyCode::W) { velocity.y = 8.0; // 跳跃初速度 } // 下蹲/快速下落 if kb.pressed(KeyCode::S) { velocity.y = -6.0; } }
2. 重力加速度单独应用
fn apply_gravity( mut query: Query<&mut Velocity2, With<Player2>>, time: Res<Time>, ) { let mut velocity = query.single_mut(); const GRAVITY: f32 = -9.8; // 模拟现实重力,可自定义调整 velocity.y += GRAVITY * time.delta_seconds(); }
记得在App中同时添加这两个系统,且保证重力系统在输入系统之后执行(可以通过SystemSet或after方法排序)。
方案2:使用Bevy内置物理插件(省心高效)
如果项目需要处理碰撞、地面检测等复杂物理逻辑,直接用Bevy官方推荐的物理插件(如bevy_rapier2d或bevy_xpbd)更高效:
- 添加依赖到
Cargo.toml:
bevy_rapier2d = "0.21" # 版本号需匹配你的Bevy版本
- 初始化物理插件:
use bevy_rapier2d::prelude::*; App::new() .add_plugins(DefaultPlugins) .add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0)) .add_plugins(RapierDebugRenderPlugin::default()) // 可选:调试碰撞体渲染 .add_systems(Startup, spawn_player) .add_systems(Update, player_controls) .run();
- 生成带物理组件的玩家:
fn spawn_player(mut commands: Commands) { commands.spawn(( Player2, SpriteBundle { sprite: Sprite { color: Color::RED, custom_size: Some(Vec2::new(32.0, 32.0)), ..default() }, transform: Transform::from_xyz(0.0, 0.0, 0.0), ..default() }, RigidBody::Dynamic, // 动态刚体,受重力和外力影响 Collider::cuboid(16.0, 16.0), // 碰撞体尺寸匹配精灵 GravityScale(1.0), // 重力缩放系数,可调整 Velocity::zero(), )); }
- 玩家输入控制(只需操作物理引擎的速度):
fn player_controls( kb: Res<Input<KeyCode>>, mut query: Query<&mut Velocity, With<Player2>>, ) { let mut velocity = query.single_mut(); const MOVE_SPEED: f32 = 500.0; const JUMP_FORCE: f32 = 800.0; velocity.linvel.x = match (kb.pressed(KeyCode::A), kb.pressed(KeyCode::D)) { (true, false) => -MOVE_SPEED, (false, true) => MOVE_SPEED, _ => 0.0, }; if kb.just_pressed(KeyCode::W) { velocity.linvel.y = JUMP_FORCE; } }
物理引擎会自动处理重力、碰撞、地面检测等逻辑,无需手动编写重力代码。
方案3:简化单系统实现(快速原型)
如果不想拆分系统,可修复原代码的可变引用问题,并正确应用重力:
fn player_2_keyboard_event_system( kb: Res<Input<KeyCode>>, mut query: Query<&mut Velocity2, With<Player2>>, time: Res<Time>, ) { let mut velocity2 = query.single_mut(); // 水平移动 velocity2.x = if kb.pressed(KeyCode::A) { -5.0 } else if kb.pressed(KeyCode::D) { 5.0 } else { 0.0 }; // 垂直控制与重力 if kb.pressed(KeyCode::S) { velocity2.y = -6.0; } else if kb.just_pressed(KeyCode::W) { velocity2.y = 8.0; } else { const GRAVITY: f32 = -9.8; velocity2.y += GRAVITY * time.delta_seconds(); } }
内容的提问来源于stack exchange,提问作者Rusty_Coder
相关产品推荐
相关产品推荐

