| src | ||
| tests | ||
| entoody.babel | ||
| readme.md | ||
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
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:
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.
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 msgImplwill be renamed todefMsg, 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