Componentile: a minimalist ECS-like data structure library for tile-based games


https://crates.io/crates/componentile

This is a structure of arrays optimized for discrete spaces, where instead of having entity IDs, components live directly in array indexes. It supports chunking for cache-locality. It has 2 types of storages at the moment: one is a bitset array for booleans, and the other an array for any other kind of Copy type.

Some nice minor extras are an iterator with automatic bounds-checking, plus an extra, manual method for skipping 8 elements at a time.

The basic use is to define a world using a macro which chooses the appropriate storages and defines the World impl automatically.

define_world! {
    foo: bool,
    bar: (u8, u8),
    baz: &str,
}

Now you have a World.

    let mut world = World::new();
    let (x, y) = (0, 2);
    world.foo.put(true, x, y);
    assert_eq!(world.get(x, y), true);

Some trade-offs to be aware of: I've opted for allocation of an array the size of the world for each component. These may be very sparse arrays and consume a lot of memory. However, indexing is O(1) and spatially-local components are guaranteed to be cache-local. For a tile-based game, this seems like a sane trade-off, though I may support sparse component storages in the future.

Future plans include:

  • a 3rd dimension, probably only 8 blocks high
  • const generics to make it easier to define a World of size other than 1024x1024 cells, without having to modify const declarations in the library file
  • perhaps a sparse storage

Future plans do not include:

  • Inclusion of ancillary data structures, such as parent-child maps or maps for shared resources such as mesh data a la flyweight pattern. These should be usable with this structure, but this is a minimal structure without opinions of what other structures are used alongside it.

This is part of a game I've been working on and I'll be splitting at least one other piece out into a related, but separate library.

3 Likes