One of the most critical questions you need to ask yourself: Is it possible to add components (i.e. different kinds of data) to existing entities?
If I read the flecs FAQ right, it allows that. This is something that doesn’t work with types – a julia, C++ or java object cannot change its class at runtime.
Supposing you don’t need that, a reasonable architecture is the following:
Entity handles are 64 bit integers. Of these, you reserve e.g. 16 bit for entity kind, and 32 bit for entity index within the type.
When you have e.g. a position Tuple{UInt32, UInt32}
that is attached to some kinds of entities, then you have a positions::Vector{Vector{Tuple{UInt32, UInt32}}}
and you access it by get_position(positions, untypedHandle) = positions[kind(untypedHandle)][seq(untypedHandle)]
. (consider using Memory
instead!)
When you e.g. want to apply an update function for all entities, you would write something like
for typedEntityIterator = getTypedEntityIterators(system)
foreach(typedEntityIterator) do entity
update!(entity)
end
end
The crucial point is that in this kind of setting, the getTypedEntityIterators
will return typed iterators of handles. So the foreach
call will be type-unstable.
This is intended! It is a function barrier! And then, within the foreach
call, all the entities you handle have the same type, which makes for nice fast code.
You typically want to do the same in e.g. java. This is because its 1. nice for the branch predictor and 2. helps the JIT specialize the code.
The main difference is that julia performance will be abysmal without these techniques, while C++ / java will degrade to merely mediocre performance.
PS. Another difference is that in java/C++ you can have your cake on typed handles, and eat it: You can have different subclasses on the handle, and store the kind information twice: In the 16 bit, plus inside the RTTI / class pointer. Then, via overloading, you get very fast operations if the runtime type is statically known, and still get fast operations if it is unknown, and you never get slow megamorphic virtual function calls.
In julia this doesn’t work: Julia can never work efficiently on objects of unknown type, even if the missing info on the type would be superfluous for the operation at hand. So you need to be more disciplined about where the type is known or not.