16
SFML projects / Re: Feather Kit - A C++ Game Framework
« on: February 10, 2014, 04:26:55 am »
Okay so this is a bit of a late reply, but hey
It's seems like the use case you have in mind is actually quite a bit simpler than that. You just want the ability to load values for known attributes from a JSON file. That's trivial to achieve even with a fully type based system and it takes no more effort than for the new system you've described (i.e. no need for an 'EditorAttribute' or the like). Because this:
Provides the entity manager with both the static type of the attribute, and its string identifier. This is exactly the same information that a type based system would need in order for it to satisfy the use case. The code in my earlier post shows what that could look like:
http://en.sfml-dev.org/forums/index.php?topic=14193.msg100008#msg100008
Perhaps std::set<std::string> would makes more sense here?
This could just be a matter of taste, but I feel like it would become quite tedious having to provide the 'default-setter' function for the same type, whenever that type is registered in the entity factory as an attribute (e.g. both position and velocity being vec2, and having to provide same setter function for both, individually). That being in addition to having to register both with the entity manager. It would be nice if, for example, the 'glm::vec2' type is registered along with its 'default-setter', not as an attribute, but as a primitive which can then be mapped to a name to form a new attribute.
I realise that it's probably not entirely obvious what I mean, so I went ahead and wrote a reference implementation to demonstrate the idea (which you're free to ignore ):
http://codepad.org/kBtU0puv
As an example of usage:
There's a few things you might find interesting about this:
- Note that the user no longer explicitly registers anything with the entity manager itself. The entity factory handles all that under the covers (i.e. there is no registration code missing here, this is a complete example).
- The 'default-setter' function has been reduced to a simple parser; it doesn't concern itself with entities or managers or even attributes. It simply parses the arguments and returns an object of the appropriate type (which the entity factory will automatically deduce).
- Entity templates are parsed as soon as they're added to the system and stored as what I call 'proto entities'. This saves the system from having to re-parse (as in your current system) the strings of a template every time one is instantiated. The point here is that one could add sophisticated error handling to the parser function (previously 'default-setter') and not have to worry about any performance overhead since it will only ever been invoked once per template, rather than once per instantiation.
- This system satisfies the use case quoted at the top of this post; One could create entirely new attributes inside JSON files (in addition to simply being able to provide the values of known attributes).
- The code requires no modification to your existing EntityManager, except that it assumes the 'createEntity' function accepts an std::set<std::string>.
Quote from: therocode
Quote from: Lee ROkay, I think I understand your use case better now. You actively want the ability to load meaningless junk. That is, user defined attributes for which the compiled executable has no specialized code. Then, while waiting for a programmer to implement their logic, a level designer could get on with placing objects containing said attributes. If that's the case, then a fully typed entity system could still work. You'd simply define a 'EditorAttribute' which would suck up all unknown attributes. The editor system would look for object with said EditorAttribute and display it's contents as if they were first class attributes. If, on the other hand, the idea is to have the logic be implemented in script files, while the above idea would still work (e.g. through a ScriptVars arrtibute), you would probably be better off back with the old string based system (depending on the amount of data that gets stuffed into the ScriptVars attributes).
[...]
Yes, that is right. Such use cases is what I have in mind. I am not entiry sure what you mean with a 'EditorAttribute' which would suck up all unknown attributes [...]
It's seems like the use case you have in mind is actually quite a bit simpler than that. You just want the ability to load values for known attributes from a JSON file. That's trivial to achieve even with a fully type based system and it takes no more effort than for the new system you've described (i.e. no need for an 'EditorAttribute' or the like). Because this:
Quote from: therocode
The entity manager has such a function:eManager.registerAttribute<int>("Health");
Provides the entity manager with both the static type of the attribute, and its string identifier. This is exactly the same information that a type based system would need in order for it to satisfy the use case. The code in my earlier post shows what that could look like:
http://en.sfml-dev.org/forums/index.php?topic=14193.msg100008#msg100008
Quote from: therocode
You then create entities like so:eManager.registerAttribute<int>("Health");
eManager.registerAttribute<float>("Weight");
WeakEntityPtr entity1 = eManager.createEntity({"Health", "Weight"});
WeakEntityPtr entity2 = eManager.createEntity({"Health"});
WeakEntityPtr entity3 = eManager.createEntity({"Weight"});
The parameter is an std::vector<std::string>. [...]
Perhaps std::set<std::string> would makes more sense here?
Quote from: therocode
Example on how to register a default-setter for a glm::vec2 for position:entityFactory.registerDefaultSetter("position",
[] (const std::string attribute&, const std::vector<std::string>& arguments, fea::WeakEntityPtr entity) {
entity.lock()->setAttribute<glm::vec2>(attribute, glm::vec2(std::stof(arguments[0]), std::stof(arguments[1])));
});
This could just be a matter of taste, but I feel like it would become quite tedious having to provide the 'default-setter' function for the same type, whenever that type is registered in the entity factory as an attribute (e.g. both position and velocity being vec2, and having to provide same setter function for both, individually). That being in addition to having to register both with the entity manager. It would be nice if, for example, the 'glm::vec2' type is registered along with its 'default-setter', not as an attribute, but as a primitive which can then be mapped to a name to form a new attribute.
I realise that it's probably not entirely obvious what I mean, so I went ahead and wrote a reference implementation to demonstrate the idea (which you're free to ignore ):
http://codepad.org/kBtU0puv
As an example of usage:
int main()
{
fea::EntityManager manager;
fea::EntityFactory producer(manager);
producer.addPrimitive("vec2", [](const fea::Params& params) {
return glm::vec2{ std::stof(params[0]), std::stof(params[1]) };
});
producer.map("position", "vec2");
producer.map("velocity", "vec2");
fea::Template moveable = {
{ "position", { "0.0, 0.0" } },
{ "velocity", { "0.0, 0.0" } }
};
producer.addTemplate("moveable", moveable);
auto entity = producer.instantiate("moveable").lock();
glm::vec2 position = entity.getAttribute<glm::vec2>("position");
glm::vec2 velocity = entity.getAttribute<glm::vec2>("velocity");
return 0;
}
{
fea::EntityManager manager;
fea::EntityFactory producer(manager);
producer.addPrimitive("vec2", [](const fea::Params& params) {
return glm::vec2{ std::stof(params[0]), std::stof(params[1]) };
});
producer.map("position", "vec2");
producer.map("velocity", "vec2");
fea::Template moveable = {
{ "position", { "0.0, 0.0" } },
{ "velocity", { "0.0, 0.0" } }
};
producer.addTemplate("moveable", moveable);
auto entity = producer.instantiate("moveable").lock();
glm::vec2 position = entity.getAttribute<glm::vec2>("position");
glm::vec2 velocity = entity.getAttribute<glm::vec2>("velocity");
return 0;
}
There's a few things you might find interesting about this:
- Note that the user no longer explicitly registers anything with the entity manager itself. The entity factory handles all that under the covers (i.e. there is no registration code missing here, this is a complete example).
- The 'default-setter' function has been reduced to a simple parser; it doesn't concern itself with entities or managers or even attributes. It simply parses the arguments and returns an object of the appropriate type (which the entity factory will automatically deduce).
- Entity templates are parsed as soon as they're added to the system and stored as what I call 'proto entities'. This saves the system from having to re-parse (as in your current system) the strings of a template every time one is instantiated. The point here is that one could add sophisticated error handling to the parser function (previously 'default-setter') and not have to worry about any performance overhead since it will only ever been invoked once per template, rather than once per instantiation.
- This system satisfies the use case quoted at the top of this post; One could create entirely new attributes inside JSON files (in addition to simply being able to provide the values of known attributes).
- The code requires no modification to your existing EntityManager, except that it assumes the 'createEntity' function accepts an std::set<std::string>.