packages feed

apecs-0.10.0: src/Apecs.hs

{-|
This module forms the apecs Prelude.
It selectively re-exports the user-facing functions from the submodules.
-}
module Apecs
  ( -- * Core types
    SystemT
  , System
  , Component (..)
  , Entity (..)
  , Has (..)
  , Not (..)
  , Get
  , Set
  , Destroy
  , Members

    -- * Stores
  , Map
  , Unique
  , Global
  , Cache
  , explInit

    -- * Systems
  , get
  , set
  , ($=)
  , destroy
  , exists
  , modify
  , ($~)
  , cmap
  , cmapIf
  , cmapM
  , cmapM_
  , cfold
  , cfoldM
  , cfoldM_
  , collect

    -- ** Performance
    -- $performance

    -- * Other
  , runSystem
  , runWith
  , runGC
  , EntityCounter
  , newEntity
  , newEntity_
  , global
  , makeWorld
  , makeWorldAndComponents

    -- * Re-exports
  , asks
  , ask
  , liftIO
  , lift
  , Proxy (..)
  ) where

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ask, asks)
import Data.Proxy

import Apecs.Components
import Apecs.Core
import Apecs.Stores
import Apecs.System
import Apecs.TH
import Apecs.Util

{- $performance

When using 'cmap' or 'cfold' over a tuple of components, keep in mind the
ordering of the tuple can have performance implications!

For tuples, the way the 'cmap' and 'cfold' work under the hood is by
iterating over the component in the first position, and then for each entity
that has that component, checking whether the entity also has the components
in the remaining positions. Therefore, the first component will typically be
the most determining factor for performance, and a good rule of thumb is to,
__when iterating over a tuple, put the rarest component in first position__.

Let's take a look at an example.
Consider a simple 2D rendering system built on top of `cmapM_`:

@
'cmapM_' '$' \\(Sprite sprite, Visible) -> do
  renderSprite sprite
@

While this rendering system works, it could be made more efficient by
leveraging knowledge of how the library handles reading of tupled components.
The usage of 'cmapM_' here (or any of the other map/fold functions) will
iterate over all entities with a @Sprite@ component and filter out any of
these entities that do not have a @Visible@ component. Depending on the game,
it is reasonable to assume that there are more sprites active in the game's
world than sprites that are visible to the game's camera.

Swapping the component ordering in the tuple is likely to be more efficient:

@
'cmapM_' '$' \\(Visible, Sprite sprite) -> do
  renderSprite sprite
@

Now the system iterates over just those entities that are visible to the
game's camera and filters out any that do not have a @Sprite@ component.

While putting the rarest component first is an excellent rule of thumb, to
get the best possible performance, always consider how maps and folds are
executed under the hood, and how you can order your components to optimize
that process.
-}