1

I have an ImageSequencePlayer script that has a string input to chose where to load the images from. the Sprite[] array for the images is static now, but still I load them multiple times to the same array. I have more prefabs using this tool, e.g. a character, the enemy, some traps, flames and so on. How to create a decent design using Singletons? make it so that as soon as a prefab using this script has a different location, the game Manager looks for it and loads the sprites then gives then to all that need that? If not like this then how?

using UnityEngine;
using System.Collections;

public class ImageSequencePlayer : MonoBehaviour
{
    private int currentImageIndex;
    private SpriteRenderer spriteRenderer;
    private static Sprite[] spriteSheet;
    [Tooltip("The location of your main spritesheet.")]
    public string spritesLocation;
    public float frameRate = 24.0f;
    public enum PlayMode
    {
        order, random, uponNeeded
    }
    public PlayMode playMode = PlayMode.order;
    private bool updateEnabled = true;
    [Tooltip("Decides if the random playback can iterate. If no, it doesn't play, it is frozen.")]
    public bool canIterate = true; //if the random playback can iterate. if no, it doesn't play.
    [Tooltip("Is there some non looping animation before the loop? If so, right after it's done, it starts looping.")]
    public bool warmUp = false;
    [Tooltip("If you have a warmup sheet location, put it here.")]
    public string warmUpLocation = "";
    private static Sprite[] warmUpSprites;

void Start ()
{
    try
    {
        spriteSheet = null;
        spriteSheet = Resources.LoadAll<Sprite>(spritesLocation);
    }
    catch(MissingSpriteSheetException ex)
    {
        Debug.Log(ex.Message);
    }

    try
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
    }catch
    {
        spriteRenderer = GetComponentInChildren<SpriteRenderer>();
    }

    if(warmUp)
    {
        switch (this.gameObject.tag)
        {
            case "candleLight":
                warmUpSprites = null;
                warmUpSprites = Resources.LoadAll<Sprite>("warmUpFlames");
                break;
            default:
                warmUpSprites = null;
                warmUpSprites = Resources.LoadAll<Sprite>(warmUpLocation);
                break;
        }
        //meaning we do have something to warm up
    }else
    {
        //so if we did want some warmup, we load it,
        //if we didn't, we just free the memory.
        warmUpSprites = null;
        warmUpLocation = null;
    }
    if(playMode == PlayMode.uponNeeded)
    {
        //if we need it occasionally, we just disable everything else.
        warmUp = false;
        updateEnabled = false;
        warmUpLocation = null;
        warmUpSprites = null;
    }
}
void LateUpdate ()
{
    if (warmUp)
    {
        currentImageIndex = Mathf.RoundToInt(Time.time * frameRate);
        currentImageIndex = currentImageIndex % warmUpSprites.Length;
        spriteRenderer.sprite = warmUpSprites[currentImageIndex];
        if(currentImageIndex >= warmUpSprites.Length-1)
        {
            currentImageIndex = 0;
            warmUp = false;
            //now easing on the memory, not another warmup will happen of course:
            warmUpLocation = null;
            warmUpSprites = null;
        }
    }
    if (updateEnabled && !warmUp)
    {
        switch (playMode)
        {
            case PlayMode.order:
                currentImageIndex = Mathf.RoundToInt(Time.time * frameRate);
                currentImageIndex = currentImageIndex % spriteSheet.Length;
                spriteRenderer.sprite = spriteSheet[currentImageIndex];
                break;
            case PlayMode.random:
                updateEnabled = false;
                StartCoroutine(RandomizedPlay());
                break;
        } 
    }

}
IEnumerator RandomizedPlay()
{
    int oldIndex = 0;
    int currentIndex;
    int iterNumber = 0;
    while (canIterate)
    {
        currentIndex = Random.Range(0, spriteSheet.Length - 1);
        if(currentIndex == oldIndex)
        {
            while((currentIndex == oldIndex) && (iterNumber < 8))
            {
                currentIndex = Random.Range(0, spriteSheet.Length - 1);
                iterNumber++;
            }
        }
        spriteRenderer.sprite = spriteSheet[currentIndex];

        oldIndex = currentIndex;
        iterNumber = 0;
        yield return new WaitForSeconds(1.0f / frameRate);
    }
}
public void OccasionalAnimation(bool backWardsNeeded)
{
    StartCoroutine(OccasionalAnimEnum(backWardsNeeded));
}
public IEnumerator OccasionalAnimEnum(bool backWardsNeeded)
{
    currentImageIndex = 0;
    while (currentImageIndex < spriteSheet.Length)
    {
        //meaning while we do have anything to play
        spriteRenderer.sprite = spriteSheet[currentImageIndex];
        currentImageIndex++;
        yield return new WaitForSeconds(1.0f / frameRate);
    }
    if (backWardsNeeded)
    {
        //so we need to play the shit backwards as well, like in the book
        currentImageIndex = spriteSheet.Length - 2;//so we won't repeat the last again.

        while (currentImageIndex >= 0)
        {
            //meaning while we do have anything to play
            spriteRenderer.sprite = spriteSheet[currentImageIndex];
            currentImageIndex--;
            yield return new WaitForSeconds(1.0f / frameRate);
        }
    }
    //at the end it should be at the starting sprite.
    currentImageIndex = 0;
}
}
5
  • 1
    Good to see that you are not in hurry this time and willing to change your design. Can you please explain this: make it so that as soon as a prefab using this script has a different location, the game Manager looks for it and loads the sprites then gives then to all that need that. And please add your current code as well Commented Sep 5, 2016 at 16:24
  • Yep, I saw reason and realized that continuing a wrong design will cost more time than rewriting it. I include that image sequence player in a second. Commented Sep 5, 2016 at 16:34
  • So the idea is that I have the script, using a static spriteSheet array that loads the sprites to play one after the other. How to know where to load the images from? I type in the name of the folder in the editor, I have only the necessary images in there. This way as you said earlier, those images get loaded a lot of times, but now to one array. To get around this, I would like to load these string values to a string array and let the Manager script know that it has to load from those. Commented Sep 5, 2016 at 16:45
  • Another edit: I have just started the singleton as I was rigging and rendering out another animation. Commented Sep 5, 2016 at 16:46
  • Is it possible to have a decent design with singleton since using a singleton is usually not a good design... Commented Sep 5, 2016 at 20:36

1 Answer 1

1

Add the following code at start of script to make it singleton:

private static ImageSequencePlayer _instance;
public static ImageSequencePlayer Instance
{
    get
    {
        if (_instance == null)
        {
            _instance = FindObjectOfType<ImageSequencePlayer>();
            if (_instance == null)
            {
                Debug.LogError("ImageSequencePlayer Instance is null");
            }
        }
        return _instance;
    }
}

Now you dont need to make anything static.

Access the non-static sprite array from anywhere like this:

ImageSequencePlayer.Instance.spriteSheet

and all the other properties/fields in same way.

Use Awake() method to load images from resources:

void Awake()
{
    // populate sprites array here.
}

EDIT :

The singleton script shouldn't be present in scene on more than one objects. Create an empty object and attach the singleton script. If you have any other common functionality (like spritesheet), move that code to singleton.make sure you dont have sprite loading code in your other script which you are applying on all objects.

Sign up to request clarification or add additional context in comments.

6 Comments

I thought only of the loadings themselves to be singletons, guess I was wrong. I read a wee bit about it and I thought singleton is there is only one of it. I have this script on multiple prefabs, around 4-5 types, 20 in total. so 4-5 spritesheets. If I get what you are after, we make a unique part only from the spritesheet and use everything else as used to?
Your edit cleared up a bit. So singleton = there is only one as the name shows :D I plan to make singleton arrays and load them using the singleton stuff. the sequence player that will have 20 instances will only tell the boss what to load and it loads it only once. Thank you for the help, I learned a lot :D
Well. The boss(singleton) loads the sprites in Awake method (in very start of application) without anyone telling it to do. And other script can access it whenever they need
Yes, I was unclear as I usually am :/ By telling it I mean the players are already having the locations typed in the appropriate strings. I do it via the editor. The boss just reads it and does its magic.
Yes. Read more about singleton and please upvote
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.