ECS (with specs) for turn-based game


#1

I have been experimenting with the Entity-Component-System paradigm in game developement (specifically by using the excellent crate specs). Point in question, I am working on turn-based games, e.g. a roguelike.

After some effort I managed to get the whole thing running, but then I started wondering. The more I read about it, the more it looks to me like ECS was thought to be used on real-time games, where systems run continously and in parallel. Adapting it to my turn-based game produced some weird quirk: systems have to run one at a time, and are mostly exclusive!

E.G. I have a system making an entity step in some direction and changing its position accordingly, which only runs when the entity currently playing actually perform a step; otherwise, the step system remains silent.

While this seems to suggest ECS is not suited to my use-case, the Entity-Component part actually worked great: it gave my entities a great deal of flexibility, yielding the benefits ECS promises in this sense compared to OOP.

So I am wondering whether I should change my approach. I could, for example, define a single Entity type containing all possible components behind an Option, and then store those in a huge Vec. To make an explicit example of what I mean:

// an example component
struct Life {
  pub max_hp: u8,
  pub current_hp: u8,
}

// another example component
struct Movement {
  speed: u8,
}

// a generic entity, owning its components
struct Entity {
  id: usize,
  life: Option<Life>,
  movement: Option<Movement>,
}

// the main game type
struct Game {
  // storing all the components
  world: Vec<Entity>,
}

impl Game {
  // main method to interact with the game:
  // an entity performs some action
  pub fn play_action(&mut self, entity_id: usize, action: Action) {
    // apply action to game following the game's rules
  }
}

Pros and cons of this approach: I expect it to be unoptimized, but, after all, speed is not my main concern. On the other side, it is much more explicit and easy than a full-fledged ECS like that provided by specs.

So, am I grossly misunderstanding ECS? Should I stick to it, pursue my example, or something different altoghether?


#2

I have a (somewhat hacky) example of using Specs in a roguelike here. The ECS runs at a real time tick rate, doling out ‘energy’ to game entities. When they have enough energy, it’s their turn (and if they’re marked as a player, the game stops giving out energy until the user inputs an action, effectively pausing).

This kind of real-time/turn-based hybrid is nice, because it means you can drive things that aren’t turn-based off the same ECS instance (animations, maybe? I didn’t get that far :p)

My implementation is probably a bit rubbish (and I definitely don’t think I’m using Specs to its full potential), but hopefully it’ll give you some ideas!

Other recommended reading:

http://journal.stuffwithstuff.com/2014/07/15/a-turn-based-game-loop
https://gridbugs.org/modifying-entity-component-system-for-turn-based-games/

EDIT:

Oh, also! If I remember correctly Zemeroth (a turn-based strategy game written in Rust) uses some form of ECS. Their code is approximately 5000% better than mine, so check that out too :stuck_out_tongue:


#3

That’s great! I actually tried it and it worked without any issues. The mixed turn/real-time approach is really interesting.

I actually knew this post before and extensively took inspiration from it. I’m afraid my concerns apply here too: indeed, the author recognises that it’s not using the system part of ECS. I’m doing something similar to its Actions, but I tend to see those as small systems, each executing their small task when required. It kind of works, but I’m worried I may be misusing the ECS concepts and making things overly complicated.


#4

The “turn based game” is kind of a myth, from an engine development perspective. Almost all games end up as interactive simulations (aka “real time”) on the basis that they are… interactive programs. If you want to process input, render animations and menus, play music and sounds, compute AI decisions, load data from disk, etc., then you’re going to want some kind of continuous async “game loop” driving things.

Now if your game has turn based gameplay, then that’s something that you implement in game logic, independent of the fact that the program is an interactive simulation. Even real time games use turn-based logic, even if it doesn’t look like it per se: many games use state machines to model the state of the game. In a turn-based game, your states are “waiting for input” and “responding to input”, and you dispatch accordingly, depending on what is needed each frame. But a real-time game might do the same thing with states like “climbing a ladder” and “rolling after a jump”, etc. So a turn-based game is just a more visible case of the behavior you’d implement in any other game.

A system is still a system, whether you need to process everything in one frame, spread out over many, or continuously. I don’t think it is a misuse of ECS to do things your way. Turn-based gameplay just really has no significant bearing on how you build a game engine, unless you know up front that you don’t want to animate or process anything between turns. Very few games want to take that road.


#5

If I remember correctly Zemeroth (a turn-based strategy game written in Rust) uses some form of ECS.

I actually gave a short talk about structuring turn-based games in Rust a few weeks ago - video/slides - but everything is in Russian. (I should really consider a blog post in English about this theme)

Long story short, I believe that you don’t need that much different systems for turn-based game logic, so Zemeroth uses just a hashmap-based storage for components and focuses on command->event dataflow + declarative animation system.

Soo, I mostly agree with the topic-starter :slight_smile:


#6

Unfortunately my Russian is not very good, but if you ever write that post in English I’ll be most interested! :sweat_smile:
PS: Zemeroth looks amazing, keep it up!

I see your point and I would expect it to hold in general. Though, I suspect (canonical) Roguelikes may be a notable exception to the rule. In those games there is little to no graphics and animations, but the game logic is quite complex. Maybe a pure turn-based engine* may still be adequate to their needs, while simplifying the game logic management.

(*) or, rather, the model of the game. The view/controller, if a MVC pattern is being used, can/should still be real-time based, thus even allowing some simple animation.