For inspiration, I recommend looking at bevy. It does a great job with efficiently querying for components in a type-safe way. From their book, this is an example of what a system looks like:
fn greet_people(query: Query<&Name, With<Person>>) {
for name in &query {
println!("hello {}!", name.0);
}
}
Name
and Person
are both components.
You might think that a scripting language for ECS-based systems is pointless. And in the majority of cases you would be right.
This comes as a surprise to me. I think scripting capabilities would be incredibly useful, especially if you can hot reload the scripts during development time. For core engine functionality, this might be less relevant, but for the gameplay itself it could be really nice.
You may wonder why the entity (usually some sort of ID) is part of its component. It wouldn't be too convenient if we had to query and pass around entities manually.
This one I'm curious about. How common is the case when you need to operate with the entity directly, and is it worth the cost of duplicating the entity ID onto each component? In bevy's case, you can query for the entity ID like you would a component using Entity
, which I've found to be easy enough to do.
function click_handler(boundary: BoundaryComponent) { -- This creates a new bubble component for the entity stored in boundary. boundary with new BubbleComponent("Hi at {boundary.x}, {boundary.y}!"); -- From here on we can use boundary with functions/methods/members -- registered for both components, because the type system knows. boundary.show_on_top(); }
Does this mean that each entity can have any number of components of a particular type in this implementation? Would each component also need its own ID to distinguish between two components of one type on the same entity?
Another option here is that instead of creating a BubbleComponent
that's part of the same entity as the BoundaryComponent
, it might make more sense to create a new entity that's at the same position as the BoundaryComponent
's entity, possibly using some kind of relative transformation to keep the bubble at the same position as the boundary.
The next example which seems to create two bubbles on the same entity is just as confusing to me. If I were to query for those bubbles, would I iterate over each of those bubbles in separate iterations, but all other components that are part of this query are the same? What about in situations where two or more component types have multiple instances per entity, like if one entity had two BubbleComponent
s and two BoundaryComponent
s? Would I iterate over the permutations of those entities, so (bubble 0, boundary 0)
, (bubble 0, boundary 1)
, (bubble 1, boundary 0)
, (bubble 1, boundary 1)
?
I like the ideas presented in the article otherwise. I vaguely remember TypeScript having some sort of reflection support that Angular took advantage of or something. I wonder if that can be used to create a scriptable ECS like proposed in this article?