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. 

Monday, September 7, 2020

Starting a Dev Diary for LocalPineappleRuinsEverything

Starting a dev diary as a keepsake of how much I've done. Don't expect this to be updated on a frequent cadence until I start sharing the game more publicly. For now, this will just be a place to write down my thoughts on Project: LocalPineappleRuinsEverything. 

The project has two goals; to give me an excuse to get my feet wet in Unity DOTS and to make a compelling VR archery game for the Oculus Quest. 

I've gotten to a somewhat comfortable development cycle with ECS, thinking of my code in forms of entities, components and systems. In addition, I'm following a Functional Core/Imperative Shell philosophy on my systems, as to be able to run tests on functional pieces. The goal is to make a game that is highly performant for what I'm thinking will probably be the most consumer friendly (and popular) yet not as power intensive VR headset; the Oculus Quest. This will allow for more effects, more arrows, and just an overall more pleasant looking game in VR.

Which leads to my second goal. VR archery games exist both on SteamVR and Oculus Quest, but I find the offerings on the latter lackluster. Most seem to be bare minimum and just plain ugly at times. I find it hard to believe that the best VR archery game, both in controls and aesthetic, is Longbow, a demo game available freely on Vive. This is why I want to make this game. That and my girlfriend's mother absolutely loves Longbow but only owns a Quest.

While these are my goals, I by no expecting this to be a break out success that I'll be pouring my heart and soul into. This is first and foremost a learning experience, and as such I expect to be redoing a lot. It doesn't help either that Unity DOTS is still in preview, and as such they do not recommend it for release. Besides, I am doing this in my free time on top of a full time software developer position, so I bet there will be days, weeks even that I just want to get off the computer after work. Still I'm trudging ahead to what I expect will be a fun game. 

Anyways that seems to be enough for an intro. Guess I'll post a pic of the test level I set up for when I start doing pathfinding in ECS:

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...