apecs
Fast ECS framework for game programming
https://github.com/jonascarpay/apecs#readme
Version on this page: | 0.7.1 |
LTS Haskell 23.19: | 0.9.6 |
Stackage Nightly 2025-04-24: | 0.9.6 |
Latest on Hackage: | 0.9.6 |
BSD-3-Clause licensed by Jonas Carpay
Maintained by [email protected]
This version can be pinned in stack with:
apecs-0.7.1@sha256:453f1e8e6a1858c87deb00dc1022829e7147a31dfbf6979f9ce80124efeb27db,1994
Module documentation for 0.7.1
Depends on 5 packages(full list with versions):
Used by 2 packages in lts-13.1(full list with versions):
apecs
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. apecs aims to be
- Fast - apecs is designed for high-performance applications. Its performance is competitive with Rust ECS libraries.
- Simple - Game logic is expressed using a small number of combinators, and minimal boilerplate.
- Safe - The
cmap
/cfold
-DSL hides all the dangers of the low-level API. - Extensible - apecs can be used with anything that implements the low-level API. See apecs-physics or apecs-stm for examples.
Links
Performance
ecs-bench shows that apecs is competitive with the fastest Rust ECS frameworks.
Example
{-# LANGUAGE DataKinds, FlexibleInstances, ScopedTypeVariables, TypeFamilies, MultiParamTypeClasses, TemplateHaskell #-}
import Apecs
import Control.Monad
import Apecs.Util
import Linear (V2 (..))
newtype Position = Position (V2 Double) deriving Show
newtype Velocity = Velocity (V2 Double) deriving Show
data Flying = Flying
makeWorldAndComponents "World" [''Position, ''Velocity, ''Flying]
game :: System World ()
game = do
newEntity (Position 0, Velocity 1)
newEntity (Position 2, Velocity 1)
newEntity (Position 1, Velocity 2, Flying)
-- 1. Add velocity to position
-- 2. Apply gravity to non-flying entities
-- 3. Print a list of entities and their positions
cmap $ \(Position p, Velocity v) -> Position (v+p)
cmap $ \(Velocity v, _ :: Not Flying) -> Velocity (v - V2 0 1)
cmapM_ $ \(Position p, Entity e) -> liftIO . print $ (e, p)
main :: IO ()
main = initWorld >>= runSystem game
Changes
[0.7.1]
Added
$=
and$~
operators as synonyms forset
andget
respectively
Removed
getAll
andcount
, which were made redundant bycfold
.
[0.7.0]
Added
- The
Reactive
store and module is a redesign of theRegister
store, and provides a more general solution for ‘stores that perform additional actions when written to’. - The
Apecs.Stores.Extra
submodule, which contains thePushdown
andReadOnly
stores.Pushdown
adds pushdown semantics to stores, andReadOnly
hides theExplSet
instances of whatever it wraps. - The
EntityCounter
and associated functions have all been specified toIO
, sinceGlobal EntityCounter
only works in IO. Furthermore,EntityCounter
now uses aReadOnly
store, to prevent users from accidentally changing its value. Redirect
component that writes to another entity incmap
.
Changed
- Default stores have
MonadIO m => m
instances, rather thanIO
. This makes it easier to nestSystemT
. - All apecs packages have been consolidated into a single git repo.
Apecs.Components
contains the components (and corresponding stores) fromApecs.Core
.
[0.6.0.0]
Changed
- Nothing, but since 0.5.1 was API-breaking I’ve decided to bump to 0.6
[0.5.1.1]
Changed
Register
needs UndecidableInstances in GHC 8.6.2, I’m looking for a way around this. I’ve removed it for now.
[0.5.1.0]
Added
- The
Register
store, which allows reverse lookups for bounded enums. For example, ifBool
has storageRegister (Map Bool)
,regLookup True
will yield a list of all entities with aTrue
component. Can also be used to emulate a hash table, wherefromEnum
is the hashing function. This allows us to make simple spatial hashes. I’m open to suggestions for better names than Register. cmapIf
, cmap with a conditional test
Changed
ExplInit
now too takes a monad argument.- Started rewrite of the test suite
- Caches now internally use -2 to denote absence, to avoid possible conflict with -1 as a global entity
Removed
- The STM instances have been removed, to be moved to their own package
[0.5.0.0]
Changed
System w a
is now a synonym forSystemT 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.
Added
- STM stores. These will be moved to a separate package soon.
[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 andEitherStore
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
, andExplMembers
. This makes it illegal to e.g. iterate over aNot
.- phantom arguments are now given as
Proxy
values, re-exported fromData.Proxy
. This makes phantom arguments explicit and avoids undefined values.