This post details how to build a simple customizable floating messages package in for Unity projects using C#. It was built and implemented in All Fours Mobile. For novice developers, such as myself, that are transitioning to making modular and reusable components, this post may be particularly useful. At the end, you should be able to export this project as a package that you can reuse in your other games. It will also be easy to change the look and feel of the actual floating message via the power of prefabs. The GIF below shows what the end result looks like.
The project will consist of only 3 components: FloatingText.cs, FloatingTextSingle.cs and floating_text_single.prefab. FloatingText will be the point where any other monobehaviour can call to make a floating message. floating_text_single.prefab contains the FloatingTextSingle script that is to be used for setting the message and color of the floating message.
Floating Text Prefab
FloatingText will simply instantiate a copy of this prefab and send the message to be served. Taking this approach, you can easily modify this prefab upon developing a different game to match that game’s look and feel. The image below shows what the prefab looks like in my particular game.
Note that this particular prefab is intended to be spawned inside a canvas so that appears above GameObjects in world space. It’s essentially a background image with a Text object child. Vertical Layout Groups are used to ensure that the background image can expand when the text overflows. The topmost parent has the component FloatingTextSingle. The sections below will discuss this component.
Global Variables in FloatingTextSingle.cs
public Text mainText;
public Image mainColor;
public Vector2[] positions;
public float movSpeed;
public float pauseTime;
private RectTransform _rectTransform;
private Vector2 _nextDestination;
private int _iterator;
private bool _paused = false;
The variable names are pretty much self explanatory. mainText and mainColor are what’s set to be shown to the player. The positions array stores the coordinates pattern the floating message should perform. movSpeed determines how fast the prefab moves between positions. pauseTime dictates how long to wait upon reaching a position from the array. The rest of this script is provided below.
public void Start()
{
if(positions != null)
{
_rectTransform = GetComponent<RectTransform>();
_nextDestination = positions[0];
_iterator = 0;
}
else
{
Destroy(gameObject);
}
}
// Update is called once per frame
public void FixedUpdate()
{
//_paused is toggled in coroutine PauseForTime()
if (_paused)
{
return;
}
_rectTransform.localPosition = Vector2.Lerp(_rectTransform.localPosition, _nextDestination, movSpeed);
if(Vector2.Distance(_rectTransform.localPosition, _nextDestination) <= 0.3f)
{
//Setting new destination from array, exiting and destroying if at end of array
++_iterator;
if(_iterator >= positions.Length)
{
Destroy(gameObject);
}
else
{
_nextDestination = positions[_iterator];
}
}
}
public void SetValues(string msg, Color color)
{
mainText.text = msg;
mainColor.color = color;
}
private IEnumerator PauseForTime()
{
_paused = true;
yield return new WaitForSeconds(pauseTime);
_paused = false;
}
On Start, the script simply checks if positions has been entered and then sets its first destination. On FixedUpdate, if paused, it returns. If not, it lerps the instantiated prefab to _nextDestination using the _movSpeed variable. We then check if the GameObject has reached its destination and iterate to the next position if it has. A coroutine is used to achieve the delay. Once the GameObject has passed through all of the desired coordinates, it self destroys. With this approach, the spawner does not have to worry about cleaning up child objects.
Now for the spawner class FloatingText.cs. I’m using a custom class here called MessageWithColor to store message and color in 1 object.
Global Variables in FloatingText.cs
#region Public Objects to be set in editor
public GameObject slideMsg;
#endregion
public static FloatingText Instance { get; private set; }
private bool _isDequeuing = false;
private Queue<MessageWithColor> _queue = new Queue<MessageWithColor>();
slideMsg would be the floating_text_single prefab we developed previously. We’ll be using a Queue here to store messages since we’d want First In First Out behaviour. For the final parts, we have the RunMessage and DequeueMessages functions.
public void RunMessage(string msg, Color color)
{
if((msg == string.Empty) || (msg == null))
{
return;
}
_queue.Enqueue(new MessageWithColor(msg, color));
if((_queue.Count == 1) && (!_isDequeuing))
{
StartCoroutine(DequeueMessages());
_isDequeuing = true;
}
}
public void RunMessage(MessageWithColor mwc)
{
RunMessage(mwc.message, mwc.color);
}
private IEnumerator DequeueMessages()
{
//Dequeuing Items
while (_queue.Count > 0)
{
GameObject tmp = Instantiate(slideMsg, new Vector2(-3000, 0), Quaternion.identity, transform);
//Getting FloatingTextSingle component from prefab to calculate pause duration as well as set values
FloatingTextSingle fts = tmp.GetComponent<FloatingTextSingle>();
MessageWithColor mwc = _queue.Dequeue();
fts.SetValues(mwc.message, mwc.color);
yield return new WaitForSeconds(fts.pauseTime * (fts.positions.Length - 1) * 0.3f);
}
_isDequeuing = false;
}
RunMessage is the entry point for spawning a new floating message. Calling this function enqueues a new message and then begins dequeuing only if this is the first message queued. This is ensured by the _isDequeuing flag that prevents the coroutine from being called whilst it is still emptying the queue.
You can then pop a new floating message from any other object by running
FloatingText.Instance.RunMessage(string msg, Color color);
Of course don’t forget that you’d need to have this component attached to a gameobject inside a canvas for it to instantiate itself. You can easily implement a singleton pattern using DontDestroyOnLoad for it to persist through scenes.
Once done and adjusted to your liking, you can simply Export as a package in Unity and easily imported into your other projects. I’ll attach my copy below as soon as its approved by the Unity Asset Store.