Approach to preloading assets

I’m wondering how the rest of you address the need to preload dynamic objects to avoid hiccups during gameplay.

As a simple example, I have explosive objects in my game. When they explode, I spawn an “explosion” prefab which contains some particle effects and lights. There’s a hiccup the first time I do this, as the Instantiate call isn’t cheap. So, I want to try to preload things as much as possible for dynamic objects.

Another example is a UI object I have, just an object with a canvas and some text, which shows up any time the player looks at an object they can interact with. The first time I look at an interactable object, there’s a hiccup as this resource is Instantiated.

I have a “Loading” scene which I display while loading the next scene, and my naive plan is that while this scene is up, I’ll pre-instantiate a bunch of stuff. Hopefully it doesn’t cause any weird popups on the screen, or noise, when preloading these things. Some things I’ll preload for all scenes, while other things I’ll likely leave it up to the individual scenes to decide what they want to preload. I pool some objects that get reused a lot, like decals, so I’ll probably move even more stuff into my pooled object manager.

I’m just curious if anyone else has a different approach to this.

There’s a setting under Player Settings named “Preloaded Assets”, which loads the asset right away and keeps them in memory until the game shuts down.

The obvious downside to this is that they can never be unloaded even when not in use, so use with caution, but it sounds like it’s what you’re looking for here.

Thanks for the heads up on this. I’d been using this for a couple of assets, but never felt like it worked as expected. Now I see that it only works in a built player, not in the editor. But that’s fine. As for needing to keep these objects alive at all times, that’s pretty much what I want for most of the things I wanted to preload.

I’ve just tested this out with in my build, and it appears to remove the hiccups I was experiencing. I’ll just have to cope with the fact that I’ll still have some hiccups when running in Play Mode in the editor, knowing that things will run smoothly in a build.

Some of these things can be helped with object pooling as well, such as the explosion prefab.

2 Likes

If you can pre-instantiate the resource then disable it until it’s triggered to be active again, then do it.

With the UI for example, -if possible- include it in your scene and at the scene initialization you just disable it, or its renderers, then whenever the player looks at it just enable the renderers again (CanvasGroup alpha for example!)

Object pooling would be extremely helpful in your case with explosions as Joe mentioned as well, you don’t wanna end up with your memory being eaten up by your unused objects that are alive the whole game life cycle.

Thanks for the suggestions. It sounds like I’m on the right track, and I’m now ultimately combining three techniques depending on what makes the most sense on a per-asset basis.

  • For Singletons, assuming they’re used for pretty much the entire duration of the game, I’m preloading those using the “Preloaded Assets” approach that Baste recommended. That works well for these kinds of things.
  • I’m using object pooling for things like Decals and Explosions, things which can easily be “reset” to their original state and reused. I’m preinstantiating some number of pooled objects at the start of the level. These pooled objects can generally be encountered in any scene, so it makes sense for them to be preloaded at the start of all scenes.
  • For less common dynamic objects, such as AI Enemies that are only encountered in a small number of scenes, I’ve configured my scenes to declare a list of objects to preload when the scene starts. This occurs while my Loading scene is still displayed, so hiccups aren’t an issue.

Thanks again for the help. This feels pretty simple and manageable.

For singleton ScriptableObjects, we’ve got a pattern to ensure they’re preloaded. In a case where you have prefabs that needs to be always available, the singleton ScriptableObject singleton containing those would look like:

public class PrefabRegistry : ScriptableObject {
    private static PrefabRegistry instance;

#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]
    public static void LoadInstance() {
        string guid = "ae9a323488255d04cafed1dd8e910ec1"; // Found in .meta file of asset.
        string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
        instance =  UnityEditor.AssetDatabase.LoadAssetAtPath<PrefabRegistry>(assetPath);
    }
#else
    private void OnEnable() {
        instance = this;
    }
#endif


    [SerializeField]
    private GameObject somePrefab;
    public GameObject SomePrefab => instance.somePrefab;
}

The reason we do it like this is that in builds only, OnEnable is called on all of the preloaded assets, so we can use that to register the singleton. In the editor, we have to fetch it through the assetdatabase as OnEnable is not called.

6 Likes

Hi~
What I’ve noticed is that OnEnable is called when Editor reloads (recompile). I’m using version 2018.4.0f1. Has this behavior changed in 2019?

My post is from way before 2019.1 was out of beta, so nope!

Once the SO is loaded, it gets OnEnable called on it on assembly reloads. When I wrote “In the editor, we have to fetch it through the assetdatabase as OnEnable is not called”, what I meant is that we’re not guaranteed that OnEnable is called at startup for preloaded assets (as we are in builds).

The solution will cause the instance to be set when it’s already set; that’s not a very expensive operation.