-
-
Notifications
You must be signed in to change notification settings - Fork 95
Svelto.ECS real life best (and bad) practices
We have a very specific engine that is executed on entities that needs this special behaviour. These entities are called "Resources" and these resources may or may not be "spawnable". if they are spawnable, they must spawn with a special animation.
The best way to handle this case in Svelto.ECS is to use a StaticEntityDescriptor to describe the base resource entity then use a DynamicEntityDescriptor to add the "SpawnableResourceEntityView" to the base resource entity.
Only the Resource with the extra SpawnableResourceEntityView will be handled by the SpawningResourcesEngine
I have multiple engines that can destroy entities, I want the entities to be destroyed (or pooled) by a single engine and not all over the place:
in this case the best thing is to a) move the entity between groups or b) set a component flag. The engine that is responsible of doing the real destruction, will iterate all over the entities in the entities to destroy group or all the entities with the flag on. This engine alone will decide what to do with the entity to destroy. A similar case is already happening in the Survival example.
I want to move away from EntityViewStruct and implementors, but many things are linked to Unity:
the link must be broken. The best way to face this design issue is to make the EntityViewStruct and even the implementor/monobehaviours as modular as possible. This would make the EntityViewStructs less important as they would loose their original meaning, but this is OK. the ITransformComponent shouldn't be repeated inside several EntityViewStruct, but should be used only through one TransfromableEntityView. The implementor shouldn't be one for all, but one for each component. A TransformImplementor will be created for the ITransformComponent. Several implementors can then be used on a gameobject. At this point we can introduce a TransformEntityStruct that holds the relevant data, like position and rotation for example. The whole game would always use the TransformEntityStruct, effectively moving away from any Unity dependency and then, just when necessary, a TransformingEntitiesEngine will iterate over all the TransformEntityStruct values and copy them to the TransformableEntityView transform components, which eventually would update Unity objects.
if you are sure that the entities inside a group generate the same subset of entity views, those entity views always have a 1:1 relationship, so that they can be indexed with the same index. However when entity views are generated optionally, then the number of optional entity views do not match anymore with the number of entities. This means that if you iterate a not optional entity view, you cannot use the same index used to iterate other entity views. In this case the EGIDMapper must be used to be able to fetch an entity struct by EGID instead of a simple array index. However, until I introduce the new Sparse Sets based group, this operation must be considered relatively slow. For this reason, when you need to fetch an entity view based on the egid of another entity view that has been being iterated through an array, it's important to be sure that the type of this entity view generate the smaller set. Let's clarify with an example:
void Action(GFXPrefabEntityStruct[] array, //array of GFXPrefabEntityStruct from the group exclusiveGroupStruct
ExclusiveGroup.ExclusiveGroupStruct exclusiveGroupStruct, uint count, IEntitiesDB arg4,
T[] variations)
{
var EGIDMApper = entitiesDB.QueryMappedEntities<ShaderParameterEntityStruct<T>>(exclusiveGroupStruct);
for (int i = 0; i < count; ++i)
{
{
if (EGIDMApper.TryQueryEntity(array[i].ID.entityID, out var shaderParam))
{
variations[index++] = shaderParam.value;
}
}
}
}
this example would be valid if the number of GFXPrefabEntityStruct are less than the number of ShaderParameterEntityStruct so that there are less TryQueryEntity to perform
- cache entity views locally. EntityStruct and EntityViewStruct cannot be cached anyway and EntityView as classes are considered deprecated.
- design do it all entities. This is not about design abstract and modular entity views, which is a good practice, it's about implementing entities that are NOT game design concept and are used as containers of generic, global data. One of the first mistakes that a coder coming from OOP does, is usually creating entity views that are just container of global accessible data. One of the symptom of this behavior is that usually there is just one entity if this type in game. Don't do this, ever!
- using Engines Add/Remove callback when it's not needed. You must always prefer querying the database anyway.
- abusing the entity view flexibility using custom data structures. Custom datastructure must never used, unless for very specific algorithms (like space subdivision). your entity view must follow the limit of the EntityStruct, which is returning always primitives and valuetypes of primitives. Never store entity views in your datastructures.