A component/entity system

component, entity, composition
nimble install entoody


this is a rewrite of my entity system (fowltek/entitty)

what is entity

Entity is an aggregate of components. Their combined data and behaviors.

what is component

Components are data and/or behavior. We use an object type (similar to a struct) to store data, and define messages to implement behavior.


Message is a function that operates on an entity. There are two kinds of messages, unicast and multicast. Unicast messages are fulfilled by at most ONE component, while multicast messages may be

Return value

Only unicast messages may return a value. For multicast messages, another mechanism would have to be added to make sense of all the return values. That sounds like YOU work to me, lul. I recommend you pass along a seq where results can be collected, then you can make sense of them later: ```nimrod proc debugInfo (results: var seq[string]) {.multicast.} ## Collect information on an entities components proc debugInfo* (entity:PEntity): string = ## Collect component information as a var res: seq[string] = @[] entity.debugInfo(res) # call the message result = res.join(", ") # collect the results

see below

defMsg(Position, debugInfo) do (res: var seq[string]): res.add "Position: "& $entity[Position].TPoint2d defMsg(Orientation, debugInfo) do (res: var seq[string]): res.add "Orientation: $# degrees" % $entity[Orientation].radians.radToDeg.formatFloat(ffDecimal, 2) ```

omg so confuse, can see sample?

Yes. Can see sample. ```nimrod import entoody, basic2d

the parameter entity: PEntity is inserted in the function

proc getPosition* : TPoint2d {.unicast.} proc setPosition* (pos: TPoint2d) {.unicast.}

type Position* = distinct TPoint2d

define getPosition and setPosition for Position

on entities without the Position component

defMsg(Position, getPosition) do -> TPoint2d: # you can use the subscript operator to access an entity's components entity[Position].TPoint2d defMsg(Position, setPosition) do (pos: TPoint2d): entity[Position] = pos.Position

type Orientation* = object radians*: float

not going to define any messages for Orientation, so do this to recognize it as a component

discard componentID(Orientation)

when isMainModule: # You should be done defining static components before you can instantiate an entity var dom = newDomain() var ent = dom.newEntity(Position, Orientation) assert ent.hasComponent(Position) ent.setPosition point2d(100,100) let pos = ent.getPosition assert pos.distance(point2d(100,100)) < 1.0 ```

new things since fowltek/entitty

  • messages are declared as a type MSG message_name, this way when you implement a message with def_msg you get an error if the arguments mismatch.
  • much better code is generated
  • PEntity is a reference type. why not, eh? I will drop the data:pointer field and make it inheritable
  • explicit entity setup functions: allocate, initialize, destroy
  • msgImpl will be renamed to defMsg, msgImpl now does not allow for unicast weight, but you can still read it to find out what the defMsg() macro does
  • new function: findMessage(ci:PComponentInfo, messageName:semistatic[string]): TMaybe[pointer] for getting the raw function for a message (if the component responds to it). The return value is really TMaybe[MSG message_name]

for the future, maybe

  • domains could include a component set and be limited to those components, instead of the component list being global