yelnats321 / entityplus Goto Github PK
View Code? Open in Web Editor NEWA C++14 Entity Component System
License: Boost Software License 1.0
A C++14 Entity Component System
License: Boost Software License 1.0
Although the library was designed with a no exception mode in mind, all the exceptions are non recoverable for the most part. Have to figure out how to support a no exception environment and also write tests for such an environment (as currently all the tests are based on an exception environment).
Consider adding serialization support
Currently for_each
is much faster than get_entities
for traversing some set of components because it uses iterators under the hood. However, this means that removing components or creating or deleting entities will break it. It may be possible to modify the control_block_t
to pass this information to the manager.
Destroying an entity or deleting its components make the most sense to add support for. Additionally, adding components to the current entity should already be supported, because it won't invalidate any iterators being used.
Adding support for creating entities withing for_each
in my opinion has little usage and adding support for it would be hard and possibly limiting (how many entities should be created?)
Remove visual studio specific files and add cmake support instead.
Benchmark current template metaprogramming implementation and try to reduce compilation times if possible. Saw some things on this blog that may be useful: http://odinthenerd.blogspot.com/2017/03/start-simple-with-conditional-why.html. Will test out templight and metabench, research other tools.
I am currently in the process of rewriting EntityPlus! One of the issues is how to transition this repository to the new version. As this is a ground-up rewrite, I am not sure what the correct approach is. I also want to push to the repo when I have a working, mostly stable interface, and it will probably be a single commit that entirely changes the code. It may not be as fast as intended, but featureful enough to use and additional optimization will be added without altering the interface (ideally).
I am not sure if this is the best way to do this. If you have any suggestions on how to transition to this new rewritten version, please let me know!
If you have any features you are interested in, check out the other issues, especially #22 and see if they are covered there. If not, please let me know.
Currently entities employ reference semantics rather than value semantics, which is the less intuitive of the two. This means that passing in a const entity
to a function does not guarantee that the entity won't be modified (since you can just copy it into a non const entity and modify it there).
There are other problems like sync()
not being able to be called from a const entity and other little things that just feel off.
The options going forward are either to keep things as is or to split entity into two classes, a const entity and a non-const one, where the manager will return one or the other based on context. Probably the best approach to this but might feel unintuitive and hard to follow.
Currently you are prohibited from storing entities. Rather, altering an entity will invalidate any previous reference to it. This restriction is made so that you can have caching on the entity side for has_(tag/component)
. Although the case can be made that the restriction is not worth it for anO(1) has_component
, the main feature of tags is their low overhead and so removing O(1) has_tag
is detrimental to that, making them obsolete in terms of performance.
It seems useful for some applications to be able to store entities rather than having to query them each time. Without event integration this is not very useful, but with it the want for storeable entities is increased.
There are two options that I see for this feature. Either modify entity
so that it has an sync()
function, which will sync it up to its actual values inside entity_manager
. The issue here is that I am not sure how to deal with the case where a sync reveals that the entity has been deleted. I can add a bool indicating this state but it just adds more complications for state checking and the like. However this can be alleviated by sync()
returning an indication of if the entity is valid post sync.
The other option is to have another type that we would call an entity_handle
. This would be a barebones storeable type that underneath only holds a pointer to its manager and an ID. You can get an entity_handle
from an entity
via get_handle()
and also convert an entity_handle
to an entity
via get_entity()
. The same problem exists as with the syncing if the entity was deleted in the meantime. This would probably mean that get_entity()
will have to return a pointer or an optional, but without library support I am very hesitant with the latter option.
On the one hand the second option conveys more information because we know that entity
is temporary, entity_handle
is not. On the other hand having additional types is annoying and complicates code needlessly. I am leaning more towards the first option, especially because the interface can be much nicer.
Had an idea for a while to specify the underlying storage of a component (for example an unordered_map
may be more performant for some data sets). This would be done by supplying a component_list
with an unordered<T>
as a template argument, which seems like a very clean way of doing it, but might be hard to implement.
On a similar vein, add a not
option for get_entities
and for_each
, so one can ask for specific tags/components and also specify which tags/components not to have. So something like for_each<Position, Player, not<Inactive>>
would fetch every player+position that does not have the Inactive
tag. Not restricted to tags, could be components too.
Add unit testing (probably with catch
since it's header only) as well as metaprogram testing (for all the metaprogramming stuff).
As it stands for_each will iterate over the smallest component/tag provided, however this means that if the intersection of the provided components/tags is very small compared to the smallest component/tags, we are doing a lot of unnecessary work. It may be helpful for the user to provide groups of entities they plan on iterating over in the future to reduce the cost of this iteration.
As an example, say there are 50 entities, 20 of which have only tag A, 25 which have only tag B, and 5 which have both A and B. If we did for_each<A, B>()
we would iterate over 20 entities (the ones with tag A) and then compare the bit signatures of the entities to match AB. Instead the user may tell us that they will use a grouping of AB regularly and so now we have a separate list of all entities with AB that we update with changes, and now we only have to iterate over the 5 AB entities.
This project is currently in a bit of a limbo state. The version for all intents and purposes should be at 2.0, however I am afraid there are certain features/changes that are not backwards compatible that I want to make before I release, but have not had the time to sit down and examine the code thoroughly to make the change. I am currently very busy with school work, and I don't see it slowing down for at least a month or two.
With all this being said, I want to wait until GCC/Clang/MSVC support C++17, or at least most of the nice features that are coming with that standard version. I think that when that happens, I will rewrite a lot of the code to use these features and support C++17 only. This would mean nicer error codes and a more simplifications in the code. However, this may reduce the userbase of the library.
To combat this standard version bump, I will rewrite the code alongside a 2.0 release. That way, any features (or breaking changes) that occur as a natural result of the rewrite can be also ported into the 2.0 release. The rewrite will force me into the state of mind that I need to be in to further extend and enhance the library, as I have not touched the code for some time now. In effect, there will be a 2.0 and 3.0 release simultaneously, with the only difference between the two being compiler/standard version.
I don't know if a standard version increase is a deal breaker for any users (or potential users), so I would like to hear your input too.
Hello,
I'm trying to use your library:
cmake .
-- Building for: Visual Studio 14 2015
-- The C compiler identification is MSVC 19.0.23026.0
-- The CXX compiler identification is MSVC 19.0.23026.0
[...]
When building the solution from Visual Studio 2015, I get the following error message:
\entityplus\metafunctions.h(26): error C2737: 'entityplus::meta::delay_v': 'constexpr' object must be initialized
Thanks
Remove or incorporate that part of boost with the library. Look for a different container library instead.
Currently there is no pretty way to take an entity as a parameter for the library user, since the fully qualified type is in detail
. Either expose entity_t
from entity_manager
so the user can typedef
it, or somehow inherit from a base class that the user can use it in parameters.
The issue with the first approach is that the user will need to expose typedef
to any function that wants to take an entity
, but this is no large burden as they can just write their own header #include
ing the library and defining the typedef
.
The second approach requires a whole new set (or an exposure) of the functions that operate on an entity
inside entity_manager
(the ones that take entity
as a param) and will need to be kind of tricky about making sure the type information will match up. It will also introduce a new way to operate on entity
s, which is something I'd like to avoid.
First approach easiest to implement for now, but may be changed later.
Include licensing for the code (probably going to use boost).
Add events for adding/removing components/tags and creating/deleting an entity. This feature should be optional (by providing an event_manager
to the entity_manager
ctor or by specifying one via a function).
Can't change case of files without removing/readding them, will do that around v1.0
Add more examples! In the code so people can just expand on those and see what works and what doesn't, as well as lead by example with idiomatic code.
Will mesh well with cmake I think.
So I am currently in the process of a pretty large rewrite, meaning a lot of old code will be removed and replaced with (hopefully) better code.
The main key points I am trying to hit are:
I will also be maintaining the current feature set, just possibly in a slightly more cohesive way. Some functions may be renamed and the status quo may change a bit, but I aim to not reduce the feature set (and hopefully add to it in a meaningful way).
I don't have a good understanding of their purpose and how to use them in practice.
In add_component
due to an MSVC bug the lambda is not generic and therefore all the types can be deduced when the function is called, before the lambda is invoked. This means that the pretty eval_if
errors are swamped by other errors. One way to fix it is to change the entity manager's add_component
to take a tuple of perfectly forwarded args instead, so that the generic lambda does not rely on the parameter pack and can once again be generic.
Versioning has been a little lacking. There is only one release (v1.0) even though in reality there have been other releases (minor and patches, and also a couple breaking ones). Adding entity grouping will be technically a release of v.2.0.0 and then afterwards I will try to follow semantic versioning as best as I can. The git will still have commits that don't necessarily correlate to a version, but when I feel satisfied with a feature I will bump up the minor version, for bug fixes the patch version, and also for breaking changes the major. I don't foresee many breaking changes with the current feature set, but that may of course change.
Curious if you have any recommendations on a good way to handle references between entities, and how to store and access those relationships. In particular, we're looking at this in context of entity inventories.
Originally what we kind of hoped was going to work was a component along the lines of this:
struct Inventory {
int capacity;
std::vector<entity_t> items;
};
and then use a system and events to manage adding/removing items to a particular entity. This fell flat on its face because entity_t
is defined after the components are.
One easy little change we found out we could make was to expose entityplus::detail::entity_id_t;
and then using that in place of entity_t
above in the vector. We kept going to see how it plays out, and what we ended up with was this helper function to convert the entityplus::detail::entity_id_t;
into entity_t
:
template <typename ...CompsAndTags>
inline std::unique_ptr<entity_t> find_entity_by_id(entity_manager_t* manager, entityplus::detail::entity_id_t id)
{
for (entity_t entity : manager->get_entities<CompsAndTags...>())
{
if (entity.id == id)
{
return std::make_unique<entity_t>(entity);
}
}
return NULL;
};
and it made for fairly enjoyable access anywhere you wanted to use the entities items
auto inventory = player.get_component<Components::Inventory>();
for (auto iter : inventory.items)
{
auto item = find_entity_by_id(this->entity_manager, iter.second);
// now we have a pointer to work with
item->get_component<Components::Identity>();
// off we go using the entity
}
We actually ran with that for a little bit and it's been alright, but it feels dirty given that we have to modify entityplus to expose that ID. We're lookin to switch this up, but the best next alternative we can think of is having the Inventory component know nothing about the items and go through our InventorySystem which could maintain a mapping of entities with inventories to the list of item entities, then have a function like std::vector<entity_t> InventorySystem::get_items_for_entity(entity_t)
for accessing the items.
The reason we don't like that approach as much is because we lose the ability to fully describe our entity by simply looking at its components. With that approach our component tells us how many items might be in the inventory, but we need to communicate with a system to know what items those actually are.
So I guess what I'm asking is if you have any ideas that might allow for referencing entities from within a component. Or perhaps you know of an entirely different approach that maybe we didn't think of. I did notice you mentioned in #19 kind of this scenario we're talking about, but we're failing to understand how the tag approach would work when we have N entities that could have inventories (N tags?).
Appreciate any thoughts.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.