Objective: Part 08 of setting up floating combat text in Unity will look at building and utilising an Object Pool design pattern for optimisation.
We are on the final stage of this feature, which means we can tidy it up a bit to add some more optimisation. Currently we are instantiating/destroying the TMP prefabs as we require them but in the long run this won’t work as I plan to have weapons/explosive barrels that are going to deal damage very rapidly (over time) and this process is going to be very taxing on the performance of the game reducing the FPS in critical moments.
In terms of player experience this is extremely frustrating, so we want to make sure we are on top of this while the opportunity presents itself.
Step 1 — Create Object Pool Script & Manager Game Object
In the Hierarchy lets use our existing GameObject called ‘ObjectPoolManager’ which obviously as you can guess is responsible for holding the scripts that deal with Object Pooling.
Let’s now create our script ‘FloatingCombatTextObjectPooling’ and make sure to add this to the ObjectPoolManager GameObject.
Step 2— Starting to Build the Object Pool Script and Creating the Lists at Game Start
To get started let’s create some important variables:
- Maximum Normal & Critical Prefab values — Will be used when building our lists.
- A List for each of the Prefabs
- Prefabs for both Normal & Critical
- Containers to hold them both in the Hierarchy, keeping it nice and tidy/clean.
- A spawn position for the prefabs
Make sure to create the 2 empty containers and drag both of these GameObjects into the [SerializeField] in the Inspector of the ObjectPoolManager GameObject.
In void Start() let’s call both of the return methods seen here:
CreateCriticalTextPopUpPool — This return method is responsible for instantiating a List of Critical Prefab GameObjects into it’s respective container in the Hierarchy and then returning that list and storing it in the variable we created above.
CreateNormalTextPopUpPool — Same as above except for the Normal Prefabs.
The CreatePool() method is what you can see being called in the above methods and it is responsible for:
- Running a For Loop until reaching it’s maximum pool size as defined in our variables
- Instantiating a new GameObject
- Adding it to the respective List
- Assigning it’s parent to be the respective containers
- Making the GameObject inactive
This is one of the most important steps as we don’t want all of the prefabs in the List to be active while the game is running. The purpose of this design pattern is that we instantiate these prefabs at the start of the game and the activate them when required (again switching off when finished with).
This is to prevent using Instantiate/Destroy during runtime which can be heavy on performance.
Step 3 — Creating the Ability to Request a Prefab From the Lists
In the same script let’s add the functionality so that we can request a prefab from this List:
Stepping through this:
RequestCriticalPrefab() — The first responsibility of this method is to loop through it’s respective List and find the first GameObject in the Hierarchy that is set to ‘Inactive’. When it has found this, SetActive(true) and return out of the method completely as it has served it’s purpose. It will never reach the second part of the method UNLESS it loops through the entire List and ALL the GameObjects are ACTIVE, in which case, let’s discuss the next method:
CreateSinglePrefab() — Is responsible for creating a new prefab “On-the-Fly” and adding it to the List. We don’t ever want to be “caught out with our pants around our ankles” as the saying goes. We always want a contingency plan in case the worst happens (all prefabs are already active and we don’t have anymore to display on the screen).
After enough play testing of your game, you should have a basic idea as to how large your initial pool size should be so that this method is never called. But always allow for variance.
I will leave this method here also for you to take a look:
RequestNormalPrefab() — Same as above except that it is for the Normal prefabs.
Step 4 — Using the Request Feature
I have set this feature up so that when I shoot a bullet and I hit an enemy, it will use this feature on my ‘WeaponShooting’ script attached to the Player. So let’s first take a look at this:
This ActivateDamagePopUp() method will trigger the Request feature to be used on the ‘FloatingCombatTextPopUp’ script depending on the damage value:
Here we are passing in 2 parameters:
- Vector3 pos — We need the position of the enemy to be passed through from the hit so that we can set the prefabs position equal to this.
- Int damage — The random damage value that was calculated in the ‘WeaponShooting’ script. This value is used to decide whether a Critical Hit prefab should be activated OR a Normal Hit prefab.
We are also saving a reference to the GameObject prefab in newPopup which is passed along so that we can grab the <TextMeshProUGUI> component from it and pass in the damage value as a string.
Step 5 — Set the Prefab GameObject to Inactive When Finished
Currently on my prefabs I am using AnimationCurve’s to make the adjustments to the position and scale. When these AnimationCurve’s come to an end I want them to disable():
Now it has become INACTIVE in the respective List and waiting to be activated and assigned again.
Let’s take a look at the Hierarchy in action and of course the final product of my Floating Combat Text!