apecs

Fast ECS framework for game programming

https://github.com/jonascarpay/apecs#readme

Version on this page:0.5.0.0
LTS Haskell 22.17:0.9.6
Stackage Nightly 2024-04-19:0.9.6
Latest on Hackage:0.9.6

See all snapshots apecs appears in

BSD-3-Clause licensed by Jonas Carpay
Maintained by [email protected]
This version can be pinned in stack with:apecs-0.5.0.0@sha256:7208be1035e28e20e7fd93e9094d322cedd376ca4e6a9959da0c659a308e67f2,1951

Module documentation for 0.5.0.0

apecs

Build Status Hackage Stackage

apecs is an Entity Component System (ECS) framework inspired by specs and Entitas. ECS presents a data-driven approach to game development, that elegantly tackles many of the unique issues of game programming. The apecs front-end DSL exposes a small number of combinators that allow game logic to be expressive and extremely fast.

Links

Performance

ecs-bench shows that apecs is competitive with the fastest Rust ECS frameworks.

Benchmarks

Example

{-# LANGUAGE DataKinds, ScopedTypeVariables, TypeFamilies, MultiParamTypeClasses, TemplateHaskell #-}

import Apecs
import Linear (V2 (..))

newtype Position = Position (V2 Double) deriving Show
-- To declare a component, we need to specify how to store it
instance Component Position where
  type Storage Position = Map Position -- The simplest store is a Map

newtype Velocity = Velocity (V2 Double) deriving Show
instance Component Velocity where
  type Storage Velocity = Cache 100 (Map Velocity) -- Caching adds fast reads/writes

data Flying = Flying
instance Component Flying where
  type Storage Flying = Map Flying

makeWorld "World" [''Position, ''Velocity, ''Flying] -- Generate World and instances

game :: System World ()
game = do
  newEntity (Position 0, Velocity 1)
  newEntity (Position 2, Velocity 1)
  newEntity (Position 1, Velocity 2, Flying)

  -- Add velocity to position
  cmap $ \(Position p, Velocity v) -> Position (v+p)
  -- Apply gravity to non-flying entities
  cmap $ \(Velocity v, _ :: Not Flying) -> Velocity (v - (V2 0 1))
  -- Print a list of entities and their positions
  cmapM_ $ \(Position p, Entity e) -> liftIO . print $ (e, p)

main :: IO ()
main = initWorld >>= runSystem game

Changes

[Unreleased]

Changed

  • System w a is now a synonym for SystemT w IO a. A variable monad argument allows apecs to be run in monads like ST or STM. Most of the library has been rewritten to be as permissive as possible in its monad argument.

[0.4.1.2]

Changed

  • Either can now be deleted, deleting Either a b is the same as deleting (a,b).
  • Some were missing their inline pragma’s, now they don’t

[0.4.1.1]

Changed

  • Export Get, Set, Destroy, Members by default
  • Export cfold, cfoldM, cfoldM_ by default
  • Fix () instance

[0.4.1.0]

Added

  • cfold, cfoldM, cfoldM_
  • Either instances and EitherStore

Changed

  • Changed MaybeStore implementation to no longer use -1 for missing entities.
  • Fixed some outdated documentation.
  • Change the global void entity to -2, just to be sure it won’t conflict if accidentally used in a cache.

[0.4.0.0]

Added

  • A changelog

Changed

  • Store is now split into 5 separate type classes; ExplInit, ExplGet, ExplSet, ExplDestroy, and ExplMembers. This makes it illegal to e.g. iterate over a Not.
  • phantom arguments are now given as Proxy values, re-exported from Data.Proxy. This makes phantom arguments explicit and avoids undefined values.