-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Component/System/Resource Grouping #155
Comments
Thanks for writing all this up.
Given that the
Looking at the pub trait Serialize {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer;
} So a trait object for pub trait AnySerialize {
fn serialize(&self, ser: Box<AnySerializer>) -> Result<Box<Any>, Box<Error>>;
}
impl<T> AnySerialize for T
where T: Serialize
{
fn serialize(&self, ser: Box<AnySerializer>) -> Result<Box<Any>, Box<Error>> {
<T as Serialize>::serialize(self, ser).map(Box::new).map_err(Box::new)
}
} (It's of course obvious that your idea is much better, just wanted to note that it is possible)
Fully agree with that point.
I cannot really follow what you need this for or what it's really about. Could you please explain when you would use that?
Would recommend renaming It's a pretty complex topic, so I'm not sure about all the details yet. I'll think a bit more about this and come back here later. |
@Aceeri great detail, thanks for the issue! I don't buy the ergonomics argument. This is a one-time operations that engines hide from the users anyway: there is no reason why Amethyst, for example, should require users to register all the components manually. Right now they (as in - Amethyst devs) at least have flexibility for optionally registering some components/systems, where As for serialization, I haven't really looked into it. How difficult/error prone it is to serialize and deserialize the world today? |
@torkleyy That Expanding usage just means allowing people who have access to the ComponentGroups to call a method with the components as a generic. The fundamental part of this is just removing the boilerplate of: method::<Comp1>();
method::<Comp2>();
method::<Comp3>();
... which that macro allows for even when you don't have access to the procedural macro implementation. Since right now the way I do it is just: quote! {
#(
method::<#component>();
)*
}; in simplest terms. The component group methods are mainly just for debugging purposes and aren't entirely necessary. Doesn't make much sense to make them non-static methods either since you shouldn't really be constructing a component group rather than just using it as a generic argument. @kvark I'm not entirely sure what you mean by optional components, but you can always just group together components into sections like Ignoring components in a group can also be added to component groups: Subgroup ignoring components.: #[derive(ComponentGroup)]
struct ParentGroup {
comp1: Comp1,]
#[group(subgroup)]
#[group(ignore(Comp2))]
subgroup: Subgroup,
}
#[derive(ComponentGroup)]
struct Subgroup {
comp2: Comp2,
comp3: Comp3,
} Serialization/deserialization of the world is kind of tricky because it is very context specific. This ties back to the entity serialization/deserialization, since a component can depend on another entity. If a component does have reference to another entity then it doesn't make much sense to deserialize/serialize that component's storage alone. |
Is this still relevant with |
No, don't think so. It would be overly complicated for little use. |
Grouping systems together through a procedural macro that would allow calling a method with each type of component/system/resource. This would allow for static dispatch of serialization or anything dealing with groups of components, systems, or resources (like configurations).
Reasons
Ergonomic registering of components, systems, and resources
While registering components by hand takes dozens of lines depending on the size of the project and its dependencies, registering a group would take a single line of code:
Modularization of dependencies
External users of specs will likely not be using barebones specs and only their own components, especially in the future. For example, Amethyst provides
Transform
,Renderable
,Parent
, and more. Currently either the third party would have to make a special registering method to register all their components together or leave it up to the user to register those themselves, which can be a very tedious task when given a large amount of components.Similarly without a component group, serialization would require manually plucking component storages and serializing them manually (as it is currently not possible to get a trait object of a
Serialize/Serializer/Deserialize/Deserializer
fromserde
.It is very likely that a third party would make a macro for similar tasks to avoid a lot of boilerplate and centralizing it to a singular macro would be extremely beneficial rather than having the user juggle different implementations of macros/methods for registering and serialization.
Implementation Details
Currently the groups are just defined by two traits:
The implementation for the groups are defined by a procedural macro. For example:
Expanding usage of the groups for external implementations
Currently the only way to add methods that take in components is to modify the procedural macro. I have an idea to generate an implementation for a trait like
GroupLocals
orGroupSubgroups
.So an implementation for some group with 3 components,
Comp1
,Comp2
, ... might look like:This would allow a macro of the form:
Feel free to ask any questions or ideas for improvements.
The text was updated successfully, but these errors were encountered: