Friday, September 11, 2020

Entity Bridges and the Path Of Least Resistance Philosophy

Wanted to write up a quick blog post about a development philosophy that I've taken up during this project. Since I'm working with a preview package (not meant for release) of Unity DOTS, I have found multiple instances of features that I would have expect to work but still do not. 

For example, the physics engine currently has a way to detect TriggerEvents when two triggers intersect, but not when they stop intersecting. I need this for drawing and undrawing a bow. Since on the MonoBehaviour side we have OnTriggerEnter and OnTriggerExit, I'd expect this to eventually exist in DOTS. Now, it wouldn't be too difficult to implement my own trigger system in DOTS, but it would be more work. And since this is something that I hope is included in the release version, it would be unnecessary extra work. So what to do?

Well, you don't. Instead I've created an abstract class EntityBridge:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public abstract class EntityBridge : MonoBehaviour
{
    protected bool isInitialized = false;
    protected Entity entity;

    protected EntityManager entityManager;

    protected virtual void Awake()
    {
        entityManager =
            World.DefaultGameObjectInjectionWorld.EntityManager;
    }

    public void Initialize(Entity entity)
    {
        this.entity = entity;
        isInitialized = true;
    }
}

The idea here is to create a bridge between MonoBehaviours and Entities, allowing inheriting classes to work in the world of MonoBehaviours and communicate with Entities. For example, now we have an implementation of EntityBridge called BowEntityBridge. BowEntityBridge has trigger callbacks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        private void OnTriggerEnter(Collider other)
        {
            if (other.tag == "UndrawnArrow")
            {
                _audioSource.PlayOneShot(
                    _handleAudioClips[Random.Range(0, _handleAudioClips.Length)]);

                entityManager.SetComponentData(
                    entity,
                    new BowDrawAreaTriggerComponent
                    {
                        IsArrowInBowDrawArea = true
                    });
            }
        }

        private void OnTriggerExit(Collider other)
        {
            if (other.tag == "UndrawnArrow")
            {                
                entityManager.SetComponentData(
                    entity,
                    new BowDrawAreaTriggerComponent
                    {
                        IsArrowInBowDrawArea = false
                    });

                _isBowDrawnAudioClipPlaying = false;
            }
        }

This allows me to have the enter and exit tech without having to reinvent the wheel. Then when Unity DOTS is update to have it (if it ever does), then I'll move it all to ECS. 

Now, I understand that I lose a lot of the performance boosts by mixing legacy Unity and Unity DOTS. I would never use this in a professional setting unless absolutely necessary. However, the time saved by just doing this instead of having to make my own physics system for triggers is enough of a boon for me to decide to just have it this way for now. Besides, I soon found that I would need EntityBridge for another application. But that's a story for another time. 

Anyways wanted to post my thoughts on this. If some future reader out there wants me to go a little more technical on how this EntityBridge is used let me know and I'd be willing to talk more about it. Probably commenting below is the best way to reach me for now. 

No comments:

Post a Comment

Using TargetData in Blueprints (UnrealEngine's Gameplay Ability System)

The promised follow-up! You don't need to read up on part one , but do so if you're curious on how to make a custom TargetData. In t...