This week I worked on making Placers easier to work with in the editor.
What was wrong with them? I’ll tell ya!
Placers spawn in the Enemies as needed in the Editor, but previously needed an external actor – Enemy Manager Helper – to setup the enemies. Problem this caused was that anyone working with levels and enemies needed to put in a placer and an Enemy Manager Helper in order to setup levels. That’s extra steps. As many times as we go over the steps to setup enemies in a level it’s not something that we get to do daily and when people don’t do things regularly or it’s not their focus, they forget.
The future was not having to do anything more than have placers.
Enemy Manager Helper used to have all events that able to be called in the editor to do the following: Setup Enemies, Delete Enemies, and Validate Enemies.
Setup Enemies deleted all of the enemies in the level to make sure that everything was clean and then setup all of the enemies that the placers needed in the level. But, due to silliness, Setup Enemies did not clean up soft references properly (Oops). Setup Enemies needed to be clicked on manually by a developer each time you made a change to any enemies in the level. Tedious.
Delete Enemies deleted all of the enemies and was used by other functionality – like Setup Enemies – to clean out all of the enemies in the level currently. Problem with this was that it didn’t properly clean up soft references between placers and enemies. That’s bad.
Validate Enemies validated all of the locations of the enemies and color-coded the placers based on whether they were in good places or not. A similar function – Validate Spawn Location – was used to validate a single placer.
So, that’s all boring, but that was to illustrate the previous setup and show that there was room for improvement.
I ported all of the Helper’s functionality to the Placer itself. Put all of the logic in functions instead of in events and updated everything so that everything used the new functions. While I was at it, I also cleaned up the Patrol Point functionality so that everything that someone needed to use that was previously spread out between Placers, Helpers and Patrol Points were now just on the Placers (Patrol Point functions were left on the Patrol Points too because that’s convenient). Looks like this:
I also cleaned up the names of the functions to just be better.
We now have:
- SetupEnemies() – to setup all enemies in the level
- DeleteAllEnemies() – to delete all enemies and clean up the soft references, but to leave the Placer’s configuration in place so that SetupEnemies() can be used directly after if needed.
- SetupThisEnemy() – Setups up just this placer’s enemy.
- DeleteThisEnemy() – Deletes just this placer’s enemy.
- Validate() – Validates all spawn locations for all placers.
- ValidateThis() – Validates spawn locations for just this placer.
- AddPatrolPoint() – Adds a new patrol point.
- DeletePatrolPoint() – Deletes a patrol point.
- CloseLoop() – Closes the linked patrol points into a loop so that the enemy will loop through the patrol points.
- OpenLoop() – Opens the linked patrol points so that they are no longer in a loop.
Much cleaner. Now anyone can just drag out the placer, choose the enemy they want and click SetupThisEnemy() or SetupEnemies() and be done.
But can we go further? That’s still a lot of steps to setup an enemy. What if just choosing an enemy automatically set it up? And what if anytime the user had to choose from a long list of tags they only saw the tags that were relevant?
Choosing a valid tag from the filtered enemy tag list will now automatically setup the enemy and clearing the tag will automatically clean up the enemy. Users no longer need to user SetupEnemies() or DeleteAllEnemies()!
This was done by hooking into PostEditorChangeProperty(), which is called anytime a setting is updated. Setup the logic that you need for each case, and then you’re set!
In our case here, I wanted to identify anytime the Enemy To Spawn tag changed and then call SetupThisEnemy(). SetupThisEnemy() is a Blueprint Function so it had to be called in a fancier way than just calling a native C++ function. Since this is Editor Time stuff and performance doesn’t matter, there’s no real need to move the function to C++ in the form of a BlueprintNative or BlueprintImplementable function.
At the end of the Placer changes we have a much cleaner user experience that will save time and with better validation because there are no bad soft references.