Post by Admin on Jul 16, 2016 4:43:54 GMT
Scott Richmond @strich Dec 16 2015 22:09
Am I right in saying that currently Entitas isn't actually immutable? The properties you define in Components, for example, aren't actually immutable unless they're set as readonly.
Looks like the components on entities are immutable, but properties on components aren't
Simon Schmid @sschmid Dec 16 2015 22:19
We designed Entitas in way that encourages immutable components. But because we’re in a garbage collected environment we recycle the components internally for performance reasons. So techically components are mutable, right. But we pretend they were immutable
Scott Richmond @strich Dec 16 2015 22:26
Just seems like a high price to pay - For example the game board cache component in your match-one demo has to copy its array to a new component every time you add/remove an element. At least my cursory scan of the code suggests that - That seems like a hidden problem just waiting to rear its head if it finds its way into a per-frame loop of some kind.
I've been reading up on the benefits of immutability recently and I believe I understand the value in it - Concurrency safety, etc. But are those values really that important to the common case game?
I don't know yet, personally.
Maxim Zaks @mzaks Dec 16 2015 22:48
Lets say we have immutable mindset but mutable implementation
We are totally aware of the performance problems that comes with immutable data structures, this is why we ask to replace a component but in reality we use the same component put new values in the old component and fire the event that the component was updated, even though technically it is still the same object.
have a look at this generated method
ReplacePosition(int newX, int newY)
github.com/sschmid/Match-One/blob/master/Assets/Sources/Generated/PositionComponentGeneratedExtension.cs#L22
what you are basically asking for with Property Observer is a method like:
ReplacePositionX(int newX)
where you only replace the X property and keep the Y property of the position component.
We actually write those methods our selfs from time to time so that we can have a simpler way of replacing some properties inside of a component.
We could also generate such methods but than as components can have undefined number of properties we would have to generate all the permutations of that properties, and people would use only a small friction of them. This is why we don't do it. We write them manually if we see there is a component which has multiple fields and we would like to change only one property at the time.
What however does not happening with those manual methods we don't send a special events which say that only the X field of the position has changed, we say the position has changed.
Simon Schmid @sschmid Dec 16 2015 23:09
the array from the game board cache in the match one demo will be updated on change. The same instance is used, there are no copies.
var grid = _pool.gameBoardCache.grid;
var pos = entity.position;
grid[pos.x, pos.y] = entity;
_pool.ReplaceGameBoardCache(grid);
calling _pool.ReplaceGameBoardCache(grid) will get a pooled or new component and will set the grid. The previous component is beeing pooled for later reuse. The grid stays the same instance
github.com/sschmid/Match-One/blob/f53ef9b92a9210d698189a37eecc22ad3d8c3937/Assets/Sources/Generated/GameBoardCacheComponentGeneratedExtension.cs#L21-L29
Scott Richmond @strich Dec 16 2015 23:13
I see
Thanks for confirming that for me
It does seem then, that the whole immutable methodology is a bit pointless in this case, surely?
What is it giving us? Is it just the design pattern?
Simon Schmid @sschmid Dec 16 2015 23:15
It gives us what you kind of want with the properties observer
Components could be seen as properties if you want. When ever we change one, we can get notified
This enables e.g. ReactiveSystems
This message was deleted
Scott Richmond @strich Dec 16 2015 23:17
Why not be more granular though? @mzaks has stated he must sometimes break the 'rule' to better write code. However instead of having to implement manual work-around methods one might simply generate the appropriate observer pattern around properties that want/need it.
I like the reactiveSystems - It is definitely required for many components. But, at least in my eyes, the reactive properties would be the common case.
Simon Schmid @sschmid Dec 16 2015 23:22
Maybe it helps to see components as properties. Initially we chose components to be immutable for a few reasons. In other implementations like swift and obj-c components are actually immutable.
I could also imagine that a general KV observer impl might have a negative impact on performance.
For me personally the main reason we currently don’t have it is because it doesn’t fit well with the idea of immutable components
there is basically no change in properties
Scott Richmond @strich Dec 16 2015 23:30
I think one of the key issues I have is a hard time understanding how to manage the view of a component if all I'm told is that it has be destroyed and created. How do I know whether I need to simply tweak the healthbar UI a little or completely reinitialize it. Do I now need to write a lot of checking code - Is the ui already existing? Did the max health var change or was it the current health? Do I actually need to fully destroy the UI because it isn't expected to be required anymore?
Maxim Zaks @mzaks Dec 16 2015 23:30
Well one idea is to put an Attribute on a field in component. e.g.
class PositionComponent{
[NeedsReplaceOfItsOwn] int x;
int y;
int z;
}
than the generator can generate both messages
ReplacePosition(int newX, int newY, int newZ)
ReplacePositionX(int newX)
hm the last comment I don't really get, @strich could you elaborate on this more please?
I guess you mean that if you don't know what the previous version of the component was you don't know how it changed, am I correct?
Scott Richmond @strich Dec 16 2015 23:35
Yes, more or less
Maxim Zaks @mzaks Dec 16 2015 23:39
We have a couple of ways to be able to see the previous and the new values which will be added to the entity
On entity
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Entity.cs#L12
And also when you subscribe to a group
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Group.cs#L11
Scott Richmond @strich Dec 16 2015 23:42
The problem there would be the requirement to test each current<->previous property
Maxim Zaks @mzaks Dec 16 2015 23:42
If you add some event handlers you will get this things but this will be executed directly on change. Synchronously.
Reactive systems are asynchronous. So in a reactive system you only know that this entity had following component changed but it is all in the past so you don't know what was the value before the change
It could also happen that the component was changed multiple times before the reactive system was called.
This is why we normally use reactive systems because they aggregate change. But if you need a life update and has to know what was the previous data I would recommend to just add Observers to entities or to groups.
We use such observers for example to be able to build up an "index"
Lets say you have a name component and you want to get all the entities which names start with M
You would than have such index which subscribes to group of entities which has NameComponent.
Than when a name of an entity changes You can update the index (or lookup table) because you know what was th previous name and what will the new name be
Maxim Zaks @mzaks Dec 16 2015 23:48
Does this helps?
Scott Richmond @strich Dec 16 2015 23:50
Yeah you're helping me build up an understanding of why Entitas is as it is.
The key question I keep asking myself though is whether it solves things the way we want to solve them - At the end of the day we need to be happy with the core arch we're working within regardless of whether it is the 'best' way or not.
Maxim Zaks @mzaks Dec 16 2015 23:54
Totally agree with you.
And generally I can only explain why it works how it works.
It is than up to you to decide if this is something that you enjoy working with.
Or even make it different because you think we did it wrong.
In every project there are some historical decisions. And some decisions which are based on taste and preference. This doesn't mean that they are right or wrong they are just different.
And I want to thank you to point out this things because this is how we can reevaluate those decisions and think about if they are a matter of taste or actually wrong. I think till now the discussion goes in the direction of taste. which is totally fine.
Scott Richmond @strich Dec 16 2015 23:59
uFrame, for example, I think more accurately fits our common case in the way we code. But we dislike the GUI designer and really enjoy what you guys have done with Entitas as a lightweight but powerful tool.
Am I right in saying that currently Entitas isn't actually immutable? The properties you define in Components, for example, aren't actually immutable unless they're set as readonly.
Looks like the components on entities are immutable, but properties on components aren't
Simon Schmid @sschmid Dec 16 2015 22:19
We designed Entitas in way that encourages immutable components. But because we’re in a garbage collected environment we recycle the components internally for performance reasons. So techically components are mutable, right. But we pretend they were immutable
Scott Richmond @strich Dec 16 2015 22:26
Just seems like a high price to pay - For example the game board cache component in your match-one demo has to copy its array to a new component every time you add/remove an element. At least my cursory scan of the code suggests that - That seems like a hidden problem just waiting to rear its head if it finds its way into a per-frame loop of some kind.
I've been reading up on the benefits of immutability recently and I believe I understand the value in it - Concurrency safety, etc. But are those values really that important to the common case game?
I don't know yet, personally.
Maxim Zaks @mzaks Dec 16 2015 22:48
Lets say we have immutable mindset but mutable implementation
We are totally aware of the performance problems that comes with immutable data structures, this is why we ask to replace a component but in reality we use the same component put new values in the old component and fire the event that the component was updated, even though technically it is still the same object.
have a look at this generated method
ReplacePosition(int newX, int newY)
github.com/sschmid/Match-One/blob/master/Assets/Sources/Generated/PositionComponentGeneratedExtension.cs#L22
what you are basically asking for with Property Observer is a method like:
ReplacePositionX(int newX)
where you only replace the X property and keep the Y property of the position component.
We actually write those methods our selfs from time to time so that we can have a simpler way of replacing some properties inside of a component.
We could also generate such methods but than as components can have undefined number of properties we would have to generate all the permutations of that properties, and people would use only a small friction of them. This is why we don't do it. We write them manually if we see there is a component which has multiple fields and we would like to change only one property at the time.
What however does not happening with those manual methods we don't send a special events which say that only the X field of the position has changed, we say the position has changed.
Simon Schmid @sschmid Dec 16 2015 23:09
the array from the game board cache in the match one demo will be updated on change. The same instance is used, there are no copies.
var grid = _pool.gameBoardCache.grid;
var pos = entity.position;
grid[pos.x, pos.y] = entity;
_pool.ReplaceGameBoardCache(grid);
calling _pool.ReplaceGameBoardCache(grid) will get a pooled or new component and will set the grid. The previous component is beeing pooled for later reuse. The grid stays the same instance
github.com/sschmid/Match-One/blob/f53ef9b92a9210d698189a37eecc22ad3d8c3937/Assets/Sources/Generated/GameBoardCacheComponentGeneratedExtension.cs#L21-L29
Scott Richmond @strich Dec 16 2015 23:13
I see
Thanks for confirming that for me
It does seem then, that the whole immutable methodology is a bit pointless in this case, surely?
What is it giving us? Is it just the design pattern?
Simon Schmid @sschmid Dec 16 2015 23:15
It gives us what you kind of want with the properties observer
Components could be seen as properties if you want. When ever we change one, we can get notified
This enables e.g. ReactiveSystems
This message was deleted
Scott Richmond @strich Dec 16 2015 23:17
Why not be more granular though? @mzaks has stated he must sometimes break the 'rule' to better write code. However instead of having to implement manual work-around methods one might simply generate the appropriate observer pattern around properties that want/need it.
I like the reactiveSystems - It is definitely required for many components. But, at least in my eyes, the reactive properties would be the common case.
Simon Schmid @sschmid Dec 16 2015 23:22
Maybe it helps to see components as properties. Initially we chose components to be immutable for a few reasons. In other implementations like swift and obj-c components are actually immutable.
I could also imagine that a general KV observer impl might have a negative impact on performance.
For me personally the main reason we currently don’t have it is because it doesn’t fit well with the idea of immutable components
there is basically no change in properties
Scott Richmond @strich Dec 16 2015 23:30
I think one of the key issues I have is a hard time understanding how to manage the view of a component if all I'm told is that it has be destroyed and created. How do I know whether I need to simply tweak the healthbar UI a little or completely reinitialize it. Do I now need to write a lot of checking code - Is the ui already existing? Did the max health var change or was it the current health? Do I actually need to fully destroy the UI because it isn't expected to be required anymore?
Maxim Zaks @mzaks Dec 16 2015 23:30
Well one idea is to put an Attribute on a field in component. e.g.
class PositionComponent{
[NeedsReplaceOfItsOwn] int x;
int y;
int z;
}
than the generator can generate both messages
ReplacePosition(int newX, int newY, int newZ)
ReplacePositionX(int newX)
hm the last comment I don't really get, @strich could you elaborate on this more please?
I guess you mean that if you don't know what the previous version of the component was you don't know how it changed, am I correct?
Scott Richmond @strich Dec 16 2015 23:35
Yes, more or less
Maxim Zaks @mzaks Dec 16 2015 23:39
We have a couple of ways to be able to see the previous and the new values which will be added to the entity
On entity
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Entity.cs#L12
And also when you subscribe to a group
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Group.cs#L11
Scott Richmond @strich Dec 16 2015 23:42
The problem there would be the requirement to test each current<->previous property
Maxim Zaks @mzaks Dec 16 2015 23:42
If you add some event handlers you will get this things but this will be executed directly on change. Synchronously.
Reactive systems are asynchronous. So in a reactive system you only know that this entity had following component changed but it is all in the past so you don't know what was the value before the change
It could also happen that the component was changed multiple times before the reactive system was called.
This is why we normally use reactive systems because they aggregate change. But if you need a life update and has to know what was the previous data I would recommend to just add Observers to entities or to groups.
We use such observers for example to be able to build up an "index"
Lets say you have a name component and you want to get all the entities which names start with M
You would than have such index which subscribes to group of entities which has NameComponent.
Than when a name of an entity changes You can update the index (or lookup table) because you know what was th previous name and what will the new name be
Maxim Zaks @mzaks Dec 16 2015 23:48
Does this helps?
Scott Richmond @strich Dec 16 2015 23:50
Yeah you're helping me build up an understanding of why Entitas is as it is.
The key question I keep asking myself though is whether it solves things the way we want to solve them - At the end of the day we need to be happy with the core arch we're working within regardless of whether it is the 'best' way or not.
Maxim Zaks @mzaks Dec 16 2015 23:54
Totally agree with you.
And generally I can only explain why it works how it works.
It is than up to you to decide if this is something that you enjoy working with.
Or even make it different because you think we did it wrong.
In every project there are some historical decisions. And some decisions which are based on taste and preference. This doesn't mean that they are right or wrong they are just different.
And I want to thank you to point out this things because this is how we can reevaluate those decisions and think about if they are a matter of taste or actually wrong. I think till now the discussion goes in the direction of taste. which is totally fine.
Scott Richmond @strich Dec 16 2015 23:59
uFrame, for example, I think more accurately fits our common case in the way we code. But we dislike the GUI designer and really enjoy what you guys have done with Entitas as a lightweight but powerful tool.