Game API Opinion

Hi,

My game has a custom ECS design. It is really nice, working fantastic, handling 10,000’s of entities. The one problem I am running into… the game is getting so big I am having issues remembering where methods or systems are that handle common things. I blame age…

I would love your opinions. I’ve been debating make a partial class called, API, for a one stop shop. This way I can have a single access point for everything.

API Examples:

API.CreateEntity(string type);
API.MoveEntity(Entity entity, Vector2 position);
API.KillEntity(Entity entity);
API.EntitySetColor(Entity entity, Color color);

Currently design roughly looks like this:

Assets.CreateEntity(string type);
Entity.Get<SpatialComponent>().SetPosition(Vector2 position);
SystemEngine.GetSystem<DeathSystem>().AddEntity(Entity entity);
Entity.Get<RenderComponent>().SetColor(Color color);

Is this idea a can of worms not worth opening? Thoughts?

Thanks,
David

I’m a big fan of SOLID design principles and I find they do help mitigate some of this, but it sounds like you need an EntityManager object/component that can handle this for you.

For colour, if Entity contains no public means of setting that value for that property, or any other, perhaps an EntityPropertyManager object/component might be appropriate?

I would recommend against a blanket host object for everything since it’s really just gonna put you back in the same boat you are now, you just have to find everything under API. You could do that with a namespace.

Still, it’s your API and you’ve gotta do what works for you, first and foremost. You’re the one who has to use it, after all! :slight_smile:

Thanks. I have a simple EntityManager which retrieves Entities by id only. Maybe I have the EM designed wrong or it missing functionality.

My GameEngine has dozens of Systems (RenderSystem, MoveSystem, AISystem, etc…) to operate on the components attached to entities. It operates nice and neat. I feel that design is super solid. I have 50+ components that can attach to any entity, all which contain data and many helper methods for its own data.

It is just getting overwhelming with everything. Generally when I get this “gut” feeling, something went off track in the code.

David

I am starting to wonder if it is the “events” or “event triggers” that is causing my headaches. Like Event Handling/Listening. Looking at the large list of all the things I’d wanted in the API. It all tends to be causes from some effect (aka an event trigger). Hmmmm.

@HietpasGames From my experience, you should do both. Keep your current design, but add a “shortcuts” API object that does what you are proposing. (so, API object is a facade pattern)

If you redesign to put the actual api surface only in API you will probably end up with some severe spagetti code.

I wouldn’t say it’s wrong, it just depends on what you want. Maybe EntityLifetimeManager is what you want, which then exposes functionality for CreateEntity and KillEntity.

Perhaps all API needs to be is ComponentManager where you can just do stuff like…

var entityLifetimeComponent = ComponentManager.get<EntityLifetimeComponent>();
var newEntity = entityLifetimeComponent.Create("enemyEntity");
...
var entityPositionComponent = ComponentManager.get<EntityPositionComponent>();
entityPositionComponent.MoveEntity(newEntity, new Vector2(123, 234));

Or whatever for names… Manager, System, Component. It’s really just about how you define your stuff, but I think you get the idea.

As we are coding with a “hardcore Object-oriented language” C#, why not put these methods inside their respective classes?

You can make a nice hierarchical class structure and put these methods in the best fitting layers.

Ex:
E1 = Entity.Create(string type )
E1.Move (Vector2 position)
E1.Kill( )

This way, you can save a lot of trouble in the future when you are adding more entity types, behaviors, and “special case entities”.

1 Like

Thanks but that would break the pattern of ECS and lose the benefits of ECS. For example, not all entity objects move, like a rock. Same could be said for a kill function. There are cases where entities are not killable. I’ve learned over the past few years of game programming, doing an OO design over an ECS pattern can make things super messy. Don’t get me wrong, I am not saying OO is bad or anything. It has its place and I use it for my GUIs.

2 Likes

I would go with having either the top level Engine class being a singleton to avoid having many singletons, that way you can grab most things in the game from one place. Or have a class called Application that has a few vital static pointers (or what ever C# uses) in it.

For your ECS i would have a World class that stores component managers and systems along with an EntityManager/lifetime pool thingy.

That way it becomes more like:

World* world = Application::s_World;
EntityID = world->CreateEntity();
world->AddComponent<Transform>(id);

Or:

Engine* engine = Engine::Instance();
EntityID id = engine->World->CreateEntity();
engine->World->AddComponent<Transform>(id);

World is just a container for holding the EntityManager, ComponentManager which in turn holds ComponentPools etc. The above is C++ but same is valid for C#.

I think what’s missing here, is composition. ECS is about components, and components are about composition.
From C#'s point of view, a class can’t have multiple inheritance (mixins), but it can implement many interfaces. What handles those interfaces could be dependency-injected in, ideally through a factory. This would allow you to have a List and then you could extract things based on interface. var moveables = AllGameObjects.Where(x=> x is IMove).
Ultimately, you might need to consider performance, and perhaphs multiple collections of different types (List, List, List, List) what ever! So that’s two choices, as this approach would not require some common base class / marker interface.
It works out to be a bit more code, but it will organise your collections, and allows your objects to be smarter, as they’ll be away of how to move themselves, or perform AI logic, etc. That’s easy to change, as you might inject different code handling the same interface(s) etc.

I think your API idea is good though I would probably put the code that can be in Entity into Entity like Hasindu said. In your example I would use the API for Create, Move, and Kill, as they theoretically need to access stuff outside of the entity but put SetColor in Entity.

Hi there,

My colleagues here gave quite some good advice, I only want to add one thing that you can do right now (if you haven’t already). Document your code, at least at the beginning of each class with what it does, then have a diagram with how your pieces come together.

And, remember to update each time you refactor or add things. I recognize the problem you’re having, it’s age independent, it happens when you are constructing something rather complex and you don’t work on it everyday. I usually need 1h of reading and remembering myself before continuing work after a long time off the project. :wink:

Hope this helps.
Cheers!

Take a look at Nez. In fact i would just take his samplss and build off it. but he has old code that might be over complex like his asset managers and fonts. i took about half his engine. burt i dont wanan reinvent anything standard.

Dont invent an ECS if u dont have special needs and seems ur have general use case . theres one in Unity and in here its pretty standard. Nez ECS is similar, works with different spaces and colliders and physics and is decent… i dont use it but i took a hard look at it… u can nest transfroms and build complex objects with nested objects in them. use differnet colliders and such. wehn u break and itesm u break it, destroy the parent, and spawn off its pieces as tempory particles ( unsaved in level) Already ur not mentioning interfaces and you want to avoid templates in the API. (at least the outside of it)

you should serialize polymorphic collections of your entities . Your levels would be jsut a collection or two of entities , and should have one line of code to load and save ( datacontact serialization or similar) the whole level. The collections can have collection data contract… a general solution is alwasy better than a special code, because when microsoft improves it u get it for free.

the rest is attributes on member that u want to persist, like position… vertex in local space… and let .net handle that rest. So i could have a collection of IEntity, and the collection serialiser can Datacontract set to IsReference, and Knowntypes and u can add all sorts of polymorphic nested things. You can have a whole level editor built off that and make complex itesm all stored in a single Entities collection. Those entities can each have have child Entities collections that move relative to them. Maybe its overkill but im from a CAD background and thats how they do it. the ECS and otehr general mechanism in Nez even has great LINQ timer that lets u add events in time in one line.