• Advertisement
Sign in to follow this  

Game Engine Questions (ECS)

This topic is 556 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey Everyone, Iv been trying to create an engine based around the ECS architecture (first attempted so still trying to find my group). Its been going quite well but  there are around 3 problems/questions that I have come across and was wondering if I could have some advice on them.

 

1. The current system that i have is that the entity stores a vector of components (each component and entity has a unique ID the component one being a static int) and then for each system, i get the component I need by calling an entity function with the ID of the component I want. The function just searches through the list for the component with the requested ID and returns it.

 

My question is, is there a better/more efficient way of doing this. i have seen some people having a component holder with a vector of each component and the components hold a reference to its entity's ID, is this a better system?

 

2. My second question more relates to passing data to the systems. Currently each of my systems gets passes delta time, a bool for go that makes the main loop run (lazy for now but can be changed),  and a class called world which has the entity manager so that system can ask to create new entities (like bullets) and a spawn class that contains positions for spawns (in relation to the game i'm making with it). 

 

I already feel that this approach is not really a good one but i don't know the best way to go about changing it. When it comes to things like the input manager and the "world" class, I could pass them to the systems in the constructor but other then this i don't see a way of making this work without global.

 

3. The last question i have is based around an octtree im using for collision. For my systems i have a stored link to the root node in my collision system and a component for each entity (that will have collision checks) has a link to the node the entity is in. The octtree has knowledge of the entities and each node has a vector of them (that are inside that node). each entity first makes sure its in the correct node (with its aabb) and then after that, I currently call root->update() in my collisionchecksystem. 

 

The octtree node then checks all the collisions possible and then calls update for the other nodes (recursively) the octtree gets the obb and aabb components from the entity to do collision checking. At this point im really not sure if this is a good system. I don't think its best to do this bit recursively so it might be much better to do the collision code in the collision check system (would be a lot better for the logic after collision as well e.g. damage taken) and just get all other entries we need to check collision for from the octtree (or maybe just their id's for efficiency) but my problem with this approach is I don't see a good way of making sure the same 2 entities don't have 2 collision instances with each other (as i would be requesting all entities that could collide with the entity in question).

 

I would very much appreciate any advice on some of these questions. ATM i do have most of these problems working but I want to try to build a good foundation and understanding of how to go about these problems before i get to far ahead in the engine. 

 

Thanks for your time :).

Share this post


Link to post
Share on other sites
Advertisement

1. There was a related question some time ago about instanciating components via an Add-Method into an existing object so you might look in here http://www.gamedev.net/topic/682291-c-angle-bracket-function/ to have an idea how other people are doing this.

 

2. I would avoid passing too much data into an update loop every frame. The approach I do here is mixing OOP and global functions together whenever needed. If something acts as if it would be pure static behavior then I put it into an own sub-namespace and in there declare the functions directly. So all I do need to do is calling Time::TimeFrame() on the beginning of each frame to update the internal storage and then use Time::DeltaTime() for getting the time delta correctly in every entity's update function. Same for any manager or managing class

Share this post


Link to post
Share on other sites

I've explored the ECS route for a while now, but haven't yet completed anything substantial - just a tip, my main bottleneck was trying to structure really low-level concepts within the ECS, I even tried to have VertexBufferComponents at one point, that was a dark period in my life. I now aim to have an engine running without the ECS, and the ECS is a high level layer over the concepts in the game engine. Now I have a SpriteComponent and either ImageComponent/ColorComponent, which is still relatively low-level, but works for now, trying to minimize that further.

 

1.

I think the most important concept to understand in ECS, is that an Entity isn't an object itself, conceptually. It's main purpose is to just be an ID that ties groups of components together. How this expresses itself in my design, is that much like the systems/entities concept, which is the main/popular attraction of ECS for most people, the same inversion of control applies to entities and components.

I have a ComponentManager that has arrays of all component types (I use the class name itself, via reflection (not C++, but this still applies)), and each of those arrays are sparse arrays, where the component for an entity can be accessed via the entity id. The result is a 2d arrays, though I have a specific manager for each component type as well. In the simplest form, component access is:

`var componentA = types[componentA_ID][entity_ID];`

where componentA_ID is the id assigned to the ComponentA type, and entity_ID is self explanatory.

 

At this point, entities are purely integers, which is the case in my engine. They aren't objects. I have a helper class that wraps an entity id, and gives convenient component setters, but this is just an optional utility. I don't think "an entity OWNS components", but "there are components, and they are assigned to the same ID". Maybe that can be phrased better.

 

This way you have more control over component storage, rather than them being tossed around in Entity instances. Also, this gives you a centralized entry point into your components. In my system, View objects "attach" to the component containers directly, which ever ones they need:

`var view = new View<ComponentA, ComponentB>();`

That view would go to the component manager, and get references to the containers responsible for the ComponentA and ComponentB types. Whenever a ComponentA or ComponentB is set on an entity, the view is notified of the change, and it checks the entity if it's valid (in this case, if it has both ComponentA and B assigned to it). This way, setting ComponentC on an entity has no effect on the view, it doesn't care.This combined with keeping a bitflag for each entity of what components it has, means any changes to entities get propogated immediately to views, and only ones that care, for minimal cost.

 

To sum up my gibberish, component access is direct, no looping or anything, via the component_type_ID and entity_ID. (entity == entity_ID in my case).

Views selectively subscribe only to relevant component change events to keep a list of valid entities. (so, also no looping through entities at runtime to get the relevant ones)

 

2.

Sort of relevant to the above, the concept of ECS for me is really just EC, and the S is an optional helper for structuring functionality. Technically, systems don't need to be actual objects, they just refer to pieces of logic that process entities (you could have an ECS-based game written totally in your main function, but that would be messy).

 

Anywho, I do have a system API to keep track of things. They extend a base class, and have 5 callbacks:

onInitialize, onActivate, onUpdate, onDeactivate, onDispose

The system_manager object gets passed to onInitialize, onActivate, onDeactivate, and onDispose, and the delta_time gets passed to onUpdate.

The initialize/activate things are sort of specific, but initialize gets called when the system is added to the system_manager, and activate gets called when the system will actually start running. This is important, because my system_manager handles system dependencies - when declaring your system, you call the super constructor, passing in a list of system classes it requires. When the system_manager has all required systems added to it, then it will call the activate callback, at which point your system can get references to the required systems through the system_manager...did that make sense?

 

Generally my systems have a bunch of the above View objects, and they get initialized in the onInitialize callback, so I don't need direct references to the entity_manager (which is accessible through the system_manager). (BTW - the entity_manager holds a reference to the component_manager, so essentially: system_manager->entity_manager->component_manager)

 

This way, the system purely cares about itself, codewise/internally, but gets the references to the outside world and other systems through the system_manager, passed to the init/activate callbacks. If a system doesn't need the references during its update callback, then it doesn't even store a reference.

 

Not sure about your 3rd question, I haven't personally used octtrees in-depth.

 

I hope my splurge helps you...it's basically a raw braindump. I am by no means an expert, but I've gotten to this structure over several iterations of trying to make something with an ECS - the direct system to system communication was a big one, earlier I tried to do a pure hardcore approach where ALL inter-system communication should happen via components.

 

P.

Share this post


Link to post
Share on other sites

@[member='PSvils'], (on question 2) So u pass what is needed by the systems to its constructor so its just stores locally. I did think that would prob be the best method.

 

Iv never seen systems dependent on each other, any chance you could give an example of when this might be used.

 

Thanks for the post was very helpful :)

Share this post


Link to post
Share on other sites

By what is needed, I meant it passes a list of class references to its super constructor.

 

The system manager uses this list when you add the system to the manager, and checks to see if all systems in the list already exist in the system manager. If yes, it activates the system, if not, it doesn't. It rescans this every time you add/remove a system to the manager.

 

The system itself can only access the other systems through the system manager.

 

Systems being dependent on each other can happen for different reasons. A bad reason (one which I use though) is performance. Sometimes direct access to system specific data is faster, and easier. An example would be a PhysicsSystem and a BulletSystem. The BulletSystem would require the PhysicsSystem, and get a reference to it when activated. That way, it can directly do raycasts on the physics space within the PhysicsSystem, as opposed to somehow raycasting through creating entities and components etc.

 

It's both faster, and probably more understandable / debuggable.

Edited by PSvils

Share this post


Link to post
Share on other sites

@[member='PSvils'], so im guessing each of your systems has a reference to the system manager? 

Share this post


Link to post
Share on other sites

"The system_manager object gets passed to onInitialize, onActivate, onDeactivate, and onDispose"

 

:) So yes, systems get references to the system manager.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement