bevy_xpbd
bevy_xpbd copied to clipboard
Freeze when cloning Collider
Hi, love the lib, thanks for all your work 🙏
ℹ️ I found a way around the bug described below.
After experimenting for a while, I found a roundabout way to do what I wanted, without freezing the game.
The idea is to delay the creation of the Collider and to set it in a system that only does that. For some reason it seems to work and I could spawn/duplicate thousands of bullets with 0 freeze.
I will use that in my game for now, because at least it's working. But I'm still curious about the problem I describe.
use avian2d::prelude::*;
use bevy::prelude::*;
#[derive(Component, Clone)]
struct Bullet;
#[derive(Component)]
struct ToDup;
#[derive(Component, Clone)]
struct NeedCollider;
fn main() {
App::new()
.add_plugins((DefaultPlugins, PhysicsPlugins::default()))
.insert_resource(Gravity(Vec2::ZERO))
.add_systems(Startup, |mut cmds: Commands| {
cmds.spawn(Camera2d);
})
.add_systems(Update, |q: Query<Entity, With<Bullet>>| {
println!("{}", q.iter().len())
})
.add_systems(Update, spawn)
.add_systems(Update, dup)
.add_systems(Update, col)
.run();
}
fn spawn(mut cmds: Commands, mut mt: ResMut<Assets<ColorMaterial>>, mut ms: ResMut<Assets<Mesh>>) {
cmds.spawn((
Bullet,
NeedCollider,
ToDup,
Transform::default(),
MeshMaterial2d(mt.add(ColorMaterial::from_color(Color::WHITE))),
Mesh2d(ms.add(Circle::new(2.0))),
));
}
fn dup(
mut cmds: Commands,
bullets: Query<Entity, With<ToDup>>,
mut mt: ResMut<Assets<ColorMaterial>>,
) {
bullets.iter().for_each(|entity| {
let mut bullet = cmds.entity(entity);
bullet.remove::<ToDup>();
bullet.clone_and_spawn().insert((
MeshMaterial2d(mt.add(ColorMaterial::from_color(Color::BLACK))),
LinearVelocity(Vec2 { x: 0.1, y: 0.0 }),
));
});
}
fn col(mut cmds: Commands, bullets: Query<Entity, With<NeedCollider>>) {
bullets.iter().for_each(|entity| {
cmds.entity(entity)
.insert((RigidBody::Dynamic, Collider::circle(2.0)));
});
}
I'm working on a game that uses some component caching, which prepares an entity, and when needed, clone it for actual usage. This all works well, except for when I try to clone a Collider. I've reduced the code to a minimum of:
[dependencies]
avian2d = "0.4"
bevy = "0.17.2"
use avian2d::prelude::*;
use bevy::prelude::*;
#[derive(Component, Clone)]
struct NeedDup;
fn main() {
App::new()
.add_plugins((DefaultPlugins, PhysicsPlugins::default()))
.add_systems(Startup, |mut cmds: Commands| {
cmds.spawn(Camera2d);
})
.add_systems(Update, spawn)
.add_systems(Update, dup)
.run();
}
fn spawn(mut cmds: Commands, mut mt: ResMut<Assets<ColorMaterial>>, mut ms: ResMut<Assets<Mesh>>) {
cmds.spawn((
NeedDup,
RigidBody::Dynamic,
Collider::circle(2.0),
MeshMaterial2d(mt.add(ColorMaterial::from_color(Color::WHITE))),
Mesh2d(ms.add(Circle::new(2.0))),
));
}
fn dup(mut cmds: Commands, entities: Query<Entity, With<NeedDup>>) {
entities.iter().for_each(|entity| {
cmds.entity(entity).remove::<NeedDup>().clone_and_spawn();
});
}
Without the collider, I can hold the left mouse button for a long time. I get some warnings, but otherwise the bullet are displayed/moved correctly
As soon as I enable the collider, it freezes right after a couple of bullets
As far as I could tell, the Collider::circle is solely responsible for making this freeze.
What am I doing wrong? How can I make the code better? Thanks!
As far as I can tell, spawning many entities at the same position isn't responsible for this.
// similar
fn click(
mut cmds: Commands,
m: Res<ButtonInput<MouseButton>>,
mut mt: ResMut<Assets<ColorMaterial>>,
mut ms: ResMut<Assets<Mesh>>,
) {
if !m.pressed(MouseButton::Left) && !m.just_pressed(MouseButton::Right) {
return;
}
cmds.spawn_batch([
(
Bullet,
ToDup,
RigidBody::Dynamic,
Collider::circle(2.0),
Transform::default(),
MeshMaterial2d(mt.add(ColorMaterial::from_color(Color::WHITE))),
Mesh2d(ms.add(Circle::new(2.0))),
),
(
Bullet,
ToDup,
RigidBody::Dynamic,
Collider::circle(2.0),
Transform::default(),
MeshMaterial2d(mt.add(ColorMaterial::from_color(Color::WHITE))),
Mesh2d(ms.add(Circle::new(2.0))),
),
// and more
]);
// similar
}
Removing the Collider fixes the issue, but trying to add it back, either directly or indirectly is causing the same issue.
-
Direct
fn dup( mut cmds: Commands, bullets: Query<Entity, With<ToDup>>, mut mt: ResMut<Assets<ColorMaterial>>, ) { bullets.iter().for_each(|entity| { let mut bullet = cmds.entity(entity); bullet.remove::<ToDup>(); bullet .clone_and_spawn() .remove::<Collider>() .insert(( Collider::circle(2.0), MeshMaterial2d(mt.add(ColorMaterial::from_color(Color::BLACK))), LinearVelocity(Vec2 { x: 10.0, y: 0.0 }), )); }); } -
Indirect
fn dup( mut cmds: Commands, bullets: Query<Entity, With<ToDup>>, mut mt: ResMut<Assets<ColorMaterial>>, ) { bullets.iter().for_each(|entity| { let mut bullet = cmds.entity(entity); bullet.remove::<ToDup>(); bullet .clone_and_spawn() .remove::<Collider>() .insert(( NeedCollider, MeshMaterial2d(mt.add(ColorMaterial::from_color(Color::BLACK))), LinearVelocity(Vec2 { x: 10.0, y: 0.0 }), )); }); } fn col(mut cmds: Commands, bullets: Query<Entity, With<NeedCollider>>) { bullets.iter().for_each(|entity| { cmds.entity(entity).insert(Collider::circle(2.0)); }); }