• Advertisement
  • Popular Tags

  • Popular Now

  • Advertisement
  • Similar Content

    • By Tuner_z

      Name: One Level: Stickman Jailbreak
      Price: Free
      Developer: RTU Studio
      Platform: Android
      Language: C# (Unity3D)
      Google Play: https://play.google.com/store/apps/details?id=com.RTU.OneLevel
       
      Hello!
      I want to show you my game! "One level: Stickman Jailbreak" is a puzzle game with unusual gameplay where you must help the character to escape from prison. You just need to take the key and get out alive. The game has only one level, and there are many ways to complete it. Not everything is as simple as it might seem at first glance, so there are clues in the game.
       
      Short description:
      Nobody escapes from here!
       
      Description:
      Tommy got into trouble again! Our hero is behind bars. But he's not going to stay in jail for a long time and he decides to escape. Tommy steals a key and gets out of the jail cell. But our friend doesn't go free: Tommy suddenly finds himself in the same room from which he just escaped! The conditions for escaping change every time. In order to go free Tommy will have to solve logical puzzles and you can help him in this!
      At first it will be easy, but the tension will increase, and the tasks will become more complicated with each level. You should use your brain for all 100%, but if your skill is not enough, you can use a hint or ask for help from friends!
      You can solve the puzzles alone or with your friends and spend time well!
       
      Features: 
      Features:
      - 48 unique levels;
      - the game is translated into 10 languages: English, French, German, Spanish, Italian, Portuguese, Russian, Japanese, Chinese, Korean;
      - the function of "help from friend";
      - hints;
      - instructions.
       
      Trailer:
       
      Screenshots:





       
    • By Brandon Marx
      Hello forum,
      I have some decent amount of experience in Unity making games for Software Engineering projects in college, these were very specific projects however and I still am fairly new to building games. I wanted to make a game that uses the shadows of objects for collision detecting (i.e. shooting a gun at a characters shadow causes that character damage. What is the best engine to do this in (game will be 3D), and does anyone have any advice on how to approach this concept? I consider myself fairly experienced in programming, but game dev is just an entirely different beast.
    • By juicyz
      Hey all,
      I've been slowly working on my game called AotW for a while now.  I have come to the conclusions that it would be nice to cooperate with 1 or 2 others to help finish it.  Ive been trying to keep my GDD up to date with my ideas and development so that would give a better overview of the game when the time comes.  Currently I have a basic skeleton of the RPG elements needed but everything can still be discussed and talked about and we can transform my idea to something the group likes.
      The premise of the game is a Diablo-like procedurally generated map with RPG elements that include sockets, inventory, classes, abilities, crafting, loot, items, sockets, and enchanting.  This will be done in a 2D iso view as I can't do 3D art and I enjoy 2D games a lot.
       
      I don't plan on releasing this as this is more of a hobby project for me and I have a full-time job.  Though I'd like to start putting more hours into development and having others definitely will be motivation.  I also want to be able to say I have finally "finished" a game idea to some degree.  If the time comes and we want to release it, then we can go ahead and do so but that's not my purpose or plan. 
       
      Discord:
      Juicyz#3683
       
       
      Thanks,
      Juicyz
    • By Arnold // Golden Donkey Productions
      Hi, I've been working on this issue for a while and haven't yet found an answer.
      Does anyone know the best way to convert unity's LAT & LONG into a vector 3 position that I could use in a virtual world (if it's even possible). 
      Thankyou in advance
    • By wobbegong_
      I am taking an absolute beginner's game development course and we have just finished game jams in small groups. Our current assignment is to get feedback from people working in any aspect of game development. I would very much appreciate any feedback! The game is up on itchi.io (sound warning) https://wobbegong.itch.io/zombie-shooter It's essentially a very basic PvE.
      I also have some things I'm wondering about, but you don't necessarily have to answer these. 
      1. Do you have any tips on working with physics? My group wrestled a bit with Rigidbody physics not totally working the way we wanted to -- jumping ended up kind of floaty and inclines seem to mess up movement. Alternatively... how can I build terrains with depth that won't result in wonky physics?
      2. How can I keep up the level of challenge in an interesting way as the player progresses through the waves?
      3. What are some of your personal guidelines for creating title screens?
      Thank you very much in advance!
  • Advertisement
  • Advertisement
Sign in to follow this  

Unity Need help with issues I encountered while writing my ECS

This topic is 870 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

Hi everyone,

 

I'm working on making an Entity Component System in C++ for my game and there are some things I'm a bit confused about.

Right now my game is structured as following:

  • Entity: Contains an unique id and a bunch of components, only accessible by using a singleton EntityManager. e.g.

    int EntityManager::createEntity()

    void EntityManager::addComponent<T>(T* t, int entity_id)

    T EntityManager::getComponent<T>(int entity_id)

  • Component: Simple 'databag' structs without any methods, just simple data. These are stored in entities using the above mentioned EntityManager as void* (and using typeid::hash_code to compare the requested types at get/hasComponent).

  • System: Derived from 'ISystem' with only two methods 'onUpdate' and 'onMessage', and stored in a SystemManager singleton. When a component gets added or removed the systems get a message containing the entity id, so they can (un)register to it.

Is this the right way of doing this? I read some things about getting a lot of cache misses if you don't store components in a contiguous array but I can't imagine how to do that without coupling back the code again. (So far I haven't noticed any performance issues, though there's not a lot running right now).
 

Furthermore, these two problems have been bothering me a lot:

1) It seems like a good idea to use separate rendering systems (e.g. SpriteRenderer, ShapeRenderer, TilemapRenderer) but how do I take care of things like sorting (depth, shader, texture) and batching when these systems are completely decoupled?
 

2) The camera, I've searched far and wide for a good answer but I can't seem to find one. Is the camera an entity? If so, does that mean the view matrix is a global or singleton? (What if I want multiple camera's with only 1 'active' like in Unity?). This is assuming that the camera transformation gets applied at the rendering process, otherwise I guess it'd be possible to have like a 'CameraSystem' which translates all objects opposite of the currently active camera position, though that seems slow to me.


Please enlighten me!

Thanks in advance my dear gamedevs!

Share this post


Link to post
Share on other sites
Advertisement

First of all, don't make anything be a global or singleton (including EntityManager and SystemManager). There's absolutely no reason to do that. Just pass your instances around to whomever needs to use them.

 

 

 


Is this the right way of doing this? I read some things about getting a lot of cache misses if you don't store components in a contiguous array but I can't imagine how to do that without coupling back the code again.

 

 

Not sure why you think that would "couple the code back again".You can store a contiguous array for each component type in your EntityManager (The EntityManager can be agnostic of the actual component types - only the callers need to know). The systems can request the ones they need (some people store components in the SystemManagers, but I don't like this, as it's not always clear which system would "own" which component). Then somewhere you need to store a mapping of which components each entity has. There are many ways to do this, and a good design probably isn't trivial (pointers vs indices, do you store these in the entity object or in a table in EntityManager, do components move around in their array and you need to deal with that, etc...).

 

If you're making a small game, you probably don't need to worry about storing components in contiguous memory, but it is one of the major benefits of an entity system.

 

 

 


2) The camera, I've searched far and wide for a good answer but I can't seem to find one. Is the camera an entity? If so, does that mean the view matrix is a global or singleton?

 

Sure, it can be an entity. This really isn't very complex. The view matrix is data in a component on the camera entity. The render system (or whomever needs the camera information) just looks for the first "active" camera component and uses the information in that. Done.

Edited by phil_t

Share this post


Link to post
Share on other sites

Hi phil, thanks for your response, there are some things I'd like to ask about some more.

 

 


First of all, don't make anything be a global or singleton (including EntityManager and SystemManager). There's absolutely no reason to do that. Just pass your instances around to whomever needs to use them.

Would this mean I make these classes a non-singleton and just pass references/pointers to whoever is using them? (e.g. EntityManager has a pointer to SystemManager that it sends messages to on add/remove component). Even though it seems a bit cleaner, it does mean that whenever you decide to make use of what-would-be-a-singleton, you'd have to go out of your way to add it to the constructor (or even worse: add getters and setters).

 

 

 


Not sure why you think that would "couple the code back again".You can store a contiguous array for each component type in your EntityManager (The EntityManager can be agnostic of the actual component types - only the callers need to know).

What confuses me about this is if you store the components as say IComponent* or void*, how would that solve the issue of accessing contiguous memory? This is actually why I thought it would mean you'd have to couple back the code because when a system requests say a 'Position' component, the EntityManager would still have to iterate through the components to see which one is a 'Position' component. Unless you store all types of components in their own respective container (e.g. vector<Position> position_components <-- accessed by entity index/id). But this would couple back the code again because you'd have to create a new container for each new component that an entity could possibly have.

 

 

EDIT: Your suggestion about the camera entity sounds like a good idea! But how would I go about making sure only one camera can be active at a time (without making use of a global state)?

Edited by mrimamister

Share this post


Link to post
Share on other sites

Even though it seems a bit cleaner, it does mean that whenever you decide to make use of what-would-be-a-singleton, you'd have to go out of your way to add it to the constructor

 

Yes. That's a good thing. Makes your dependencies clear.

 

 

 


Unless you store all types of components in their own respective container (e.g. vector position_components <-- accessed by entity index/id). But this would couple back the code again because you'd have to create a new container for each new component that an entity could possibly have.

 

I just coded this up quickly (I've only ever implemented one in C#, there might be better ways to do this in C++, but it should give you the idea).

// Just some common base class
struct ComponentArrayBase {};

template<typename _T>
struct ComponentArray : public ComponentArrayBase
{
    ComponentArray(size_t defaultSize) : Components(defaultSize) {}

    std::vector<_T> Components;
};

class EntityManager
{
public:
    template<typename _T>
    size_t AddComponentType(size_t defaultSize)
    {
        _componentArrays.push_back(std::make_unique<ComponentArray<_T>>(defaultSize));
        return _componentArrays.size() - 1;
    }

    template<typename _T>
    std::vector<_T> &GetComponentArray(size_t componentTypeId)
    {
        return static_cast<ComponentArray<_T>&>(*_componentArrays[componentTypeId]).Components;
    }

private:
    std::vector<std::unique_ptr<ComponentArrayBase>> _componentArrays;
};


// Some components:
struct PositionComponent
{
    float x, y, z;
};
struct PhysicsComponent
{
    float mass;
    int shapeType;
};



    EntityManager em;

    // At startup, add the component types we need:
    size_t ComponentIdPosition = em.AddComponentType<PositionComponent>(500);
    size_t PhysicsIdPosition = em.AddComponentType<PhysicsComponent>(500);

    // Them whomever needs them does this:
    std::vector<PositionComponent> &positions = em.GetComponentArray<PositionComponent>(ComponentIdPosition);
    std::vector<PhysicsComponent> &physics = em.GetComponentArray<PhysicsComponent>(PhysicsIdPosition);


... you could use hard-coded ids too (and then maybe an unordered_map instead of a vector for _componentArrays) so you don't need to pass the generated component ids around. Or you could use typeid(_T) if you have RTTI enabled.

 

 

 

 


Your suggestion about the camera entity sounds like a good idea! But how would I go about making sure only one camera can be active at a time (without making use of a global state)?

 

Do you need to? It's hard to offer suggestions here without knowing more about why you want multiple cameras. Is it for a split-screen view? Is it for switching between cut-scenes and gameplay? The reasons for it and your needs will dictate how you implement this.

Edited by phil_t

Share this post


Link to post
Share on other sites

[Disclaimer: I've never written a game with an ECS system before, though I've read a bunch about them. So below is acquired second-hand knowledge, not learned first-hand experience.

Take with a glass of water and spoonful of salt. Consume at your own risksmile.png 

 

Unless you store all types of components in their own respective container (e.g. vector<Position> position_components <-- accessed by entity index/id).

Yes, this is how it's often done. Though because components may move around within the vector as you're adding and removing entities (because not every entity will have every component, and because you may want to occasionally sort components), you may want to do something like:

std::unordered_map<EntityID, ComponentIndices> // or std::vector<ComponentIndices> with 'EntityID' being the index into *this* vector.
 
struct ComponentIndices
{
     int physicsComponentIndex = NoComponent;
     int scriptableComponentIndex = 17;
     etc...
};

But this would couple back the code again because you'd have to create a new container for each new component that an entity could possibly have.

Yep. You'll also have to write a new data struct and a new logic system for each Component type. It's not hard to type std::vector<RenderComponentDataStruct>.

struct SpecificComponent
{
    //...data...
};

class SpecificSystem //Why inherit?
{
public:
   void onUpdate();
   //other funcs, some system-specific.
   
private:
   std::vector<SpecificComponent> componentData;
};

In some ECS architectures the "entities" are conceptual only, without any actual class. In those flavors of ECS, an 'entity' is just an ID, with all the data in the components and all the logic ran by systems (which may include a scriptable component for more uniquish per-entity custom behavior logic).
 
Basically, components are the data, systems hold the logic, operate on the data, and own the components. Because some systems depend on other systems (like physics on positions), you can pass one system as a reference to the constructor of dependent systems.
 
In total, you really shouldn't have too many "systems". A dozen-ish maybe.
 

But how would I go about making sure only one camera can be active at a time (without making use of a global state)?

What if you want more than one camera active at a time? Maybe you want to support split screen, or maybe you want to, for example, show in the upper-left corner of the screen a door opening up elsewhere in the level when you step on a switch.

Instead of thinking of cameras as "active" or "inactive", I'd just give cameras IDs (if they are entities, use their entityID), and assign your screen a camera.

For example:

struct Screen
{
    Rect region; //What region of the game window to render into.
    CameraID camera; //What camera to use.
};

void World::Render(const Screen &screen);

Though I don't know if I'd actually make the camera an entity or not. Not everything needs to be forced into components.
 

It seems like a good idea to use separate rendering systems (e.g. SpriteRenderer, ShapeRenderer, TilemapRenderer) but how do I take care of things like sorting (depth, shader, texture) and batching when these systems are completely decoupled?

 
You can have those "renderers" generate the drawcalls, and then pass those drawcalls to the real renderer that culls them, sorts them, and then draws them all.
In my code (which isn't an ECS), I call these "batches" - LineBatch, QuadBatch (for sprites, tiles, etc...). Instead of a SpriteRenderer, I create a QuadBatch, fill it with sprites, and then render it.
 
Basically, the batches are for code-convenience/code-clarity in creating the drawcalls, so they are built on top of the regular renderer. You mentioned different "rendering systems", which is confusing because "systems" mean something specific in a ECS system. You don't want multiple different rendering systems in the ECS sense of the wordbut you can certainly have convenience classes or convenience functions that help you generate data for the renderer.
 
In fact, your components should be at a higher level of abstraction than your renderer anyway (though you may still have a RenderingSystem depending on your design), because not everything you'll draw will come from components. You don't have to (and probably shouldn't) cram your HUD or other GUI stuff into your ECS system, for example.

 

ECS is a useful paradigm for parts of your architecture, but not your entire architecture. Different paradigms and patterns fit the nature of different problems, and games have multiple problems that are best tackled through the lens of different paradigms. There is no "one solution fits every problem", and because games are complex things with many disparate challenges, ECS doesn't solve every game-related problem perfectly; it solves some of the problems really well, and others poorly, so make it "a" tool in your toolbox, not "the" tool.

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

    EntityManager em;

    // At startup, add the component types we need:
    size_t ComponentIdPosition = em.AddComponentType<PositionComponent>(500);
    size_t PhysicsIdPosition = em.AddComponentType<PhysicsComponent>(500);

    // Them whomever needs them does this:
    std::vector<PositionComponent> &positions = em.GetComponentArray<PositionComponent>(ComponentIdPosition);
    std::vector<PhysicsComponent> &physics = em.GetComponentArray<PhysicsComponent>(PhysicsIdPosition);


... you could use hard-coded ids too (and then maybe an unordered_map instead of a vector for _componentArrays) so you don't need to pass the generated component ids around. Or you could use typeid(_T) if you have RTTI enabled.



A typical pattern here is to add one more layer of indirection and to reference the components by the EntityID itself. You don't need the entity store know the index of the component at all. When you query the Component Mapper for a particular entity, it uses a data structure (e.g. a good hash table) to map the EntityID to the ComponentIndex.

The public API for the Component Mapper can allow you to use the ComponentIndex if you happen to have one cached, but you should avoid that, because of course if you _actually_ care about contiguous memory usage then you're also recompacting the component vector every time a component is removed and so the ComponentIndex is unstable across any operation that might add or remove components. Since your EntityId to ComponentIndex cache thus can only live locally, you might as well just cache the pointer instead... unless of course you _really actually_ care about data locality and your components are stored in a Structure-of-Arrays form instead of an Array-of-Structures form.

Computers is hard.

Of course, most games don't need any of that over-complicated cruft and shouldn't use an ECS anyway and just stick to a simpler component model...

Share this post


Link to post
Share on other sites


Of course, most games don't need any of that over-complicated cruft and shouldn't use an ECS anyway and just stick to a simpler component model...

 

indeed.

 

it appears there are only three circumstances where an ECS is truly called for:

1. when non-coders must be able to define entity types with differing components - like in unity. or when entity types with differing components must be data driven for some reason. or entity definitions change frequently, and build times are long.

2. as a workaround to inheritance issues. compostion may be a possible better alternative in this case.

3. as a method of code optimization, via data oriented design - requires cache friendly SoA data oriented design. i've only seen this mentioned once in a gammasutra postmortem, when it comes to real world use. PS2 or PS3 title as i recall. they had to go cache friendly to meet their minimum publisher mandated FPS. they were pretty desperate. they had already optimized all of render, and were down to optimizing update. lucky for them, cache friendly design just put them over the top.

 

unless one or more of the above three case applies, ECS appears to be overkill, introducing unnecessary complexity. IE over-engineering.

Share this post


Link to post
Share on other sites

Thanks everyone for the great replies, this has definitely been very educational!

 

 


Of course, most games don't need any of that over-complicated cruft and shouldn't use an ECS anyway and just stick to a simpler component model...

 

 


unless one or more of the above three case applies, ECS appears to be overkill, introducing unnecessary complexity. IE over-engineering.

 

Could you elaborate on the alternatives? Although I'm writing this system partly to educate myself on how an ECS works (on a lower level than Unity), I am serious about developing my game and have thought before that this may be a bit overkill (I'm developing a 2D rpg with tile maps). I've tried developing games before using 'traditional' OOP, by which I basically mean just a bunch of inheritance trees, and I've seen it turn out really nasty (multiple inheritance, duplicate code, different classes with only slight differences). I never actually tried to use 'normal' composition (e.g. Player has 'IMovementBehaviour', 'IRenderBehaviour', IXBehaviour properties) but I came across this thread: http://gamedev.stackexchange.com/questions/34625/composition-heavy-oop-vs-pure-entity-component-systems

Here someone explains how even if you do take the latter mentioned approach, you still end up with empty classes that just contain a bunch of components (back to ECS).

(The only difference I see in the example in the link is that components also contain logic, which isn't part of the 'traditional' ECS approach, and I can imagine that interdependency between components can become a problem)

 

EDIT: grammar

Edited by mrimamister

Share this post


Link to post
Share on other sites

In a ""normal"" OOP architecture (which BTW, ECS isn't "not OOP"), you don't need a huge inheritance tree.

 

It can be as simple as a single level of inheritance:

17a4120f6a.png

 

The key is that 'Entity' provides as much good defaults as possible, so the subclasses override as few functions as possible.

Further, you don't need different classes for different enemy types - as much as possible, you customize enemies by the parameters you passed into the constructor (appearance, stats, etc...). So you really only need separate classes for distinct behavior, and even the behavior can be adjusted by passed in parameters. Further, once you realize that entire functions (callbacks, functors, or lambdas) or classes (or scripts) can be passed in as parameters, even behavior can be customized through parameters. Or you could just make "Entity" scriptable, and just make scripts for each of the "derived" classes.

Share this post


Link to post
Share on other sites

The thing is that once you assume that behaviour can be defined by parameters/properties, you basically go back to composition.

For example if you have AI behaviour in say the 'Basic Enemy' class (which most likely other enemy classes will inherit) and then later you later decide you want the player to also be controlled by an AI (e.g. 'demo mode' when the main menu has been inactive for a while). You could make a base class AIEntity and make both the basic enemy and the player inherit that (obviously very bad because you'd just end up making a blob(?) class as an easy fix) or you could (more likely) say "hey, in that case let's just give the player a property IMovementBehaviour which can be set to say PlayerBehaviour or AIBehaviour at runtime". All good, except when you repeat these steps a dozen times and come to the conclusion that a lot of behaviour should be changable in runtime. That leads to classes without any implementation and with just a bunch of behaviour interface properties which is essentially the same as a CES.

 

This is of course based on a lot of "what if"'s but I never developed a game before on a relatively large scale, and I'm sure a lot of things will pop up during the development, it doesn't really seem possible to know exactly how everything should behave from the beginning. Am I just overthinking it? 

Share this post


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

  • Advertisement