Sign in to follow this  

ECS - Renderable, Animation, Physics

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

I am running into some road blocks attempting to effectively implement ECS methodology. I have a heavy background in OOP, and I think that is really tainting my perspective.

 

According to a lot of the information I have gathered regarding ECS, one of the primary benefits is that each system is supposed to work independently, and also, using components should help avoid memory cache issues since components should be stored in contiguous Memory.

 

 The problem that I am experiencing is the following.

 Given a particular element for which Rendering, Animation and Physics need all be applied there is a particular sub set of information which must be available and synchronized to all systems, while an overlapping subset of information may not require availability and synchronization.

 

 For the rendering system to work it requires a component containing a Sprite, a position and a rotation.

 For the physics system to work it requires a component containing a position, a force and a rotation.

 

The animation system is slightly more complex, as there are three types of animation

  1) Tile: No animation, requires only a sprite

  2) Simple Animation: Loops through a single animation, requires a set of sprites

  3) State Animation: Infers sprite from a state and a TimeInState, requires a State and a set of sets of sprites.

 

So it seems that there is the following portions of data required for the ECS as a whole (not partitioned into components):

   Sprite - Current sprite representative of entities animation

               Rendering system Read

               Animation system Write

 

   Sprites - Complete set of sprites available to entity

                  Animation system Read

 

   State   - Current state of entity

                <Some System> Write

                Animation System Read

 

   TimeInState - How long entity has been in a particular state, to enable mapping to a specific frame of a specific animation

                  <SomeSystem> Write

                  AnimationSystem Read

 

   Position - Both physics position and drawing position (while in an idealized situation they would be equal in actuality they are only tightly coupled through some mapping function i.e drawing Y often equals -physics Y, and physics often work of Center of gravity while rendering works on either top left corner, or center of sprite)

                  Physics system Read/Write

                  Rendering System Read

 

   Rotation - Both physics position and drawing position (similar issues to position, but possible to avoid)

                 Physics system Read/Write

                 Rendering System Read

 

   Force - Physics

                <some system> Write

               Physics system Read

  -----------------------------------------------

As you can see, it becomes a rats nest of inter connectivity destroying the System Independence principle. Also, since each system uses multiple components it also destroys the Contiguous memory access principal as well. Some sources concede that complete system independence is not always possible but the breakage is acceptable as long as the more performance enhancing principal of contiguous memory access is preserved, this is seems would require Data duplication, something that I find great difficulty in accepting as good practice (this may be due to OOP and Database bias). 

 

The animation Components present a new challenge entirely... namely, how to efficiently store jagged arrays in contiguous memory. 

In my particular case it turns out to be a jagged array of jagged arrays. I plan to use a image packing software to create image atlases, the image packing software produces a string->box mapping file so a particular sprite can be located by providing the sprites String formatted Name/Id. Also, since various states may have more or less frames than other states, and since some entities may have more or less states than others you can see that the first jagged array is entity to states, followed by states to frames and finally frames to strings.

 

I suppose the gist of this post is.... ARRRRGGGG, WTF?!?! HELP PLZ!!

Edited by Paragon123

Share this post


Link to post
Share on other sites

You could try packaging the data in some other way to minimize this cross-accessing. It seems you want your AnimationSystem to do a bit much, you could split it up such that one part is doing the logic and then have another System for choosing a texture from some cache based on that data and save a pointer to the current texture and data about how its used inside a texture component.

You could then package the position and rotation into one component and then have the rest of the physics data inside another. Then the rendering system could read the position/rotation component and texture component.

I think parallel reading from one array of components that some other system already wrote to and reading/writing out to the own component array shouldn't be that bad, but its something to be minimized.

Share this post


Link to post
Share on other sites

Well, the purpose of an ECS is to get away from OOP and its intertwined calltrees to have a nice clean structure, where you get a fixed list of systems that do straight iterations through full arrays of components in a fixed order. For example, you first call the physics system which updates all positions and then call the render system which reads all positions.

I dont see how the observer pattern, which is basically spaghetti-code repackaged into OOP, can help you on that, when it generates back-and-forth jumping between single components of different types. For example that would mean every single time the physics system updates one of the position components there would be a render component observing it and the code would just jump there, then back, then on the next component again and so on with the contraption.

 

I would suggest you read the article series at http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/.

Edited by wintertime

Share this post


Link to post
Share on other sites

In terms of different systems accessing the same components (your "rats nest of inter connectivity"), I don't see this as a big problem. The systems still operate independently.

 

For instance, the physics system updates the positions of entities, then the rendering system draws them at those positions. The rendering system still doesn't have a dependency on the physics system though. You may need to be very explicit about the order in which systems get updated though (this isn't a new problem that an ECS framework brings to the table though).

 


Also, since each system uses multiple components it also destroys the Contiguous memory access principal as well.

 

That is true.

 

Regarding your animation, I also have an ECS framework with sprites that are animated from a texture atlas. The way it works is:

 

- The "Sprite" component identifies a texture atlas and a frame from that texture atlas.

- The "Animation" component identifies a sequence of frames (e.g. go from frame 10 to frame 20 in 5 seconds) and a current frame number.

 

The Animation system is then responsible for advancing the frame number if it's time, and then modifying the Sprite component's frame number. (So it depends on both the Animation and Sprite components).

 

Anything more complicated than that (such as your "state animation") is handled by custom scripts attached to the entity, which would then create the necessary Animation component to perform whatever animation is needed. If you have a well thought-out declarative way to express your state -> animation mapping, then I would suggest having a separate "StateAnimation" system whose job it is to create/initialize the Animation component on an entity based on its state. (And then the Animation system would take care of the rest).

Share this post


Link to post
Share on other sites

Well, the purpose of an ECS is to get away from OOP and its intertwined calltrees to have a nice clean structure, where you get a fixed list of systems that do straight iterations through full arrays of components in a fixed order. For example, you first call the physics system which updates all positions and then call the render system which reads all positions.

I dont see how the observer pattern, which is basically spaghetti-code repackaged into OOP, can help you on that, when it generates back-and-forth jumping between single components of different types. For example that would mean every single time the physics system updates one of the position components there would be a render component observing it and the code would just jump there, then back, then on the next component again and so on with the contraption.

 

I would suggest you read the article series at http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/.

 

http://en.wikipedia.org/wiki/Entity_component_system

Share this post


Link to post
Share on other sites
I'll just put this here:
http://en.wikipedia.org/wiki/Talk:Entity_component_system#No_canonical_ECS
 

Well, the purpose of an ECS is to get away from OOP and its intertwined calltrees to have a nice clean structure, where you get a fixed list of systems that do straight iterations through full arrays of components in a fixed order.

According to a lot of the information I have gathered regarding ECS, one of the primary benefits is that each system is supposed to work independently, and also, using components should help avoid memory cache issues since components should be stored in contiguous Memory.

That depends on which flavour of ECS you're following. Many of the flavours do not address or achieve this goal at all.
 

As you can see, it becomes a rats nest of inter connectivity destroying the System Independence principle.

Break down the data-flow between systems first. Pretend that you've got a variable that can hold the entire contents of a system:
positionsRotations = ...
sprites = ...
forces = gameplaySystem.Update(positionsRotations)
newPositionsRotations = physicsSystem.Update( positionsRotations, forces )
newSprites = animationSystem.Update( sprites )
renderSystem.Update( newPositionsRotations, newSprites )
This is the overall glue between the systems. If this glue is external to them, as above, then the systems can all be completely isolated and independent. This glue layer creates the dependencies.
In a real usage, you also wouldn't be creating clones of your data (i.e. you wouldn't have positionsRotations and newPositionsRotations), but when you write it out this way, the required order of system updates becomes obvious.
 
As for ensuring all your data is contiguous, this is a very nice memory optimization... but it's just that -- an optimization. Don't lose sleep over it to begin with. If you've actually got performance problems, then sure, go lose some sleep and pull your hair out... but until then, relax and get things working first.
 
If a system needs to iterate through two sets of contiguous data, that's still pretty great. It's way better than two sets of random data, or even one set of random data.
 
As for jagged arrays, you can store them in two contiguous blocks. Have one block store each fixed size header, then have all the variable-sized parts stored end-to-end in another contiguous block. As long as you're never removing items, this is very easy. If you're removing items, you'll have to defragment the second block, and fix-up all your pointers into it...
struct VariableSize { int size; int* data; };
VariableSize index[256];
int allData[2048]; // index[i]->data will always point somewhere into this array
Edited by Hodgman

Share this post


Link to post
Share on other sites

This topic is 1509 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.

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

Sign in to follow this