DOTS, ScriptableOject and Addressables

Hi,

I need some help figuring out how I can implement the following for my ability system.

My ability system relies on scriptable object to define each ability. I’d like to add a list of game object that can be spawned when the ability is triggered but I can’t store gameobject reference in a scriptable object.
So I figured I migth as well use Addressables to store an asset reference in my scriptable object.

Now when I convert my scriptable abilities to entities I need to retrieve the entity corresponding to the gameobject I want to spawn.

First in the declare prefab reference I get the gameobject from the assetreference using the Addressables.LoadAssetAsync method. when it complete I add the resulting gameobject in the List referencedPrefabs and also on my scriptableasset to be able to get the game object for the Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem).

    public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    {

        for (int i = 0; i < AbilityAssignements.Count; i++)
        {
            ScriptableAbility scriptableAbility = AbilityAssignements[i].Ability;
            for (int j = 0; j < scriptableAbility.Spawnables.Count; j++)
            {

                SpawnableData spawnableData = scriptableAbility.Spawnables[j];
                AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>(spawnableData.PrefabRef);
                handle.Completed += op =>
                {
                    if (handle.Status == AsyncOperationStatus.Succeeded)
                    {
                        referencedPrefabs.Add(handle.Result);
                        spawnableData.PrefabGO = handle.Result;
                        scriptableAbility.Spawnables[j] = spawnableData;

                    }
                };
                                            
            }
        }

    }
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {       
        for (int i = 0; i < AbilityAssignements.Count; i++)
        {            
            foreach (SpawnableData spawnable in scriptableAbility.Spawnables)
            {
                Debug.Log(conversionSystem.GetPrimaryEntity(spawnable.PrefabGO));
            }
        }
    }

Now the log show Entity.Null so I don’t get the reference I expect.
Also I get an exception on this line scriptableAbility.Spawnables[j] = spawnableData;

I assume that’s because the async operation of loading the game object complete after the convertion to entity is finished.

I get why the loading of the asset is asynchronus but I can’t figure out a way to wait for it.

Waiting for it is not such a big deal I think because I’d like it to work in subscene so that cost would be only in the editor/buildtime, not gametime.

Is there a way to wait for it ?
Is it a good idea ?
Do you have any other suggetion on how to store a reference to a prefab on a scriptable object ?

2 Likes

I used a similar method as the one outlined here and it works like a charm. The basic concept is to call your loading task, store the results in a global list, then OnUpdate if your list has prefabs, convert them / do whatever you need now that they are ready. Finally clear your list once they are processed.

See if this will work for you, assuming you want to store prefabs, and not gameobjects in a scene.

I made it so I could avoid having to manually recurse and convert scriptableobjects in every unrelated monobehaviour. I use it in a similar way, storing a list of prefabs.

It converts any ScriptableObjects referenced by a monobehaviour implementing IDeclareReferencedAssets in the scene(or subscene). The actual conversion is done in an IConvertObjectToEntity on the ScriptableObjects, similar to MonoBehaviours. ScriptableObjects can declare their own ScriptableObjects or prefabs also.

Hi,
Thank you both for your feedback.
After thinking about it over the weekend, I think I ended up with a conclusion similar to the one suggested by dorianvasile.
I’ll need to test a bit more the way addressables work but basically I’ll try to :
Store the asset reference in a dynamic buffer
Make a reactive system that detect such buffer and try to load the asset.
Once the asset is loaded convert the game object to an entity
Finally add that to my ability.
If that work, I’ll probably extend it to the ability itself.
That way I might be able to monitor the asset change in a remote location and reload it at runtime.

Only issue is that the asset reference is a class, I need to see if I can get away with the guid.

Like I said need to skill up on addressables.:wink:

1 Like

@WAYNGames I’m curious what you ended up doing for this?
Would you have some code to share?

All my abilities are adressable with the label Ability.
At startup, I have a system that loads all scriptable ability adressable asset with the Ability label.
once loaded, I cache the data into a map on a singleton entity (I made a blob map especially for that). The cache is then accessed by the effect systems to get the static data of each ability.
The spawning part is not yet implemented but the rest goes through addressables.
I have not implemented it yet in my ability system but I think I will do the following :

  1. During conversion I will convert all spawnable gameobject references by abilities to entities.
  2. On those entities I’ll add a component with a PrefabID which will store the prefab instance id (int).
  3. On my player entity, or rather on my ability effect in that case, I’ll store that same id.
  4. When my game will start I’ll detect all prefab with a PrefabID component and store them in a map of isntance id / entity.
  5. Finally my spawning effect system will be able to access this map of instance id / Entity to instantiate the desired prefab from the instance id.
    I’ve described this appraoch and provided a very simple implementation of it in this thread :
    Current best practice for runtime instantiation of entities from a prefab database? - Unity Engine - Unity Discussions
    This method should work but my addressable ability will only be able to refrence prefab that are already converted to entity in the build as there will be no runtime conversion and the prefab won’t be able to cahgne through adressable either…
    Once the bug I have with subscene and UITK will be solved, I’ll try to check if it can be improved with subscenes if they can go through addressables or I’ll wait for Unity to come up with a dedicated “DOTS” addressable soution.

Performance wise, On my 4 years old surface book 2, I tested with 30k entities figthing in FFA mode with instant abilities (0 sec cooldown and 0sec cast time) Every time an entity is killed is moved at a random place and get it’s health back. I log the number of direct damage effect applied per frame and I get most of the time betewet 9 to 10k effect applied with up to 12K on some frame. Using capsule for enttiies and the hybrid rendere I maitain close to 60 FPS with all safety and other stuff disabled in editor.
If I disable the presentation group and camera to simulate a headless server, I get closer to 90/100 FPS.
Of course it’s not really representative of a real game secenario as I only have one type pf effect and cost but I’ll improve on that as I go.

I planned a video to showcase the package a bit but I have not had time to record it yet :confused:

1 Like

Keep separate things separate here is my overall advice. In a real game you will be grateful you did. Because the data and assets involved get complex and grow into the hundreds and thousands quite easily.

SO’s are for where you need to load something individually. Outside of that they offer zero value over plain objects. So they are mostly useful here as containers. Ie an SO that has a list of ability classes that are not SO’s, and you just load the container.

You also have to think about what granularity do you want to be able to update at without making a new build. Another really good reason to keep separate things separate, and keep data and assets separate. Ie you want to tweak ability design change the damage amount done by something, but you you created a situation where your ability defines end up linking to dozens or hundreds of assets. So now you have to go make sure all of those assets are compatible. But you have no design to ensure that. And even if you did your update is now hundreds of MB where it could be a few KB. You are fucked.

So hard references are problematic. Enums are a good tool for linking stuff. You can have conventions for naming assets after enum names also, keeping even more separation so it’s easier to update your game at a more granular level.

The cost of being able to update things at a more granular level is more indirection and linking via data instead of hard references.

Addressables are a whole other thing and unfortunately it’s very easy to miss some key things with them and shoot yourself in the foot. Like the fact you need to version stuff yourself, and detect do you have the latest version so you know do you need to load it remotely or locally. In a nutshell you don’t want to load stuff by addressables directly. You keep say an SO of asset references that’s what your game logic goes through to load stuff. And your addressable asset flow patches those SO’s.

The most important thing here is proper separation. Unfortunately this whole area is far more complex then it would appear at a glance. But the separation issue is fairly easy to understand why you need that. And if you get that mostly right then you will be ok. You could even punt on addressables until much later and be ok, say just using Resources.Load early on.

4 Likes

Thank you both very much for your detailed comments.

I agree with you @snacktime , it is way more complicated than it first seems. I have spent weeks working on a simple loading system just to be able to switch scenes and load prefabs at runtime and I still have nothing to show for it XD

@WAYNGames thanks for the link, I’ll follow that thread too.

I’m not sure I follow all your comments.
My scriptable object are just containers for each ability’s static data. If I want to change something like damage done by one ability I just have to reload that ability with the updated data so just a few kb.
For the spawn able game object it’s the same thing I’ll just have an int to update if I want to switch from one prefab to another. If I want to change the prefab itself then I’m currently screwed because there would be no entity representation of that change in my built.
If I managed to update this entity representation through a subscene for instance then I would be able to change the prefab themselves without changing the abilities that reference it.