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.1.0.0, 0.1.1.0, 0.2.0.0, 0.2.0.1, 0.2.0.2, 0.2.0.3, 0.2.1.0, 0.2.1.1, 0.2.2.0, 0.2.3.0, 0.2.4.0, 0.2.4.1, 0.2.4.2, 0.2.4.3, 0.2.4.4, 0.2.4.5, 0.2.4.6, 0.2.4.7, 0.3.0.0, 0.3.0.1, 0.3.0.2, 0.4.0.0, 0.4.0.1, 0.4.1.0, 0.4.1.1, 0.5.0.0, 0.5.1.0, 0.5.1.1, 0.6.0.0, 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 apecs, base (>=4.9 && <4.11), containers, linear, mtl, random, sdl2, vector [details]
License BSD-3-Clause
Author Jonas Carpay
Maintainer jonascarpay@gmail.com
Revised Revision 1 made by HerbertValerioRiedel at 2019-02-03T20:01:35Z
Category Game, Control, Data
Home page https://github.com/jonascarpay/apecs#readme
Uploaded by jonascarpay at 2017-09-16T03:44:25Z
Distributions LTSHaskell:0.7.3, NixOS:0.9.2, Stackage:0.7.3
Executables rts, simple
Downloads 19552 total (170 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-16 [all 1 reports]

Modules

[Index]

Downloads

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-0.2.0.2

[back to package description]

apecs

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

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 |

Example

import Apecs
import Apecs.Stores
import Apecs.Util
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