🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

GameObject/Entity Managment

Started by
5 comments, last by Juliean 4 years, 1 month ago

So i have been struggling with this for many years and haven't seen a solution that provides the right complexity (implementation/extension) and performance when it comes to managing the objects in a game scene. As far as i have seen there are 3 main approaches:

OOD / Components:
-Base Object (Actor), derive from this to make your new types: Character, NPC, Orc, Weapon…
-Derived type adds components (Composition over inheritance): MeshRendererComp, DialogueComp, PlayerInputComp etc.
-Objects are stored in a map or linked list? and iterated over in a Scene with Tick called etc.

Pros:
-Easy too implement/Think about
-Easy too access components with simple pointers

Cons:
-Memory is not contiguous
-Cache issues
-Raw pointers dangerous

Component-Entity Architecture:
-GameObject Class is a container for components
-Components added dynamically at runtime/removed usually via a Map on the GameObject
-Objects are stored in a map or linked list

Pro:
-Easy To implement
-Flexible, objects can be composed out of any components even at runtime.

Cons:
-Terrible memory layout, components are scattered, worse than OOD?
-Accessing components is now an indirection via a map look up.
-Cache misses worse than OOD.

Entity Component System (Sparse Set):
-GameObject is now just an EntityID, no data
-Components stored in component pools (Densly packed) and accessed via a Map(EntityID→Index)
-Systems contain logic and operate on components

-Pro:
-Components are grouped into contiguous pools
-Entities can be copied easily.
-ID's/Handles can resolve dangling and allow memory relocation

Cons:
-Everything is now an indirection.
-Each system will NOT loop over linear memory as it needs too “fetch” via potentially multiple levels of indirection the components that it desires to operate on?
-Harder too think about from a gameplay programming perspective
-Seems like its forced to be global everywhere i.e g_World→GetComponent<Transform>(entityID)

Can anyone shed light on which approach they find the most useful in terms of actually getting stuff done and having acceptable performance overhead?

Thanks.

Advertisement

karlmar said:
Can anyone shed light on which approach they find the most useful in terms of actually getting stuff done and having acceptable performance overhead?

Personally, I use all three of them in my game-engine?

ECS was my basis, and is used for system-centric implementations. Meaning collision, rendering are all handled via components and systems. So you kind of compose your entities of components to interface with the core engine.

Then I have a visual-scripting system that supports inheritance. I use that for building basic gameplay features, like enemies, players, items, etc… . For those, I find inheritance/OOP to be way more practical than ECS or consorts. Even if you don't want your code to support inheritance, you'll have some things that inherit properties of some base in any case (see Unitys new Prefab workflow, which is just glorified inheritance on an asset-level).

However, there are certain issues with plain inheritance, like when I want a certain feature to be usable with different kinds of NPCs. Thats why I also added a system like Unity, where I can have dynamic Components that I can add to entities, ie. a Vendor-Behaviour.

------

Now, of course having all three systems is an added complexity, which IMHO pays off in being able to choose whats best for each individual situations. My base-engine is very fast via ECS, but I also have the convenience of just building gameplay-systems with virtual functions and complex behaviours that can be reused.
If you want to just start with one system, I would consider this: Are you going to have complex gameplay with lots of unique features (like a spell-system where there are 30 spells that all needs “scripted” behaviours)? Then choose Component-Entity or OOP.
If you have mostly system-driven gameplay (think something like Hexagon, Pacman etc) then ECS, or some variants might be an option.
And lastly, there are also options for composition-based approaches that are not purely ECS, but other people might be able tell you more about it.

Juliean said:
ECS was my basis, and is used for system-centric implementations

Yes i thought about this a while ago but never heard of any one doint it, would makes scene management easier as traditionally alot of engines end up with scenes that maintain lists of cameras and renderables when ever an Entity is added it is scanned for such components. Where with ECS you have them all in packed data arrays ready to be iterated on.

So are you basically just doing the plain Unity Component set up but using the Entity GUID internally to index into internal pools for the systems? One of my main problems with ECS is being able to fetch components during the systems iteration. For instance if we have a basic physics system it needs both RigidBody and Transform components. A lot of examples will do the following:

for(entites that match the systems signatrue)
{
	RigiBody& rigidBody = gWorld.GetComponent<RigidBody>(entity);
	Transform& transform = gWorld.GetComponent<Transform>(entity);

	transform.position += rigidBody.velocity * dt;
	rigidBody.velocity += rigidBody.gravity.force * dt;
}

That results in 4 look ups:
1: gWorld looks up RigiBodyPool (ComponentTypeID)
2: gWorld look up TransformPool (ComponentTypeID)
3: RigidBodyPool finds component for entityID
4: TransformPool finds component for entityID

The Pool look up can be resolved by explicitly passing the pools into the systems, this also shows code flow / dependencies explicitly:

m_BasicPhysics.Initialize(gWorld.GetComponentPool<Transform>(), gWorld.GetComponentPool<RigidBody>)

However were still left with the 2 indirections but im guessing that doesnt affect performance too badly as it seems to be commonly used.

karlmar said:
So are you basically just doing the plain Unity Component set up but using the Entity GUID internally to index into internal pools for the systems? One of my main problems with ECS is being able to fetch components during the systems iteration. For instance if we have a basic physics system it needs both RigidBody and Transform components. A lot of examples will do the following:

I've gone through multiple iterations of how to access components. At first, entities had a vector/array of all components that stored in itself, and components where accessed from there.

I later changed components to be pooled and removed the store from the entities itself, meaning that yes there will be a functionality for looking up components based on some entity UID. However this is mostly for script-calls and editor-.functionality (operating on single entities).

For system/multi-entity-operations, I introduced a component cache. The systems will not iterate over entities, but instead request a set components:

for (auto entity : EntitiesWithComponents<Animation, Sprite>()) // is not en Entity&, but an CacheIterator<Animation, Sprite>

{

auto& animation = entity.Get<Animation>();

auto& sprite = entity.Get<Sprite>();

Fuck this forums new stupid editor, still cannot copy my code properly…

Anyways, internally this will create a cache-structure, which on creation will lookup all required components and put them into a tightly-packed std::vector<std::pair<Animation&, Sprite&>> (with some template magic). When entities or components are added, all caches are updated accordingly (this is highly optimized now but you might just as well discard cache-buckets entirely like I did before).
This has proven to be extremely fast, as everything is right in place for when systems needs it, in the order that entities are actually processed. There are some gotachs, like having to make sure that caches are not changed while systems are running, but I belive the only way to go faster than that is if a system holds an array of its own components (which is a lot harder when components are shared between systems).

Okay sounds like a reasonable approach, so instead of tracking signatures you just create vectors of pairs and push back the references. so the OnCreate/OnAddComponent/OnRemoveComponent/OnDestory will inform the world too update the caches. Do you still maintain the concept of a Scene? Or is it just 1 big World now with your component pools?

karlmar said:

Okay sounds like a reasonable approach, so instead of tracking signatures you just create vectors of pairs and push back the references. so the OnCreate/OnAddComponent/OnRemoveComponent/OnDestory will inform the world too update the caches.

Yep, conceptually. In reality its a bit more complicated as all components are linearely in memory (vector<Component>), meaning there have to be additional updates when enough components are added that the vector needs to have its capacity resized. Also, as to not invalidate caches while iterating over them (which could happen ie. while the collision-system is iterating, resulting in a script having its “OnCollision”-callback exceuted, spawning a new entity) all add/deleted on both entities and components are delayed until the end of each frame (with components being temporarily created in their own specialiced “create” buckets).

karlmar said:

Do you still maintain the concept of a Scene? Or is it just 1 big World now with your component pools?

Yes, everything is scenes. My editor can display/preview multiple scenes at once, it might even have multiple instances of the same scene around so everything is kept inside their own scenes. I also don't support additive loading of scenes like unity where all content goes into one scene, only side-loading and changing the currently active scene at runtime.

This topic is closed to new replies.

Advertisement