Need help with ECS understanding.

Hello guys, I try to shift my mind to start using ECS. I have a simple schema


And to be honest, I don’t know how to implement this using ECS. I understand how systems work, and how to create, move, rotate, chasing objects, for example. But don’t understand how to organize something like this. So, please, give me a vector on how to organize this properly.

It’s a bit difficult to give much specific advice without knowing more about what exactly your diagram represents, but I’ll make a few assumptions and take a crack at it.

Assuming each box is an object, therefore data and behavior. What you’re going to do with ECS is split the data and behaviors apart. As a guess, your player will have component data attached to it:

  • Player data
  • Human player tag | AI player tag
  • Money data

And you will probably have some Captain entities with component data attached:

  • Captain data (buffer)
  • Ship data (buffer)
  • Module data (buffer)

You’ll notice that I’ve done away with the hierarchy. There’s really no need for it.

Now you “simply” write systems that process each type of data, or combinations of component data. e.g. a Move Captain system that processes all entities that have Captain component data. If e.g. you need money to move, your player system would process all entities with both player and money component data.

The trick is you are processing all of the entities that have the same component data at once, rather than processing a single entity at a time. So each system needs to be independent, although you can control things so that one system needs to run before another. e.g. process the player inputs first to set up the data for the movement.

Does that kind of sort of make sense?

1 Like

Usually it start with determining the hottest/most common code path of your game. A solution will then follow. Because data oriented you will plan the shape of data for machine to work the fastest in that case.

2 Likes

Hello,

Here is my point of view.
First not everything make sense to solve with DOTS and ECS.

I asusme from your diagram that you are trying to do some sort a space RTS where your player can select several units. Your units realy are the captains as I understood the ships are not represented on screen. and the fight is more turn based tahnt realtime laser shooting with hit and miss targets.

So, IMO, player could be a classic game object that take care of registering player input.
Captains are entities that have a ship buffer element, a local to world, the rendering compnents, maybe acollider.
When selecting captain, the corresponding netity is added to a list in hte player game object, and when registering the target position you want your captains to move you add/set a targetposition componnent on each captain selected by the player game object.

Ship would just be some description of attack, defense, health or something in the same lines and when two captains fight, based on your logic some ships may be destroyed weakening the captains fleet.

For money either you win some for each ship destroyed in wich case a money compoennt should be added to each ship and added to the player’s game object money when a ship is destoyed, or the same thing but with money on hte captain.
Or even simpler, jsut a fixed amount on each ship destruction so no need for money data on the ships.

Not clear on what modules for ship are, maybe some sort of special effect like damage relfection or explosion damage on destruction. This is all very dependent on the type of game you are doing and the combat type you are planing.

In conclusion, start listing all the data you need for each of your entites/game object/game concept/units (call them as you wish). then based on this list of data, pick the one that are the most common in your game (shared by most entities/concept and most present in numbers), these are the one worth managing with ECS. For the rest stick with classi OOP/Monobehavior. Once your are more familiar with the “ECS way of thinking” you can try to “migrate” the rest to it.

2 Likes

Guys, thank you for your answers, it helps a lot. I will post the final diagram that I come up with using ECS.
For now, I have a little question.
5384520--545673--Screenshot_116.png
Each captain can earn and spend his player’s money, and now, I think to store the player’s entity in CaptainBufferElement.
5384520--545676--Screenshot_117.png
In this case, when the captain wants to change the money amount it can take his player’s entity and put AddMoneyComponent to it. And the money system can handle it. But I’m not sure if it is a good practice to store entities inside of IComponentData and IBufferElementData. And maybe I can use a more suitable approach for it?

update: and for this case, I don’t need to add CaptainsBuffer to the player’s entity, because captains will have the player’s entity, and it will be enough. but still not sure that it is good for the ECS approach.

What will your captains spend the money on ?
Additional ships for their fleet ?

Is there a point in making the captain manage their own money ?
Can’t they just spend the “player’s” money directly ?

Making a captain “carry” his money may be needed if you plan on doing an economy system where the captain need to be physicaly/spatially near a shop to spend it or transfer it to another captain.
Or if your want each captain to be able to only spend the money they personnaly earned.
Or you want each captain to earn there own money and the player getting an X% cut taht can be used by any captain.

Depending on what you want to implement, you may have no other choice but to use entity reference in components.
It’s not inherently bad,but in my experience when I end up using that, it leads to the use of entity command buffer (ECB) in the systems and that leads to a single threaded job that need to complete before the rest of the logic, so having lots of those may hurt performence, that why Unity recommend using the entity command buffer as rarely as possible.

There are ways to avoid the use of the ECB like setting the data through the use of ComponentDataArray but you need to be aware of thread safety issue that may occur when setting data on entities that are not direct “target” of your system’s query.

EDIT : reference to the doc about use of ECB :
https://docs.unity3d.com/Packages/com.unity.entities@0.1/api/Unity.Entities.EntityCommandBufferSystem.html

1 Like

The way I read it was the Captain has an entity reference to its Player, whereby they add the AddMoney component. I don’t like this approach as you can only have one AddMoney component on an entity at a time. I can imagine the first frame of a game, all the Captains will want to upgrade themselves, but only the first one will be allowed to put an AddMoney component onto its Player

1 Like

If the money is on the player and the addmoney is just a signal to change the player’s poney amount then making the addmoney an ibufferelement would solve the issue.

1 Like

I agree, but it feels like its creeping back into OOP territory. The Player gets a buffer of its captains, now a buffer of money additions…

I agree, having the relationship expressed in two different places would have been bad. A Captain could think it belongs to Player3 because thats the player entity it points to, but Player1 could think it owns the captain because its mistakenly in its Buffer…

1 Like

Thank you for the reply

Only a player has a money component. And I want to let the player’s captains change the player’s Money component data.

a player has money, captains may change this money amount, so they have to be able to affect the money amount somehow. the only way that I came up with, it is to add the player’s entity to the captain’s component and add AddMoney component to the player, when I need it. But I don’t really like this way.

So, how would you implement this?

update: I will read more about ComponentDataArray. I still feel like I work wrong with ECS.

Do you mean something like this?

public struct Player : IComponentData {
    public DynamicBuffer<AddMoney> addMonies;
}

public struct Money : IComponentData {
    public float value;
}

public struct AddMoney : IBufferElementData {
    public float value;
}

public struct Captain : IComponentData {
    public Entity player;
}

So, how would you implement this following ECS territory?

There is no need (I don’t even think you can) add the dynamic buffer inside an icompnentdata.

There a several ways to implement this. And yes it feels like creeping back in OOP territory wich is why I said in my first post that not every thing qhould be solve with DOTS.

So we have :
A player that has money and controls captains
Each captain can earn or spend money (chance the player money data)

For the player’s money a simple Money (IComponentData) on a player entity should be enougth.
The complexity comes with the relationship between the captain and the player. We have two relationships one is control and one is earning/spending money.

For the first relationship it is easier to have a ControledEntites (IBufferElement) on the player entity that contains a reference to all the Captain entities that are controled by the player.
Then you can add an PlayerInput (IComponentData) to the player and read it in a system that sets each captains movement or target or whatever. (note that I ignore the selection logic and assume the player always control all the captains at once for simplicity reasons)

For the earn/spend money relationship it’s trickier. The money change on the player but the interaction that lead to that change is triggered by the captains.
The game needs a way to tell this captain owned by this player earned/spent money. Let’s ignore the other part of the transaction for now (other unit destroyed or what was bought) again for simplicity reasons.

What you could do is add a MoneyChange (IBufferElement) on the player entity. Add a OwnedBy (IComponentData) on each captain to reference the player entity.
Then a system could add a MoneyChange element to the player referenced by the OwnedBy component data. Then another system process all the elements in the buffer and update the player’s amount before removing the element from the buffer.

That’s it for one possible implementation. It may not be the simplest one, it may not be the most performent one but it’s one that should work.

This implementation does have the bidirectional relationship between player and captain. So there is the possibility for mistake like said vestigal. Without going intondetaios, it’s a tradeoff between simplicity of use, mermory performance and cpu performance.

As a side note I think you should be carrefull of your namings. For me AddMoney implies that it will increase the amount of money the player has, you don’t want to create a spend money component for the spend operation. ChangeMoney is more neutral and can be used in both ways (addition and subtraction) without being biased by the name.

1 Like

oh, yes, you right about dynamic buffer, I had to put on the player entity directly.

At first, thanks a lot for your time and detailed descriptions, this way of implementation seems clear for me
About naming I completely agree, also think to call it ChangeMoney, for AddMoney I assumed negative changes also.
So, now it’s getting more clear for me and I definitely will implement it like this for now. But maybe someone else knows another way to do it?

Glad i could help :slight_smile:
Let us know how it turned out for you, couole of screenshot or a video maybe :wink:

1 Like

Would developing a prototype in OOP Mono be faster and could you then analyse the method calls and data passing to create a set of systems?

Potential benefits are you can probably develop this faster and more agilely than using DOTS and if the 80/20 optimisation rule applies (only 20% of your code will be called enough to need optimisation and only profiling will find it) then you would only need to move 20% of your code over to DOTS.

1 Like

Can I just say that you need to provide the data that makes up the items here as well as an outline of the things they can do to even start to break things down into systems and entities.

A diagram more like this would be ideal:

What data and actions does a Player have, or a Captain or a Ship or a Module?

Does a captain live on a ship as well as direct ships?
What happens when your captains ship is sunk and what happens to their funds?
Shouldn’t there be other ranks lower than a captain in charge of other ships?
What is the money used for?
What can a Player get their Captains to do, what can Captains get their Ships to do and what do modules do for ships?

What about fuel, ammo, crew, supplies, water, air (space ships?)?

Can’t a simple flat ID system and Owner ID tag create any hierarchy of entities you need?

In my setup I have many different Entities that can earn and use money. I use ChangeMoney as an event Entity. It contains the money value, it could have a bool to determine add/remove (or negative values for remove) and an Entity reference to the Player Entity. Then there’s a ChangeMoneySystem that runs whenever these components appear, makes the modifications and then destroys all Entities in the EntityQuery.

Depending on how many Captain Entities you have, it may be efficient to make the PlayerID reference an ISharedComponent. That way when you want all the associated Captain Entities, you get the Player ID and use it as a shared component filter. This gives you an EntityQuery of the associated Captain Entities without the need for DynamicBuffers or iterating through all Entities.

1 Like

Hi, I did consider the entity event “patern” my issue is that then your eventventities can be run in a multithreaded context because you can’t garanties that no two ChangeMoney event entities will act on the same player money.

With dynamic buffer you can at least process several player in parrallel.

Does the shared component you talk about solve that issue? If so I would be interested in and exemple implementation:)

1 Like

Ah yes, hadn’t considered parallel access to the same Entity.

No, the shared component is not related to the ChangeMoney aspect. It’s an alternative for your parent child hierarchy.

1 Like