0
\$\begingroup\$

I'm trying to re-write a small game of mine using ECS mostly for educational purposes and to see if it fits top-down RPG games in the style of Pokemon (gameboy). Since everything is tile based, initially I had a Position component that only holds the position of the player, and a Controller/Input component that, when a movement keypress is detected, starts to change the player's position until it falls into a tile. Then, the Rendering component would render the player based on a vector of clips that simulate the movement.

Thinking that my player should only move in tiles, I thought that it might be better to change the position tile-by tile every time, and then use some sort of interpolation in the Animation or Rendering component to figure out which of the clips to use (left foot forward, right foot forward etc.). This proved to be much less error prone, but now this means that I need to keep the actual position and the "visual" position (the one you can see on the screen). Thinking that logic should be not mixed with rendering, it seems that the "correct" thing would be to not keep the "visual" position in the Position component, but in one of the "front end" components like Animation or Rendering. Which would be the most correct thing to do? By correct meaning the thing that will keep the code truly decoupled in the vein of pure ECS.

\$\endgroup\$
  • 1
    \$\begingroup\$ Are you really using Haskell? \$\endgroup\$ – Vaillancourt May 26 at 18:01
  • 1
    \$\begingroup\$ Yes - or at least trying to :/ \$\endgroup\$ – alexpeits May 26 at 18:34
0
\$\begingroup\$

I haven't touched Haskell for about 17 years, so this answer will be more about an approach than an implementation... sorry.


Which would be the most correct thing to do? By correct meaning the thing that will keep the code truly decoupled in the vein of pure ECS.

I don't think there is a most correct thing to do, and I don't think a pure ECS exists: there is a correct thing to do for your context, and there is an ECS that works for you and for your game. The rest is pressure that you put on your shoulders, and it's hard to define what value this pressure adds to your skills and to your game.

So "perfectly" decoupling the logic from the visual might not be needed.


What if your "position" was actually the "world's position"? And you let your renderer convert world positions to screen position. (This way, you now think in "tiles per second" and not in "pixels per second", which can help port your game more easily to different resolutions -- everyone will be traveling at the same speed.)

Your world is based on tiles, so it could be fine to keep the tiles as "integer" values in "float" variables, but if your character needs to move then you can still use "float" values to place it.

Your position is then "logic", and not "visuals".

Then, what you could do is to work with a "state" in your animation component, along with a ratio and a starting and ending point.

Your character will be "walking". It's a logic state: where is their left foot - up or down? Where is their right foot - up or down? You'll need to know that eventually to produce the appropriate sounds, to produce the puffs of dust when walking on the sand, etc, so you can consider it's not just for the visuals.

Then you could have this kind of data in your animation component:

  • Start (either the tile, or tile world coordinates)
  • End (either the tile, or tile world coordinates)
  • ProgressRatio ([0..1], where is the character between the Start and End)
  • ProgressSpeed (how much progress should be done per frame, or per second, etc.)
  • StatesList (LeftFootUp, RightFootUp, etc... this will match your animation)
  • CurrentState (pointer to the current state)

Then each frame, a system will progress the Ratio by the Speed, then using the ratio, will update the current state (say, if your list has 4 states, the animation will have 4 frame, so 25% each; when the ratio will be at 33%, CurrentState will now point to the second state). The Position component will be updated with the start-end-ratio combo, while the render component, the one that holds the sprites, will be updated with the state.

I don't know if it makes any sense for your situation, but I hope it helps :)

\$\endgroup\$
  • \$\begingroup\$ Thanks for the very detailed response. This seems very close to the direction I'm going with. Let me see if I got this right: - the movement system will update the position (in tile coordinates) by checking for keypad events - the animation system has all this information you mentioned so it can determine if an entity has moved, so it can animate the movement and set the correct position for the rendering system Only thing I don't get is: do you propose storing the tile-based position and the visual position in the Position component? \$\endgroup\$ – alexpeits May 29 at 10:34
  • \$\begingroup\$ @alexpeits Hey just to mention that I did not forget your question, I need to test something before answering. \$\endgroup\$ – Vaillancourt Jun 1 at 2:49
0
\$\begingroup\$

If the visual position is needed by one set of systems and the logical position by another set of systems, then it might be a good idea to have a separate component for each.

  • Have the Animation system and the Rendering system read the VisualPosition components.
  • Have the game-mechanical systems read the LogicalPosition components.
  • Have the Movement System update both visual and logical position components.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.