Search code examples
c#unity-game-enginefloatingcross-fade

Floating texts in Unity


My 2D platformer game level has treasure chests placed all over the map and when a chest is collected I need to display a message. The messages are contained in a List<string> and they are displayed one by one as the treasures are collected. These messages are to be displayed in a UI>Text gameObject which is anchored to the top-center of the canvas. I want to display these texts as floating up(fading in/out) when the treasures are collected, by updating the text component of this gameObject. However, the problem arises when two or more treasures are collected before the animation for the previous one could be complete. I can easily concatenate the new messages to the existing ones, but I want the old ones to fade out and new ones to fade in. This can be done by creating multiple UI>Texts, but there are a lot of messages and I do not want to create so many redundant gameobjects. Is there any good workaround for this problem? enter image description here


Solution

  • The way I handled this in a project of mine was to create a queue of messages to display (as immediacy was not a concern, but being able to only display one at a time was). This sounds very similar to your own problem.

    // NotificationItem is just a wrapper around some text and accompanying image
    private static List<NotificationItem> notificationQueue = new List<NotificationItem>();
    
    // reference to the on-screen object
    public GameObject notification;
    
    // "Hey! I want to display a notification!"
    public static void ShowNotification(NotificationItem item) {
        notificationQueue.Add(item);
    }
    
    // I was using the DO Tween asset here, but the logic can be converted to
    // coroutines or straight update cycles
    private void Update() {
        // If there is no currently displayed notification (ie the notification object is
        // not being animated) and there is at least one item to display
        if(!DOTween.IsTweening(notification.transform) && notificationQueue.Count > 0) {
            // ...get the first one
            NotificationItem item = notificationQueue[0];
            // ...pop it from the list
            notificationQueue.RemoveAt(0);
            // ...set the notification object to the details
            notification.transform.Find("Title").GetComponent<Text>().text = item.title;
            notification.transform.Find("Text").GetComponent<Text>().text = item.text;
            notification.transform.Find("Img").GetComponent<Image>().sprite = item.image;
    
            // ...start the animation
            // (in my case, the notification animates down from the top of the screen
            // waits 2.5 seconds, then animates back up)
            notification.transform.DOMoveY(Screen.height - 85, 0.5f, false).SetEase(Ease.InOutQuad).OnComplete(PauseCallback);
        }
    }
    
    // An admittedly hacky way of getting the notification to do nothing for 2.5 seconds:
    // Animate it to where it already is.
    private void PauseCallback() {
        notification.transform.DOMoveY(Screen.height - 85, 2.5f, false).SetEase(Ease.InOutQuad).OnComplete(ReturnCallback);
    }
    
    private void ReturnCallback() {
        notification.transform.DOMoveY(Screen.height + 2, 0.5f, false).SetEase(Ease.InOutQuad);
    }
    

    The difference between my implementation and yours will be largely in the animation (as well as your Queue lists's type; e.g. you might be able to just use a List<string>). You already have your animation coded, all you need is the queue and a way to determine that your animation is complete.