apecs: A fast ECS for game engine programming

[ bsd3, control, data, game, library ] [ Propose Tags ]

A fast ECS for game engine programming

[Skip to Readme]
Versions [faq],,,,,,,,,,,,,,,,,,,,,,,,,,,,, 0.7.0, 0.7.1, 0.7.2, 0.7.3, 0.8.0, 0.8.1, 0.8.2, 0.8.3, 0.9.0, 0.9.1, 0.9.2
Dependencies base (>=4.9 && <4.11), containers, mtl, vector [details]
License BSD-3-Clause
Author Jonas Carpay
Maintainer jonascarpay@gmail.com
Revised Revision 1 made by HerbertValerioRiedel at 2019-02-03T20:01:36Z
Category Game, Control, Data
Home page https://github.com/jonascarpay/apecs#readme
Uploaded by jonascarpay at 2017-09-23T15:50:04Z
Distributions LTSHaskell:0.7.3, NixOS:0.9.2, Stackage:0.7.3
Downloads 19761 total (259 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2017-09-23 [all 1 reports]




Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

For package maintainers and hackage trustees

Readme for apecs-

[back to package description]


hackage | documentation | tutorials

apecs is an Entity Component System inspired by specs and Entitas. It exposes a DSL that translates to fast storage operations, resulting in expressivity without sacrificing performance or safety.

There is an example below, and a tutorial can be found here. For a general introduction to ECS, see this talk or here.


Performance is good. Running the ecs-bench pos_vel benchmark shows that we can keep up with specs, which was written in Rust:

| | specs | apecs | | ------ | ------ | ------ | | build | 699 us | 285 us | | update | 34 us | 46 us |


import Apecs
import Apecs.Stores (Cache)
import Linear

-- Component data definitions
newtype Velocity = Velocity (V2 Double) deriving (Eq, Show)
newtype Position = Position (V2 Double) deriving (Eq, Show)
data Enemy = Enemy -- A single constructor for tagging entites as enemies

-- Define Velocity as a component by giving it a storage type
instance Component Velocity where
  -- Store velocities in a cached map
  type Storage Velocity = Cache 100 (Map Velocity)

instance Component Position where
  type Storage Position = Cache 100 (Map Position)

instance Flag Enemy where flag = Enemy
instance Component Enemy where
  -- Because enemy is just a flag, we can use a set
  type Storage Enemy = Set Enemy

-- Define your world as containing the storages of your components
data World = World
  { positions     :: Storage Position
  , velocities    :: Storage Velocity
  , enemies       :: Storage Enemy
  , entityCounter :: Storage EntityCounter }

-- Define Has instances for components to allow type-driven access to their storages
instance World `Has` Position      where getStore = System $ asks positions
instance World `Has` Velocity      where getStore = System $ asks velocities
instance World `Has` Enemy         where getStore = System $ asks enemies
instance World `Has` EntityCounter where getStore = System $ asks entityCounter

type System' a = System World a

game :: System' ()
game = do
  -- Create new entities
  ety <- newEntity (Position 0)
  -- Components can be composed using tuples
  newEntity (Position 0, Velocity 1)
  -- Tagging one as an enemy is a matter of adding the constructor
  newEntity (Position 1, Velocity 1, Enemy)

  -- Side effects
  liftIO$ putStrLn "Stepping velocities"
  -- rmap maps a pure function over all entities in its domain
  rmap $ \(Position p, Velocity v) -> Position (v+p)

  -- Set can be used to (over)write components
  set ety (Position 2, Enemy)

  -- Print the positions of all enemies
  cmapM_ $ \(Enemy, Position p) -> liftIO (print p)

main :: IO ()
main = do w <- World <$> initStore <*> initStore <*> initStore <*> initCounter
          runSystem game w