Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Last active December 16, 2015 22:29
Show Gist options
  • Save fowlmouth/5506957 to your computer and use it in GitHub Desktop.
Save fowlmouth/5506957 to your computer and use it in GitHub Desktop.
import fowltek/vector_math
type
TVector2f = TVector2[float]
TPos = TVector2f
TVel = TVector2f
PSprite = ref object
TComponent = enum
CPos, CVel, CSprite
const
componentSizes: array[TComponent, int] = [sizeof(TPos), sizeof(TVel), sizeof(PSprite)]
discard """
given a new entity with position, sprite:
typeinfo = [
2 (size),
cpos, 0,
csprite, sizeof(tpos),
#nextcomponent, sizeof(tpos)+sizeof(psprite)
]
entity data = [vector2(0.0, 0.0), spriteinstance]
"""
type
PTypeInfo* = ptr TTypeInfo
TTypeInfo* = object
components: int
data: array[0 .. <1024, TTypeInfoEntry]
TTypeInfoEntry = tuple[comp: TComponent, offset: int]
TEntity = tuple[id: int, typeinfo: PTypeInfo]
TEntityManager = object
entityData*: seq[cstring]
proc newTypeInfo(components: varargs[TComponent]): PTypeInfo =
let compCount = components.len
result = cast[PTypeInfo](alloc0((compCount * sizeof(TTypeInfoEntry)) + sizeof(int)))
result.components = compCount
var acc = 0
for i in 0 .. high(components):
result.data[i] = (components[i], acc)
inc acc, componentSizes[components[i]]
proc newEntity (em: var TEntityManager; components: varargs[TComponent]): TEntity =
result.typeinfo = newTypeinfo(components)
# reserve an id and data space for entity
proc getComponent[A](entityManager: var TEntityManager; ent: TEntity, component: TComponent): ptr A =
for i in 0 .. <ent.typeInfo.components:
template thisData: expr = ent.typeInfo.data[i]
if thisData.comp == component:
return cast[ptr A](entityManager.entityData[ent.id][thisData.offset].addr)
@zah
Copy link

zah commented May 3, 2013

It's very easy to associate a numeric ID to each component type:

In C++, I used to do it like this

template <class T>
struct ComponentID { static int Value; };

template <class T>
int ComponentID<T>::Value = NextID();

In nimrod, one way to do it is like this:

proc ComponentID(T: typedesc): int =
   var id {.global.} = getNextId()
   return id

This is still a run-time way to assign the IDs. For a Nimrod
implementation, I planned to switch to compile-time methods.

With this association in place, getComponent becomes:

proc getComponent[A](entity: TEntity): ptr A =
    let offset = entity.typeInfo.componentOffsets[ComponentID(A)]
    return cast[ptr A](entity->components[offset])

Now let's get back to defining the types that can support this

type
   # run-time info for each component type
   # one instance per component type
   # in my system, it stored information such as the component type name,
   # pointers to required functions such as destructors, copy constructors, etc
   # that may need to be called in dynamic way over untyped pointers
   ComponentInfo = object
       name: string
       ...

   # all the static type information in the system is organised in one place
   # where the component type infos can be accessed by ID.
   # It also stores a cache of the created component combinations so far
   # (i.e. TypeInfos)
   Domain = object
      componentTypes = seq[ComponentInfo]
      activeTypes: set[TypeInfo]

   # This is one entity type (a particular combination of components)
   TypeInfo = object

      # stores the list of components for this entity type
      # in sorted order (by ID)
      # This is needed for efficient caching in the Domain
      components: seq[ComponentInfo]

      # the offset of each component in the instances of this type
      # (the actual game entities)
      # this has a size equal to the number of components in the system
      componentOffsets: seq[int]

   Entity = object
      typeinfo = ptr TypeInfo
      # this has the same number of elements as the number of
      # components in the current TypeInfo
      components = seq[pointer]

Finally, let's look at some procs for creating objects

proc createEntity(d: Domain, components: varargs[typedesc]): Entity =
    # pseudo-code:
    # sort the components by their ID (possibly at compile time)
    # look for this combination of components types in the domain
    # if it exists, return a new Entity with the existing TypeInfo and 
    # construct the components
    # if it doesn't exists, create the TypeInfo object first 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment