My task was to make Sonity able to be used in Uniy's ECS framework
Keywords: Unity, C#, ECS, Burst Compiling, Multi-threading, Tools
Sonity: Sonigon/Sonity
My task was to make Sonity able to be used in Uniy's ECS framework
Keywords: Unity, C#, ECS, Burst Compiling, Multi-threading, Tools
Sonity: Sonigon/Sonity
As a Programming Consultant for Sonity, my role was to design and implement an ECS component responsible for managing sounds for that entity. This component needed to integrate seamlessly with Unity's Entity Component System (ECS), enabling efficient audio management within a data-oriented architecture.
Key Component Objectives:
Trigger-based Sound Playback: The component had to play sounds when specific triggers were activated, allowing for dynamic audio playback within the game.
Trigger Functions: The component also needed to expose functions like play, pause, and stop, which could be called from other ECS systems giving more control over the audio during runtime.
Trigger sound from code: Be able to play sound directly from code in the ECS framework without having a component.
Because Sonity stores all information about a sound trigger within a class called SoundTriggerInternals, I needed a way for the component to reference data efficiently in an ECS environment.
Initially, I experimented with serializing the SoundTriggerInternals data to a JSON file, converting it into a byte array, and storing it in a blob asset within the component. The data would then be deserialized when the sound was played in the object-oriented context. However, this approach proved to be both inefficient and unnecessarily complex.
To simplify the system, I implemented an external class containing a dictionary that maps a GUID (Globally Unique Identifier) to each corresponding SoundTriggerInternals instance during the baking of the components. The component now stores only the GUID, allowing it to retrieve the necessary data from the dictionary when needed.
One of the great features of Sonity is that every sound event includes convenient methods such as Play(Transform owner) and Play(Transform owner, Transform position), allowing you to trigger sounds directly from code whenever you have a reference to the corresponding sound event.
We wanted to replicate this behavior within our Burst-compiled ECS workflow. However, Burst does not support direct references to managed objects like ScriptableObjects, which Sonity’s sound events rely on.
To overcome this, we implemented a lookup map that stores references to all sound events, using their asset GUIDs as keys. This enables us to retrieve any sound event dynamically at runtime without holding managed references in the ECS components.
To make this system accessible, we introduced a SoundEventLibrary struct that automatically generated a public static GUID for each sound event in the project. The library could then be updated either manually or automatically through an AssetPostprocessor, which detected changes to sound event assets and regenerated the code as needed. Sonity could then use this GUID to access the correct sound event through a lookupmap.
With this setup, any ECS behavior can easily access the asset GUID of a sound event and use it to play sounds directly from code maintaining the simplicity and flexibility of Sonity’s original design, while remaining fully compatible with Burst and ECS.
The idea of a sound event library worked so great that I was asked to help to implement it into regular Sonity as well, not just my ECS plugin.