-
Hey there, I'm working on a custom pixel-perfect 2D renderer for Bevy and I wanted to get some help with how is the simplest, but effective way to design the transform system if it only needs 2D and traslations, i.e. no rotation or scale. My biggest question here was around how and when the global translation is updated. As I thought about it I was feeling like the global translation kind of needed to be updated after every system that might change the transform. For instance, what if I had one system moving platforms of some sort, and then the next system trying to detect collisions between the player and the platforms. Collision detection has to happen on the world position of the objects, so after the platform move system I would have to make sure that I update the platform's global translation. Is it feasible to literally update the global transform of the object in between every system run somehow? Or is there a simpler way just to keep it always synchronized every time you change the transform of anything. I know there was kind of a lot of discussion about this for the Bevy transform system and I bet a lot of that would carry over to here, but at the same time, I'm working with what is hopefully a much simplified problem because it's only 2D and only translation. Any guidance or thoughts would be appreciated! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
Take a look at the discussion in #1460 :) This should give you a good understanding of the problem domain, and then let me know if you have any follow-up questions. |
Beta Was this translation helpful? Give feedback.
-
So for my system I'm currently going with an experimental strategy that is designed to save me as much coding ( and corresponding technical know-how and potential bugs 😁 ) as possible. Using the It was super easy to setup and worked almost the first time I got it compiling! I'm not sure how it compares to bevy's transform propagation system in performance, but I feel like it would be reasonably efficient. You only need to borrow the I also used my own change-detection field on the If anybody wants to point out flaws in the design that would be great. 😉 The one I can think of for sure is that adding and removing entities from the hierarchy is possibly slower because it requires a borrow to the Usage( Note: Usage example is with a custom renderer so the bundles and components are not normal bevy ones. ) fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut scene_graph: ResMut<SceneGraph>,
) {
// Spawn the camera
commands.spawn(CameraBundle {
camera: Camera {
size: CameraSize::FixedHeight(100),
..Default::default()
},
..Default::default()
});
// Load our sprite images
let sensei_image = asset_server.load("sensei2.gitignore.png");
let guy_image = asset_server.load("guy.gitignore.png");
let barrel_image: Handle<Image> = asset_server.load("barrel.gitignore.png");
// Create sensei entity
let sensei = commands.spawn(()).current_entity().unwrap();
// Add the sensei entity to the scene graph
let sensei_node = scene_graph.add_node(sensei);
// And add the sprite components
commands
.with_bundle(SpriteBundle {
image: sensei_image,
scene_node: sensei_node,
position: Position::new(0, 0, 2),
..Default::default()
})
// Add our sensei marker component
.with(Sensei);
// Create the guy ( student ) entity
let guy = commands.spawn(()).current_entity().unwrap();
// Add the guy entity to the scene graph
let guy_node = scene_graph.add_node(guy);
// The guy is a great student and follows the sensei everywhere, i.e, add the guy node as a
// child of the sensei node
scene_graph.add_child(sensei_node, guy_node);
// And add the sprite components to the guy
commands.with_bundle(SpriteBundle {
image: guy_image,
scene_node: guy_node,
// The guy follows a little behind the sensei
position: Position::new(-20, 4, -1),
..Default::default()
});
// Create barrel entity
let barrel = commands.spawn(()).current_entity().unwrap();
// Add the barrel entity to the scene graph
let barrel_node = scene_graph.add_node(barrel);
// Make the sensei a child of the barrel: no allegorical wisdom, just go with it
scene_graph.add_child(barrel_node, sensei_node);
// And add the sprite components
commands
.with_bundle(SpriteBundle {
image: barrel_image,
scene_node: barrel_node,
..Default::default()
});
} Position Propagation SystemThis turned out super easy: pub fn propagate_world_positions(
scene_graph: Res<SceneGraph>,
mut query: Query<(&Position, &SceneNode, &mut WorldPosition)>,
) {
let graph = &scene_graph.0;
for root_node in graph
.externals(Direction::Incoming)
.into_iter()
.collect::<Vec<_>>()
{
propagate(root_node, graph, &mut query, None);
}
}
fn propagate(
node: NodeIndex,
graph: &StableGraph<Entity, (), Directed>,
query: &mut Query<(&Position, &SceneNode, &mut WorldPosition)>,
parent_world_position: Option<WorldPosition>,
) {
// Unwrap parent world position
let parent_world_position = parent_world_position.unwrap_or_default();
// Handle this node's transform
let world_pos = {
// Get the node entity and it's position and world position
let node_entity = graph[node];
let (node_pos, _, mut world_pos) = query.get_mut(node_entity).unwrap();
// If the node's transform has changed since we last saw it
if node_pos.dirty {
// Propagate it's global transform
**world_pos = *parent_world_position + **node_pos;
}
world_pos.clone()
};
// Propagate child nodes
for child_node in graph.neighbors(node).into_iter().collect::<Vec<_>>() {
propagate(child_node, graph, query, Some(world_pos));
}
} |
Beta Was this translation helpful? Give feedback.
So for my system I'm currently going with an experimental strategy that is designed to save me as much coding ( and corresponding technical know-how and potential bugs 😁 ) as possible.
Using the
petgraph
crate, I create aSceneGraph
resource that can be used to allocate nodes in the scene hierarchy. You then addSceneNode
components and attach them to your entities to put them in the hierarchy. Then the transform propagation system is ultra-simple because it can simply traverse the graph to update the nodes.It was super easy to setup and worked almost the first time I got it compiling!
I'm not sure how it compares to bevy's transform propagation system in performance, but I feel like it …