QuesterDesura

C++ Need some help with the Entity Component System

Recommended Posts

Hello,

I'm currently trying to make a Minecraft Clone using OpenGL and using the ECS pattern. I didn't quite understood yet why I need the "Systems" when I already have components. My current structure I'm planing to use is this:

 VBVcfIz.png

My main problem is that I don't really know how to store the Entitys. If I for example want to attack and check which Entity I did hit then I would need to iterate through all Entity stored in my vector<GameObject> in the World Class and check if it got the Health Component and then decrease the health. Which doesn't seem very clever to me as I will probably have multiple thousands Entitys alone by the Blocks in the chunks. I did read some stuff about Handlers but didn't understood how they work or should solve the problem of iterating through all Entitys who could possible need to be called to Receive Damage. Can anyone help me?

 

Thanks 

Share this post


Link to post
Share on other sites
TheChubu    9454
1 hour ago, QuesterDesura said:

Which doesn't seem very clever to me

Yeah it isnt, but this has nothing to do with ECS.

In a "proper" engine you have spatial structures that have all physical entities inside it with their collision bodies (that can be as simple as a box, or a bunch of other primitives attached together). This is often handled by the physics engine, you can check out Bullet for instance (if you're doing a 3D game) or Box2D (if you're doing a 2D game) to see how they work and how they're used... Or you could go down the path of trying to implement your own spatial structures, in which case you'll never finish anything. Your call.

Anyway, these structures can do neat things like you pass it some primitive, and it tells you all the stuff that collides with it.

In this particular case, what you'd do is to issue a query into that spatial structure, our primitive being an infinite ray (which would require for us to check the distance later) or a line segment (from the player up to the bullet's range), pointing in the direction the player  is firing. The physics engine/spatial  structure will tell you what entities did you hit. 

With that information, you could check if those entities have a health component, and if any of they do, decrease it.

1 hour ago, QuesterDesura said:

I didn't quite understood yet why I need the "Systems" when I already have components. 

Because in "pure" ECS, components have no logic. You implement it in the systems. That player class with a "process input" method? That'd go into an InputSystem that processes entities with Player components (and **only** those).

I suggest you google for "ecs library" and see what it turns up. No sense in reinventing all of this when you can draw inspiration from tons of ECS libs that have been spawned.

 

Edited by TheChubu

Share this post


Link to post
Share on other sites

There are many different architectures people call "ECS", because it's very hyped and popular right now, few people know why they need it, and many implementations of it are terrible.

It's not one design pattern, but a buzzword that acts as an umbrella for one or two designs that I personally think are nice, and a dozen more designs that are just terrible. Some really intelligent developers go a step further and say all ECS architectures are terrible. =)

22 minutes ago, QuesterDesura said:

I didn't quite understood yet why I need the "Systems" when I already have components.

As TheChubu mentioned, in some ECS architectures, components *only* hold data, and Systems contain the logic that transforms that data. Other ECS systems, that I wouldn't even call "ECS", put the logic in the components and lose half the reason why ECS exists in the first place.

24 minutes ago, QuesterDesura said:

If I for example want to attack and check which Entity I did hit then I would need to iterate through all Entity stored in my vector<GameObject> in the World Class and check if it got the Health Component and then decrease the health.

Yes, that sounds terrible.

I don't even want components of different type put in an array together - it doesn't seem worth it to me without a compelling reason (here's me explaining why I dislike it). I wouldn't want actions like 'IAttack' to be components either, nor would I have 'Player' responsible for drawing the GUI (depending on what you mean by GUI - if it's a name or healthbar over his head, I would - but if it's a HUD or menu widgets, I wouldn't).

I probably wouldn't make Blocks into entities - the blocks need to be highly optimized since you'll have huge numbers of them, and it'd be better to implement something special just for them.

 

In Minecraft-like games, there's really only a few different types of objects:

  • Blocks - As mentioned, special care needs to be given to these because of how many there are.
     
  • Special "blocks":
  • Wall switches (activated manually)
  • Floor switches (activated on-touch)
  • Doors
  • Arrow trap, spike floor trap, spike floor, etc...
  • Moving platforms / elevators
     
  • Monsters
  • Players
  • Projectiles (arrows, fireballs, etc...)
  • Items in the world (separate concept from the items in your inventory).

Essentially, I'd make two different classes:

  • Block (for all regular blocks - it'd probably cover 99% of all your objects)
  • Entity

'Entity' would handle all the other cases: special blocks, in-world items, projectiles (unless there are alot of projectiles - inwhich case, handle it specially), and monsters and players. They all share more in common than they don't:

  • Solid: Monsters, players, most special blocks (any that aren't solid, just have a "bool solid = false" or a empty bounding box)
  • Does stuff on touch: Monsters, projectiles, floor switches, items in world.
  • Does stuff on hit: Monsters, projectiles, wall switches, special blocks, etc...
  • Visible: All (any that aren't, just have a "bool invisible" or an empty 3D model)
  • Has health: All? (indestructible entities, if any could just be given a bajillion health).

I don't think components are needed here. Instead I'd change their behavior mostly by passing in parameters, including functors. Or if really needed, derive from a base class - or just implement specific behavior as abstract classes, contained by Entity.

Sure, this will all work perfectly fine in a component system (Solid, Visible, Touchable, Hitable), but component systems cost alot more code complexity, so if I was to pay for that complexity, I'd want to be sure it is actually worth the cost of a full ECS architecture.

Share this post


Link to post
Share on other sites

So if you don't want to make all Systems derive from a base class how should they be called then? Writing them all out manually or loop through them when having them all in a vector doesn't seem to be much difference for me.

When I make 2 Classes Block and Entity would you put them in 2 vectors where are Blocks/Entitys are stored? I was thinking bout making vectors for the 4 Areas in a Cartesian coordinate system and then I can access blocks with their position as a index and when I want to check for Raycasts then I can just take the player position and directly access all blocks in a radius of x and check if they are hit.

Share this post


Link to post
Share on other sites
Shaarigan    1038

Please oblige me and forgett about "GameObject". This is an approach I hate Unity for bringing it into the game because you do not never ever need something as a GameObject in a specialized game. What you should have is some set of Subsystems like Rendering, Logic, and Physics each has a list of specialized classes (no! GameObjects) like a combination of Matrix, Mesh and Texture (Rendering), a combination of Position, Bounds, Gravity, Force (Physics) and a combination of Healt, logical Inventory (Logic). Evrything may be connected due to a connector class holding 3 pointers to each of its connections to keep a maintainable structure of an entity for the game but thats it.

Rendering does not need to work on the logic, moving players does the Input System, moving NPCs does the AI Systemoperating on coresponding classes. Use some kind of polling or event based update routine to keep your game running.

In Minecraft, the Voxel Engine has no information about a single block until it was created as part of a querry into the terrain system. You never have single blocks stored as objects but a data managing unit that maps chunk data into visual and logicall data. A querry is given into the system to check for possible actions performing when a player hits the input button and on the case of mining or placing a block, some data is written into that chunk and rendering is updating the mesh to remove the texture/vertices from that position and have the underlying block "become visible".

I have already worked on some games that made use of modifying there environment by building, or growing

Share this post


Link to post
Share on other sites
Kylotan    10008
1 hour ago, Shaarigan said:

Please oblige me and forgett about "GameObject". This is an approach I hate Unity for bringing it into the game because you do not never ever need something as a GameObject in a specialized game.

Many, many games had some concept of a GameObject long before Unity came out. It's often called different names, of which Actor or Entity are perhaps the 2 most common.

This doesn't stop you having specialised objects which subsystems can access. For example, your Rendering system can render Meshes (like you said), but the GameObject/Entity/Actor can be responsible for registering its mesh with the Rendering system.

 

To the original poster - for a combat system you might normally need 2 components for things that can get hit - a Collision component to mark the bounds of the object, and a Damage component to track damage done to the object. You can slice and dice this process however you like, depending on which component-based approach is most appealing to you: get the damage component from the collision component via an entity, have a system that returns only matching pairs of collision/damage components, etc.

Ultimately though, you're not going to want your blocks to be component-based entities anyway, as you will have far too many blocks for this to be a efficient way to work. So you'll probably want to handle mining and digging as a special case. So you'll need to ask yourself how much benefit you're going to get from these components.

Share this post


Link to post
Share on other sites
On 9/10/2017 at 3:25 AM, QuesterDesura said:

So if you don't want to make all Systems derive from a base class how should they be called then? Writing them all out manually or loop through them when having them all in a vector doesn't seem to be much difference for me.

The end-generated code isn't different, but the code itself is significantly different.

As you know, in programming there are many different ways to get the same result. But different ways of coding can have different pros and cons they tradeoff (and it's not zero-sum either - some are genuinely better, others genuinely worse) - the code design affects their execution speed, memory usage, development time, compile time, convenience vs amount of boilerplate required, ease of debugging, ease of expanding, ease of learning/understanding, ease of reasoning about after having learned it, and so on, even when the final output is the same.

 

<opinion>

My issue isn't that the end-result is different (it's not), but rather that one method actually has more cons than I can justify to myself, without any real gains, and makes it a little harder to expand the architecture in certain ways, as development continues.

In the thread I linked, someone asked a similar question and I gave some concrete examples, but to summarize a two key points:

  • Forcing a consistent interface on unrelated classes puts unnecessary constraints on how the individual interfaces are designed and used.
    Hodgeman gave one example, and I gave the Uncharted 2 example. For me personally, when I am putting constraints on an interface via inheritance, I find it hard to notice when the class isn't a suitable fit, because I've already made the assumption that it should be inherited - so it's hard to catch myself at it, hence my pro-active defense against it - i.e. I better justify it to myself in advance, because if I assume it is justifiable without cause, I have trouble remembering to question the justifications afterward.
     
  • Manually calling the systems makes their execution order clear and explicit.
    It's slightly easier to debug and optimize (including easier to parallelize). And being more straightforward, it's much easier to comprehend and reason about, helping other coders - or yourself in the future - understand what's going on. It's also less code. You don't gain anything of real substance from making them inherit, but you do lose some real benefits.

Others absolutely disagree with me. I had a link to a thread somewhere giving an alternative view, though I can't place my hand on the link.

I should also clarify that there are absolutely use-cases for components and component systems where it *does* make sense for the components to inherit from a base class. But at the level of abstraction we are talking about here, where the ECS is containing things as unrelated as Graphics and Physics, I've never been convinced that the actual benefits sacrificed on that particular altar of abstraction gain any benefit worth the cost.  =)

</opinion>

Quote

When I make 2 Classes Block and Entity would you put them in 2 vectors where are Blocks/Entitys are stored?

Yeah, I'd put them in separate containers. Entities would likely be in a vector, possibly with an unordered_map to redirect EntityID's to the vector's elements, if required.

For blocks, I'd start off with a 3D array of chunks with each chunk being a 3D array of blocks, and only do something cleverer if needed.

For example, subdividing the world into 16x16x16 chunks of blocks, to cull or stream them quicker.

By "3D array", I actually mean a regular 1D array (std::vector or std::array) to you index into as if it were 3D:

arraySize = (width * height * depth);

arrayIndex = (x) + (y * width) + (z * height * width); //Usually tossed into a convience function.

 

 

Share this post


Link to post
Share on other sites
BeerNutts    4401
On 9/9/2017 at 7:33 PM, TheChubu said:

That player class with a "process input" method? That'd go into an InputSystem that processes entities with Player components (and **only** those).

Just wanted to point out you may want the InputSystem to operate on the entities which contains an InputComponent.  This way you can control things other than just the player (like a homing missile, or a remote control car, etc.).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Similar Content

    • By MarcusAseth
      this is super strange...
      I have this function here:
      template<typename TFirst, typename... TArgs> bool util::LogConsole(const TFirst& first, const TArgs&... rest) { std::cout << first << " "; return LogConsole(rest...); } bool util::LogConsole() { std::cout << std::endl; return false; } it's recursive, keep printing "first" and recursively sending the rest, until rest is empty and the overload taking no argument is called, which just print a newline. Ignore the bool return.
      Anyway, now I have this code somewhere else:
      for (auto& E : Entities) { float valx = E->GetPosition().x;//return 751 float valy = E->GetPosition().y;//return 838 LogConsole(valx, valy); } Entities is a vector<unique_ptr<Entity>> which currently only contains the derived from Entity player Paddle, so I access the Base class Entity method called GetPosition, which is the center pivot coordinates of the object.
      I pass the variable to LogConsole which predictably prints:
      But not I try to call LogConsole without those 2 variables inbetween, like this:
      for (auto& E : Entities) { LogConsole(E->GetPosition().x, E->GetPosition().y); } and watch what kind of output I get! :
      ... the y value is an unreasonable value o_o
      How is this even possible?! Since only the second value is being affected, my assumption is that there is some weird interplay with the second template parameter, the variadic argument...but I wasn't able to debug it even stepping trough it line by line...can anyone come with an explanation for this really strange behaviour?
    • By MarcusAseth
      Maybe today I'm confused and I'm missing the obvious...
      I've made this simple struct inside my Utility.h 
      template<typename TFirst, typename TSecond> struct Pair { Pair(TFirst f, TSecond s):First{f},Second{s}{} TFirst First; TSecond Second; }; And I'm using it from App.h (which #include "Utility.h") like so:
      App.h
      Pair<Uint32, Uint32> GetWindowSize(); App.cpp
      Pair<Uint32, Uint32> App::GetWindowSize() { return Pair<Uint32, Uint32>(Width,Height); } And I'm getting a cascade of errors ALL on line 39 (image below).
      Any idea what I am doing wrong this time? x_x

       
      EDIT: please mod delete this topic, I just remembered Utility.h put everything inside namespace util, sorry x_x
    • By noodleBowl
      I've gotten to part in my DirectX 11 project where I need to pass the MVP matrices to my vertex shader. And I'm a little lost when it comes to the use of the constant buffer with the vertex shader
      I understand I need to set up the constant buffer just like any other buffer:
      1. Create a buffer description with the D3D11_BIND_CONSTANT_BUFFER flag 2. Map my matrix data into the constant buffer 3. Use VSSetConstantBuffers to actually use the buffer But I get lost at the VertexShader part, how does my vertex shader know to use this constant buffer when we get to the shader side of things
      In the example I'm following I see they have this as their vertex shader, but I don't understand how the shader knows to use the MatrixBuffer cbuffer. They just use the members directly. What if there was multiple cbuffer declarations like the Microsoft documentation says you could have?
      //Inside vertex shader cbuffer MatrixBuffer { matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix; }; struct VertexInputType { float4 position : POSITION; float4 color : COLOR; }; struct PixelInputType { float4 position : SV_POSITION; float4 color : COLOR; }; PixelInputType ColorVertexShader(VertexInputType input) { PixelInputType output; // Change the position vector to be 4 units for proper matrix calculations. input.position.w = 1.0f; // Calculate the position of the vertex against the world, view, and projection matrices. output.position = mul(input.position, worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix); // Store the input color for the pixel shader to use. output.color = input.color; return output; }  
    • By MarcusAseth
       
      I start by saying that I am aware that what I am trying to do can easily be achieved trough the <functional> part of the library or trough a Functor or a Lambda, but I wanted to see this in template form.(Code below)
      So the first function works, the find_if algorithm find the first value in a vector greater than the specified parameter, but there is no template argument deduction for that function call because the algorithm require a pointer to a function but at that time it is not know I will pass an int into it, and so I need to specify like this:
      LargerThan_NoDeduction<30,int> But this seems ugly because now I have to take care of match the two, like <31.2, double>, and the worst part is that if I now decide to pass something else, like a <'d',char> or a <-10,float> , the function expects a size_t as first template parameter, so this won't do.
      So what I wanted to achieve was to pass a predicate to an algorithm in the form of
      LargerThan(30) where the template part of it takes care both of storing the data value (in this case 30, but could be a 'c') and deducing the type we compare from out of it, so in this case int.
      So I have a function LargerThan(Type) that returns a function pointer and passes down the value to an helper function which takes both the value and the deduced type, so I don't have to type them myself.
      Problem is, this helper function still has an auto in the first template parameter, and the compiler doesn't like this
      How would you make this work trough template magics?
      #include <iostream> #include <vector> #include <algorithm> using namespace std; //// template<size_t TestCase,typename Type> bool LargerThan_NoDeduction(Type value) { return value > TestCase; } //// template<typename Type> auto LargerThan(Type TestCase)-> bool(*)(Type) { return LargerThan_helper<TestCase, Type>; } template<auto TestCase, typename Type> //auto here is not liked!!! bool LargerThan_helper(Type value) { return value > TestCase; } //// int main() { vector<int> vec{ 0,11,21,35,67,102 }; //Must specify size and type. auto p = find_if(vec.begin(), vec.end(), LargerThan_NoDeduction<30,int>);//WORKS if (p != vec.end()) { cout << *p << endl; }//WORKS //Deduces type from the value passed. p = find_if(vec.begin(), vec.end(), LargerThan(30));//ERROR if (p != vec.end()) { cout << *p << endl; }//ERROR return 0; }  
    • By markshaw001
      hi i am new to opengl can someone here tell me how to render this 2d shape i want to make curve or arc in 2d in between two lines its a rough sketch i made i tried the internet but could not find much help beacuse their were no specific tutorials of making arcs or curves

  • Popular Now