自由下落的箱子
动力学箱子 + 静态刚体地面.
箱子受重力影响。
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
fn main() {
App::new()
.insert_resource(ClearColor(Color::srgb(
0xF9 as f32 / 255.0,
0xF9 as f32 / 255.0,
0xFF as f32 / 255.0,
)))
.add_plugins((
DefaultPlugins,
RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0),
RapierDebugRenderPlugin::default(),
))
.add_systems(Startup, (setup_graphics, setup_physics))
.run();
}
pub fn setup_graphics(mut commands: Commands) {
commands.spawn((Camera2d, Transform::from_xyz(0.0, 20.0, 0.0)));
}
pub fn setup_physics(mut commands: Commands) {
/*
* Ground
*/
let ground_size = 500.0;
let ground_height = 10.0;
commands.spawn((
Transform::from_xyz(0.0, 0.0 * -ground_height, 0.0),
Collider::cuboid(ground_size, ground_height),
));
/*
* Create the cubes
*/
let num = 8;
let rad = 10.0;
let shift = rad * 2.0 + rad;
let centerx = shift * (num / 2) as f32;
let centery = shift / 2.0;
let mut offset = -(num as f32) * (rad * 2.0 + rad) * 0.5;
for j in 0usize..20 {
for i in 0..num {
let x = i as f32 * shift - centerx + offset;
let y = j as f32 * shift + centery + 30.0;
commands.spawn((
Transform::from_xyz(x, y, 0.0),
RigidBody::Dynamic,
Collider::cuboid(rad, rad),
));
}
offset -= 0.05 * rad * (num as f32 - 1.0);
}
}
碰撞器变大
碰撞器变大会有一个爆炸效果。
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
#[derive(Component, Default)]
pub struct Despawn;
#[derive(Component, Default)]
pub struct Resize;
#[derive(Resource, Default)]
pub struct DespawnResource {
timer: Timer,
}
#[derive(Resource, Default)]
pub struct ResizeResource {
timer: Timer,
}
fn main() {
App::new()
.insert_resource(ClearColor(Color::srgb(
0xF9 as f32 / 255.0,
0xF9 as f32 / 255.0,
0xFF as f32 / 255.0,
)))
.insert_resource(DespawnResource::default())
.insert_resource(ResizeResource::default())
.add_plugins((
DefaultPlugins,
RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0),
RapierDebugRenderPlugin::default(),
))
.add_systems(Startup, (setup_graphics, setup_physics))
.add_systems(Update, (despawn, resize))
.run();
}
pub fn setup_graphics(
mut commands: Commands,
mut despawn: ResMut<DespawnResource>,
mut resize: ResMut<ResizeResource>,
) {
resize.timer = Timer::from_seconds(6.0, TimerMode::Once);
despawn.timer = Timer::from_seconds(11.0, TimerMode::Once);
commands.spawn((Camera2d, Transform::from_xyz(0.0, 20.0, 0.0)));
}
pub fn setup_physics(mut commands: Commands) {
/*
* Ground
*/
let ground_size = 250.0;
commands.spawn((Collider::cuboid(ground_size, 12.0), Despawn));
commands.spawn((
Transform::from_xyz(ground_size, ground_size * 2.0, 0.0),
Collider::cuboid(12.0, ground_size * 2.0),
));
commands.spawn((
Transform::from_xyz(-ground_size, ground_size * 2.0, 0.0),
Collider::cuboid(12.0, ground_size * 2.0),
));
/*
* Create the cubes
*/
let num = 20;
let rad = 5.0;
let shift = rad * 2.0;
let centerx = shift * (num as f32) / 2.0;
let centery = shift / 2.0;
for i in 0..num {
for j in 0usize..num * 5 {
let x = i as f32 * shift - centerx;
let y = j as f32 * shift + centery + 2.0;
let mut entity = commands.spawn((
Transform::from_xyz(x, y, 0.0),
RigidBody::Dynamic,
Collider::cuboid(rad, rad),
));
if (i + j * num) % 100 == 0 {
entity.insert(Resize);
}
}
}
}
pub fn despawn(
mut commands: Commands,
time: Res<Time>,
mut despawn: ResMut<DespawnResource>,
query: Query<Entity, With<Despawn>>,
) {
if despawn.timer.tick(time.delta()).just_finished() {
for e in &query {
commands.entity(e).despawn();
}
}
}
pub fn resize(
mut commands: Commands,
time: Res<Time>,
mut resize: ResMut<ResizeResource>,
query: Query<Entity, With<Resize>>,
) {
if resize.timer.tick(time.delta()).just_finished() {
for e in &query {
commands.entity(e).insert(Collider::cuboid(20.0, 20.0));
}
}
}
碰撞事件:普通碰撞和传感器
为了效果不那么一眨眼就不见,修改物理具体和游戏距离的映射。
RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(2.0),.
pub fn display_events(
mut collision_events: EventReader<CollisionEvent>,
mut contact_force_events: EventReader<ContactForceEvent>,
) {
for collision_event in collision_events.read() {
println!("Received collision event: {collision_event:?}");
}
for contact_force_event in contact_force_events.read() {
println!("Received contact force event: {contact_force_event:?}");
}
}
pub fn setup_physics(mut commands: Commands) {
/*
* Ground
*/
// 碰到地面就会有碰撞事件。
commands.spawn((
Transform::from_xyz(0.0, -24.0, 0.0),
Collider::cuboid(80.0, 20.0),
));
// 传感器仅做检测,不参与力的交互。
// 进出传感器都会发送事件。
commands.spawn((
Transform::from_xyz(0.0, 100.0, 0.0),
Collider::cuboid(80.0, 30.0),
Sensor,
));
commands.spawn((
Transform::from_xyz(0.0, 260.0, 0.0),
RigidBody::Dynamic,
Collider::cuboid(10.0, 10.0),
ActiveEvents::COLLISION_EVENTS,
ContactForceEventThreshold(10.0),
));
}
平移锁定和旋转锁定
高度500有个对象是旋转锁定A;高度300有个对象是平移锁定B.
A落下碰到B,B开始旋转;A继续落到地面,且保持旋转角度不变。
旋转锁定对象从500落到300,出发300
pub fn setup_physics(mut commands: Commands) {
/*
* The ground
*/
let ground_size = 500.0;
let ground_height = 10.0;
commands.spawn((
Transform::from_xyz(0.0, -ground_height, 0.0),
Collider::cuboid(ground_size, ground_height),
));
/*
* A rectangle that only rotate.
*/
commands.spawn((
Transform::from_xyz(0.0, 300.0, 0.0),
RigidBody::Dynamic,
LockedAxes::TRANSLATION_LOCKED,
Collider::cuboid(200.0, 60.0),
));
/*
* A tilted cuboid that cannot rotate.
*/
commands.spawn((
Transform::from_xyz(50.0, 500.0, 0.0).with_rotation(Quat::from_rotation_z(1.0)),
RigidBody::Dynamic,
LockedAxes::ROTATION_LOCKED,
Collider::cuboid(60.0, 40.0),
));
}
多碰撞器
一个动力学刚体带多个碰撞器
commands
.spawn((Transform::from_xyz(x, y, 0.0), RigidBody::Dynamic))
.with_children(|children| {
children.spawn(Collider::cuboid(rad * 10.0, rad));
children.spawn((
Transform::from_xyz(rad * 10.0, rad * 10.0, 0.0),
Collider::cuboid(rad, rad * 10.0),
));
children.spawn((
Transform::from_xyz(-rad * 10.0, rad * 10.0, 0.0),
Collider::cuboid(rad, rad * 10.0),
));
});
动力学角色的移动
零重力,通过修改速度来控制移动。
// The float value is the player movement speed in 'pixels/second'.
#[derive(Component)]
pub struct Player(f32);
pub fn spawn_player(
mut commands: Commands,
mut rapier_config: Query<&mut RapierConfiguration>,
) -> Result<()> {
let mut rapier_config = rapier_config.single_mut()?;
// Set gravity to 0.0 and spawn camera.
rapier_config.gravity = Vec2::ZERO;
commands.spawn(Camera2d);
let sprite_size = 100.0;
// Spawn entity with `Player` struct as a component for access in movement query.
commands.spawn((
Sprite {
color: Color::srgb(0.0, 0.0, 0.0),
custom_size: Some(Vec2::new(sprite_size, sprite_size)),
..Default::default()
},
RigidBody::Dynamic,
Velocity::zero(),
Collider::ball(sprite_size / 2.0),
Player(100.0),
));
Ok(())
}
pub fn player_movement(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut player_info: Query<(&Player, &mut Velocity)>,
) {
for (player, mut rb_vels) in &mut player_info {
let up = keyboard_input.any_pressed([KeyCode::KeyW, KeyCode::ArrowUp]);
let down = keyboard_input.any_pressed([KeyCode::KeyS, KeyCode::ArrowDown]);
let left = keyboard_input.any_pressed([KeyCode::KeyA, KeyCode::ArrowLeft]);
let right = keyboard_input.any_pressed([KeyCode::KeyD, KeyCode::ArrowRight]);
let x_axis = -(left as i8) + right as i8;
let y_axis = -(down as i8) + up as i8;
let mut move_delta = Vec2::new(x_axis as f32, y_axis as f32);
if move_delta != Vec2::ZERO {
move_delta /= move_delta.length();
}
// Update the velocity on the rigid_body_component,
// the bevy_rapier plugin will update the Sprite transform.
rb_vels.linvel = move_delta * player.0;
}
}
物理钩子做过滤
首先用物理钩子的实现替换了NoUserData,有相同标记组件的会走到解算阶段。
use bevy::{ecs::system::SystemParam, prelude::*};
use bevy_rapier2d::prelude::*;
#[derive(PartialEq, Eq, Clone, Copy, Component)]
enum CustomFilterTag {
GroupA,
GroupB,
}
// A custom filter that allows contacts only between rigid-bodies with the
// same user_data value.
// Note that using collision groups would be a more efficient way of doing
// this, but we use custom filters instead for demonstration purpose.
#[derive(SystemParam)]
struct SameUserDataFilter<'w, 's> {
tags: Query<'w, 's, &'static CustomFilterTag>,
}
impl BevyPhysicsHooks for SameUserDataFilter<'_, '_> {
fn filter_contact_pair(&self, context: PairFilterContextView) -> Option<SolverFlags> {
if self.tags.get(context.collider1()).ok().copied()
== self.tags.get(context.collider2()).ok().copied()
{
Some(SolverFlags::COMPUTE_IMPULSES)
} else {
None
}
}
}
fn main() {
App::new()
.insert_resource(ClearColor(Color::srgb(
0xF9 as f32 / 255.0,
0xF9 as f32 / 255.0,
0xFF as f32 / 255.0,
)))
.add_plugins((
DefaultPlugins,
RapierPhysicsPlugin::<SameUserDataFilter>::pixels_per_meter(100.0),
RapierDebugRenderPlugin::default(),
))
.add_systems(Startup, (setup_graphics, setup_physics))
.run();
}
fn setup_graphics(mut commands: Commands) {
commands.spawn((Camera2d, Transform::from_xyz(0.0, 20.0, 0.0)));
}
pub fn setup_physics(mut commands: Commands) {
/*
* Ground
*/
let ground_size = 100.0;
commands.spawn((
Transform::from_xyz(0.0, -100.0, 0.0),
Collider::cuboid(ground_size, 12.0),
CustomFilterTag::GroupA,
));
commands.spawn((
Transform::from_xyz(0.0, 0.0, 0.0),
Collider::cuboid(ground_size, 12.0),
CustomFilterTag::GroupB,
));
/*
* Create the cubes
*/
let num = 4;
let rad = 5.0;
let shift = rad * 2.0;
let centerx = shift * (num as f32) / 2.0;
let centery = shift / 2.0;
let mut group_id = 0;
let tags = [CustomFilterTag::GroupA, CustomFilterTag::GroupB];
let colors = [Hsla::hsl(220.0, 1.0, 0.3), Hsla::hsl(260.0, 1.0, 0.7)];
for i in 0..num {
for j in 0usize..num * 5 {
let x = (i as f32 + j as f32 * 0.2) * shift - centerx;
let y = j as f32 * shift + centery + 20.0;
group_id += 1;
commands.spawn((
Transform::from_xyz(x, y, 0.0),
RigidBody::Dynamic,
Collider::cuboid(rad, rad),
ActiveHooks::FILTER_CONTACT_PAIRS,
tags[group_id % 2],
ColliderDebugColor(colors[group_id % 2]),
));
}
}
}
自定义setup system
这个估计很少有人用到。
另外还有个debug的toggle.