[ECS-GO Bridge] Best way to interface/send/read data/events from ECS to GameObjects, and viceversa

Hi there,

I’m not sure if this makes sense or if I’m missing some basic principle, still:

Supposing that not every part of a game needs to be fully using ECS but at some point you may want to either query the World, or get some events(?) from the World (using flags, maybe?), is there a “better way” to do this?

I’m mostly thinking about having an ECS world with non-ECS “bubbles” zones centred on the point of interest, maybe using the GameObject counterpart.

Basicly, having the background running in ECS, and the actual gameplay in the classic way.

Now, I understand that converting back and forth from entities to pooled GameObjects could be troublesome and really expensive, yet:

How would you “query” the ECS World to get sync data to get some other effect outside the World.

Such as, Instantiating a complex GameObject when some important event nearby the camera happens in the ECS World?

Am I suggesting heresies, or is there something that’s already there to cover this potential corner case need? :smile:

1 Like

One way of doing it is to keep a reference of the entity that is converted from the GameObject in the ConvertToEntity workflow. You could do

[RequiresEntityConversion]
public class VantageComponent : MonoBehaviour, IConvertGameObjectToEntity//, IDeclareReferencedPrefabs
{
    [SerializeField]
    private GameObject vantage;

    private Entity entity;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new Vantage
        {
            vantage = conversionSystem.GetPrimaryEntity(vantage),
        });
        this.entity = entity;
    }
}

And voila, you now have a valid entity in MonoBehaviour world! You can do EntityManager operations on the entity. Just beware to order your MonoBehaviours properly under Edit → Project Settings → Script Execution Order so that you don’t execute main thread entity operations while a System’s job is running.

Here’s one that prints the position of the entity from a MonoBehaviour’s FixedUpdate event loop.

[RequiresEntityConversion]
public class VantageComponent : MonoBehaviour, IConvertGameObjectToEntity//, IDeclareReferencedPrefabs
{
    [SerializeField]
    private GameObject vantage;

    private Entity entity;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new Vantage
        {
            vantage = conversionSystem.GetPrimaryEntity(vantage),
        });
        this.entity = entity;
    }

    private void FixedUpdate(){
        Debug.Log(World.Active.EntityManager.GetComponent<LocalToWorld>(entity).Position);
    }
}

You can even add the entity from Convert() to a static list of entities that another MonoBehaviour can act on. Just like that you’ve effectively created a System out of Monobehaviours. This isn’t recommended for performance and style reasons but might be necessary to interface ECS with classic Unity packages or Asset Store packages. @ericbegue 's PandaBehaviour package comes to mind; I’m planning on creating some classes to bridge the gap between his behaviour tree code and Unity’s ECS

3 Likes

Thanks @Abbrew !

Sure this solves part of the problem. What about the other way around?
Should we be always handling everything by polling the ECS World from the GO Universe?

I was thinking about the signal/event scenario.
That is having a bool, or a bitmask in ECS to update accordingly. Or, using Tag Components that can only be removed when actually “read” from outside. I’m basically trying to mimic Events, and only trying to get things as less computational complex as possible.

So, currently, we can definitely handle GO → ECS events, and setting Flags (Tags) on Entities, to mimic ECS → GO events.

The problem I can see are basically 2:

  1. keeping it as lightweight as possible
  2. keeping the signal/events in Sync.

I’m thinking about the asteroid field that I’ve seen in a sample: only applying physics/rigidbodies to nearby objects, and removing/disabling them when not needed or faraway ( I can’t remember what the sample was about. Jobs, maybe?)

Anyway:

  • no problem when doing everything in ECS

…but what if for some reason we had a more complex scenario, requiring some ECS to GO transparency?

Has this already been handled or it’s something that is in the roadmap?
How can we temporarily solve this, that is, having control on the granularity of a world, by creating a moving bubble of GO detail inside an ECS world?

I’m of course thinking of non- implemented features that can’t be replicated in ECS so far, or that will never will.

1 Like

I can help with 2). If you need to tightly control when an ECS sender/receiver runs and when a GO sender/receiver runs, try this:

  1. Create an update group
[DisableAutoCreation]
public class ActorsUpdateGroup : ComponentSystemGroup
{

}
  1. Create a MonoBehaviour runner
public class ActorsRunner : MonoBehaviour
{
    private ActorsUpdateGroup actorsSystems;

    void Start()
    {
        actorsSystems = World.Active.GetOrCreateSystem<ActorsUpdateGroup>();
    }

    private void FixedUpdate()
    {
        actorsSystems.Update();
    }
}
  1. Tag Systems that you want to run in the update group
[UpdateInGroup(typeof(ActorsUpdateGroup))]
public class StressSystem : JobComponentSystem
{

}
  1. Attach the runner MonoBehaviour to a GameObject, preferably one used to hold “system” MonoBehaviours.
  2. Open Edit → Project Settings → Script Execution Order, and order the GO scripts and ECS runner scripts so that messages will be in sync
2 Likes

Thanks @Abbrew , seems like the right direction to go.
I can’t help still feeling pretty noob at this :stuck_out_tongue:

I wonder how much can the distance check toward a certain point is going to impact with a large number of entities.
I already saw some octree implementations around, but I fear I may get lost in the spaghetti multithreading among distance checks and updates :smile:

…unless I simply stack them up (octree search job, and then updating each entity, eventually).

I believe having a native LOD for systems could be useful.
That is, skipping frames or doing different checks according to the distance from the camera.

I wonder if we can leverage on the existing LOD system to add/remove a component (tag) from entities accordingly.
I believe that would greatly simplify things, and would be pretty robust!

EDIT:
Looks like I missed right the LOD example in the ECSExamples.

I’m not 100% sure, but I believe that what I’m wondering about is already there.

The answer to your question is invariably going to be very context specific. Right now a number of things can force data to live on both sides of the fence and require synchronization. And how you sync things depends a lot on the bigger picture. You have lots of tools here, it’s generally not an issue.

Generally speaking adopting a design of separating data and logic will make things easier. That’s true regardless of ECS it’s just especially true when you have hybrid systems with ECS. The most difficult systems to migrate will be the ones that abuse/overuse Monobehaviours and have basically no separation of logic and data.

For example in a very large complex Rpg game, we have I think 4 monobehaviours total where there are multiples of them. Mostly physics/animation related. Every logical entity in our system has a data representation separate from MB’s.

That paradigm makes it relatively easy to sync data between ECS and non ECS stuff. Most of our data on the non ECS side is just a class representation of the data in ECS, often with the fields being ECS IComponentData structs. So we can do things like sync all data representations inside a single job and it’s cheap because it’s just straight copying.

Often it’s not so much about syncing but integrating. For example we have a lot of particle system pools. Those don’t live in ECS. We are never moving particle systems per frame directly, we are always attaching them to some other transform. That transform might itself be in ECS or not, but the only direct actions we take on the pooled objects is enable/disable and parenting, which has to happen on the main thread anyways. So there is no gain from putting the pooled objects into ECS themselves.

2 Likes

I’m definitely not as specialised in this field as I’d love to (at least, not at the moment), but I agree on every world :slight_smile:

I believe it makes sense to have some scalable “contextual LOD” right out of the box, both for data, components and systems. I’m thinking about, let’s say, updating faraway animations every X frames when it’s not relevant. It’s definitely nothing new, games have been doing this for years, but being able to just leverage on ECS or another subsystem that handles that (LODSystem? Tags objects depending on the perceived importance, such as around PCs and NPCs, maybe using a voxelized/octree approach, implementing some sort of heatmaps that cool down over time. For instance, a fast-moving projectile would leave a 3D heathmap hot trail that fades down, also helping AI to have a faster way to do decision making and obstacle avoidance. Probably it can safely be approximated in 2D layers, or “heatmapping” the navigation mesh).

I’m just brainstorming a bit, I’m pretty sure we’re going to get similar systems, and that some games are already using similar approaches under the hood.

Back to the original topic, I believe the Observer pattern works well in this case, I can’t currently see ECS replacing everything in toto, but a consistent shift to DOP is unavoidable and more accessible, thanks to the reingeneering of the Unity implementation :slight_smile:

Now I basically have to figure out if LOD on ECS as implemented right now lets us have distance-based ECS components.

Or, maybe, since the distance has to be calculated in any case, just let the system smoothly degrade, remove/add tags to entities as needed. Probably I’m thinking about something too case-specific, but I’d love to see some samples.

Actually, I need to study more ECS samples. In no time it let me add TONS of visual detail, but I’m pretty sure I’m just scratching the surface.

(and, I guess many things are yet prone to changing, according to the comments in the ECS code, especially about subscene handling :stuck_out_tongue: )

1 Like