Initializing entities inside editor / scene
Hey, guys!
So, I've been playing with the idea of in-scene/in-editor entity configuration ever since I've discovered Entitas. And since a couple of people in the chat asked me, how I solved this problem, I've decided to create this issue to share my approach and discuss with you guys what can be improved / replaced.
Really hope for some feedback!
Firstly, I want to say that I completely agree with the notion, that in-scene configuration of entities is usually a bad practice, since it makes it really hard to keep initial state of the game in check.
That being said, sometimes it becomes necessary.
To come up with a good compromise for this problem I devised the following mental experiment.
Imagine, that we have 200 buildings in the scene and all of them must have 3 components: Destructible(bool), MaxHealth(float) and CurrentHealth(float).
Now, we could place a flagging MonoBehaviour on their prefabs and then create entites with needed components in the code.
Now imagine, that we want some of the building to be damaged to various degrees making CurrentHealth < MaxHealth.
At the very least, we would want our level designer (or ourselves) to be able to tweak the CurrentHealth value in the scene editor and then, perhaps, serialize the results into a config file.
So, how do we do it? Here is my current approach (I use Odin Inspector asset in this example) :
1). Create a EntityConfig ScriptableObject
using Entitas;
using Sirenix.OdinInspector;
using UnityEngine;
[CreateAssetMenu(menuName="Configs/Entity")]
public class EntityConfig : SerializedScriptableObject
{
public IComponent[] components;
}
2). Make asset-configs from this SO.

3). Create a ViewableEntityInitializer MonoBehaviour to a prefab / in-scene GO and add EntityConfig asset
using Entitas;
using Sirenix.OdinInspector;
public class ViewableEntityInitializer : SerializedMonoBehaviour
{
public EntityConfig config;
public IComponent[] overrides;
}

4). Specify component overrides for in-scene instance if needed.

5). Create a InitSceneEntitiesSystem initialize system
using System;
using System.Collections.Generic;
using System.Linq;
using Entitas;
using Entitas.Unity;
using Object = UnityEngine.Object;
public class InitSceneEntitiesSystem : IInitializeSystem
{
private readonly Contexts _contexts;
private readonly GameContext _context;
public InitSceneEntitiesSystem(Contexts contexts)
{
_contexts = contexts;
_context = contexts.game;
}
public void Initialize()
{
List<ViewableEntityInitializer> initializableEntitiesBehaviours = Object.FindObjectsOfType<ViewableEntityInitializer>().ToList();
foreach (ViewableEntityInitializer initializableEntity in initializableEntitiesBehaviours)
{
GameEntity entity = _context.CreateEntity();
foreach (IComponent component in initializableEntity.config.components)
{
IComponent overrideComponent = initializableEntity.overrides.SingleOrDefault(c => c.GetType() == component.GetType());
IComponent finalComponent = overrideComponent ?? component;
int componentIndex = Array.IndexOf(GameComponentsLookup.componentTypes, finalComponent.GetType());
IComponent finalComponentCopy = (IComponent)Activator.CreateInstance(finalComponent.GetType());
finalComponent.CopyPublicMemberValues(finalComponentCopy);
entity.AddComponent(componentIndex, finalComponentCopy);
}
entity.AddView(initializableEntity.gameObject);
initializableEntity.gameObject.Link(entity, _context);
Object.Destroy(initializableEntity);
}
}
}
This way, we have a set of configs that is somewhat easy to maintain AND the ability to tweak initial config values when needed.

Two things to note here: 1). I use Odin Inspector because I couldn't find a blazing fast way to use the awesome Entitas type drawers, so I've decided to test the approach with Odin for now. 2). This example is hard-coded to use the GameContext, since I could't find a way to use multiple contexts without reflections or code generation.
Let's discuss :)
since I could't find a way to use multiple contexts without reflections or code generation.
Why not use code generation then? It's the simplest possible way
@KumoKairo I most definately will. I'm already using some, as you know. Just wanted this example to be as simple, as possible, so that we could focus on the approach itself and not technical implementation.
I see. Overall I think it would be a great addition to Entitas-Unity plugins. This more of a designer-based approach is what we lack right now.
Nice! This is something really similar to what we did (having an EntityConfiguration in SerializedObject), minus the awesomeness of odin -> need to get it ;-) Thanks for sharing.
This is awesome, thankyou. Would you mind throwing it up as a repo on gh? [Edit: I somehow didn't notice the code snippet - that answers my question]
@ShadeSlider Have you had a look at Entitas Blueprints? Seems like the perfect starting point for this kind of experiment and would also totally fix the multiple contexts issues.
@IsaiahKelly Yeah, that was one of the first things I looked into. Blueprints definitely have a lot in common with my approach, but they felt somewhat too heavy and too integrated into the core of Entitas. They also lacked the abilility to override components on the in-the-scene instances, which was the main goal of mine.
I think, ideally, this should be a decoupled addon that uses type drawers from Entitas, but otherwise is a separate thing.
This solution sounds nice, but what about putting the "InitSceneEntitiesSystem" logic within the "ViewableEntityInitializer" MonoBehaviour itself? I think that this would allow the logic to work when you instantiate objects after the scene is loaded as well, rather than only on scene load.
@11clock if you mean initializing entities this way while the game is running - I don't think it's a good idea. The only real reason for this approach is the ability to override and tweak component values in the editor. And I actually try to keep the amount of MonoBehaviours as low as possible, to be honest.
That being said, you can make this system into execute of reactive system to achieve what you want, no reason to make it a MonoBehavior.
@ShadeSlider Ah, I was wanting to use this as a replacement for the deprecated blueprints, so that I can use Unity's new tilemapping feature for building levels within the scene editor. At the same time, I also wanted to use this for practically every common entity, like projectiles and such, rather than building factories. For this to work, I would need to be able to parse the scriptable objects during runtime, and not just on scene load.
@11clock I see, yeah, I'm planning to extract entity creating code into a separate service or Context extension. Something like context.CreateEntityFromConfig(entityConfig);
I'll update the issue when I get to that.
@ShadeSlider Discovered an issue with this setup. The components generated on the entity become directly attached to the ones in the config file, meaning the config file gets altered by new values assigned ingame. I was wondering if you know of a way to fix this.
Edit: I did some research on the matter and discovered that this isn't really a bug but a misunderstanding on how scriptable objects work as a shared resource.
Sorry for late response. It's not a bug indeed, it's just the fact that I was copying references to the components instead of copying the component itself. In order to fix it, you need to add these lines to the code (I'll update the snippet above):
IComponent finalComponentCopy = (IComponent)Activator.CreateInstance(finalComponent.GetType());
finalComponent.CopyPublicMemberValues(finalComponentCopy);
and change next one to this:
entity.AddComponent(componentIndex, finalComponentCopy);
Why you just don't use mono behaviours instead of config SO + overrides? Isn't it enough simply to save mono behaviour into prefab and use as "config" with all "override" functionality support?