Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Best way to reference several gameObjects in DontDestroyOnLoad?

Discussion in 'Scripting' started by Oir_the_tentacular, Aug 19, 2023.

  1. Oir_the_tentacular

    Oir_the_tentacular

    Joined:
    Nov 26, 2021
    Posts:
    76
    Hello all!

    Here is what I want to happen:

    I have enemies in my game. When the enemy hits the player, I want to trigger a certain series of images to play on the screen. Each enemy has its own unique images to trigger and there are several different types of enemies. This means I will have hundreds of possible images to trigger.

    Here is what I've been thinking so far:

    Currently, I have stored all of the hundreds of possible triggerable images for each enemy as their own gameObjects in a DDOL as part of my Canvas although they're all set to inactive. I want each enemy to somehow know to activate their own specific image gameObjects once they've hit the player.

    Possible solutions that I see as problematic:
    • I could use gameObject.Find but sometimes there might be 10 or 20 enemies on screen, each of which need to reference several possible triggerable images. This makes me think using gameObject.Find will be very expensive to spam.
    • I could use gameObject.FindWithTag but since there are hundreds of possible images to pull from within the DDOL, it doesn't seem feasible to create tags for each one.
    • I could have each enemy just have its triggerable image gameObjects attached to itself as child objects but again, with 10-20 enemies on screen sometimes, that seems pretty resource-hungry and I'm not sure if I'd run into problems trying to display UI-like elements without being attached to the Canvas.
    • I could create a "ImageManager" singleton in the DDOL which manages all of the images and could be referenced whenever an enemy hits the player. I'm trying to avoid turning to singletons to solve every problem if I can help it though.
    I would greatly appreciate some guidance in the most efficient way to accomplish my goal.

    Thank you for your time!
     
    Last edited: Aug 19, 2023
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    I think you should just design these images into scriptable objects that these enemies reference.

    Then when an enemy hits the player, it just broadcasts this to perhaps a singleton instance or some static method so that the canvas can update accordingly.
     
    CodeRonnie likes this.
  3. Oir_the_tentacular

    Oir_the_tentacular

    Joined:
    Nov 26, 2021
    Posts:
    76
    Thank you for the reply! Could I ask what would be the advantage of doing it this way over, say, just attaching all of the possible images as gameObjects to an "ImageManager" singleton gameObject?
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Well having "all the possible images as game objects" is a terrible idea. You say you have hundreds... that's an absurd amount of pointless game objects.

    Better off just designing the data at the asset level and just the one image component that gets updated as required.

    Like I said here
    , these kinds of things ought to be data driven. Tell, don't ask.
     
    SisusCo likes this.
  5. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,104
    Yeah definitely try to avoid using a bunch of game objects, as this could make it really difficult to change the look of the UI used for showing the images later one.

    Also consider using addressable instead of direct references to sprites, unless you are okay with keeping all of those hundreds of images loaded in memory all the time.

    You could create one scriptable object asset registry to hold the data for all enemies. This could make it easier to manage, than if there was a separate scriptable object asset for each enemy (less dragging-and-dropping needed, easier to validate the data).

    Code (CSharp):
    1. [CreateAssetMenu]
    2. public sealed class ImageSequenceRegistry : ScriptableObject
    3. {
    4.     [SerializeField]
    5.     private Item[] items;
    6.  
    7.     public Task<Sprite[]> GetImagesForRandomSequence(EnemyId enemyId)
    8.     {
    9.         var item = items.First(item => item.enemyId == enemyId);
    10.         int index = Random.Range(0, item.Sequences.Length);
    11.         var sequence = item.sequences[index];
    12.         Sprite[] results = await Task.WhenAll(sequence.images.Select(image => image.LoadAssetAsync().Task);
    13.         return results;
    14.     }
    15.  
    16.     [Serializeable]
    17.     private sealed class Item
    18.     {
    19.         public EnemyId enemyId;
    20.         public Sequence[] sequences;
    21.     }
    22.  
    23.     [Serializable]
    24.     private sealed class Sequence
    25.     {
    26.         public AssetReferenceAtlasedSprite[] images;
    27.     }
    28. }
    Code (CSharp):
    1. public sealed class ImageSequencePlayer : MonoBehaviour
    2. {
    3.     [SerializeField]
    4.     private ImageSequenceRegistry imageSequenceRegistry;
    5.  
    6.     [SerializeField]
    7.     private Image image;
    8.  
    9.     public static async Task Play(ImageSequence sequence)
    10.     {
    11.         var instance = FindObjectOfType<ImageSequencePlayer>();
    12.         var sequenceSprites = await instance.imageSequenceRegistry.GetImagesForRandomSequence(enemyId);
    13.         ...
    14.     }
    15. }
     
    spiney199 likes this.
  6. Oir_the_tentacular

    Oir_the_tentacular

    Joined:
    Nov 26, 2021
    Posts:
    76
    Thank you very much for the reply!

    I'll give the scriptable objects method a shot! It sounds like it's exactly what I'm looking for!

    Out of curiosity, would storing the images in animations (since the trigger causes a sequence of them to be displayed anyways) and having them all be controlled by a massive animator work similarly? Or would it similarly have an unnecessary resource drain? (Just wondering if storing the images in animations would keep them at the asset level as well).
     
  7. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Not sure if making an Atlas of your sprites is feasible? but would help with rendering performance, but could possibly be a pain to code. Then each enemy that uses a particular sprite(image) would just reference where on the Atlas is their sprite, and handle logic accordingly.

    I am unfamiliar with doing it this way, but it popped up in my mind and I figured I'd mention it.
     
  8. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    280
    You should save the sprites that make up an animation, or even all of the animations for a particular character that need to stay in memory to play quickly when that character is loaded, into a sprite atlas. You can either make the sprite atlas outside of Unity, and slice it in Unity, or import individual image files and pack them into sprite atlas in Unity. The anim files will reference the sprite atlas, so any time that anim file is loaded, that sprite atlas will also be loaded. However, I imagine the potentially scriptable objects or serializable class data that contains the character animation data will already reference the animations as well as maybe the sprite atlas. So, it would all be loaded with the character. But, you might only have anim file references on the character data. If you're just displaying a single, static image when they hit the player, and that sprite is also in the atlas, then you would probably have a reference to that sprite as well, and hence the atlas would be loaded.

    You can make sprite anims with the Animation window by pressing record and changing the sprite value of the object you want to animate like a SpriteRenderer or UI Image. You can space the changes each frame contiguously in the Animation window, without skipping frames to achieve a certain frame rate, then set the Samples value to specify the framerate of the animation. You can precisely control the animation length by specifying the final value of the final frame, of it holds for multiple frames.
     
    Last edited: Aug 19, 2023