How to create animated damage numbers in Unity
Hi, Dan here. Today we’re gonna talk about Damage Numbers!
Damage numbers are commonly used in video games to show the damage a player is inflicting on enemies or other entities. Consider games such as:
- Slay the spire, Vampire survivors, Fortnite, etc…
Slay the Spire, Vampire Survivors and Fortnite Gameplay
In addition to serving as a feedback mechanism that enhances the feel of the attack, this feature can also provide the player with additional combat information. For instance, let's take battlefield, where if a single attack deals 90 damage, you know for sure that the next one will be fatal (since a full health is 100 points).
On top of having a floating text, we can also use the potential of Text Animator and add custom animations to it, creating even more striking and satisfying effects.
Some of the built-in effects in Text Animators
How damage numbers work
If we take a closer look at the references, we can notice that they are text elements that are somehow positioned at a specific point and then animated (if any).
Labels example
So we need a script that animates each label individually, that is, each label has its own life and manages its own animation. Then a controller is responsible for spawning these labels at the desired locations.
Building your own damage numbers
Now, let's try creating our own 2D version of these "damage numbers".
First we need to create a TMP text object (and add the TextAnimator component attached to it, if you have the asset).
Then let's create a new script that will allow us to update the label over time. We will call it DamageNumber.cs.
The purpose of this script is to animate the label via a coroutine updating the position and alpha of our text over time.
namespace Febucci.Examples
{
public class DamageNumber : MonoBehaviour
{
// if you have text animator, otherwise TMP Text
[SerializeField] TextAnimatorComponentBase textAnimator;
[SerializeField] float floatSpeed = 1.5f;
Transform cachedTransform;
TMP_Text tmpText;
private void Awake()
{
cachedTransform = transform;
tmpText = GetComponent<TMP_Text>();
// if you have text animator, otherwise TMP Text
textAnimator.SetText(string.Empty); // clears for next activation
SetAlpha(0f);
}
public IEnumerator Show(float lifetime, string text, Vector3 pos, Vector3 direction)
{
// --- Initialization ---
SetAlpha(1f);
cachedTransform.position = pos;
// if you have text animator, otherwise TMP Text
textAnimator.SetText(text);
// --- Moves up and fades ---
float elapsed = 0f;
while (elapsed <= lifetime)
{
elapsed += Time.deltaTime;
cachedTransform.position += direction * (floatSpeed * Time.deltaTime);
SetAlpha(1f - (elapsed / lifetime));
yield return null;
}
// Finishes
// if you have text animator, otherwise TMP Text
textAnimator.SetText(string.Empty); // clears for next activation
SetAlpha(0f);
gameObject.SetActive(false);
}
void SetAlpha(float alpha)
{
if (!tmpText)
return;
tmpText.alpha = alpha;
}
}
}
After that we can turn our label into a prefab so we can spawn how many copies we need!
Lastly we can now spawn in the label like so:
// ... somewhere in your code
// instantiate the label
GameObject number = Instantiate(labelPrefab, transform);
// start animation coroutine
yield return number.Show(
lifetime,
text,
spawnPosition,
new Vector3(Random.Range(-horizontalSpread, horizontalSpread), 1f, 0f).normalized);
That's basically it! After the initialization, the label will animate for a set amount of time and then disable itself.
Our example damage scene in Text Animator for Unity
AAAANd of course this is just an example! You can bring this project to the next level by implementing a pooling system to optimize object spawning, but that’s a topic for another tutorial.
Thanks so much for reading and have fun with mixing and matching different animations, text sizes and more.
See yaa 🫶
Daniel is a junior programmer at Febucci Team. Specialized in tech art with a Computer Engineering background. Also co-founded and managed the first student video game development team in Italy with Gabriele.
Comments