Through feedback and play-testing of Disco is Dead, our team was able to realize that our original design methodology for levels was not going to work out as intended. The initial design was to allow enemies to spawn in at random, while restricting the enemies that could be spawned in to specific pool for each individual wave. “Scriptable waves” were created, to allow this functionality, and for a time, this seemed adequate. However, through play-testing, our team was able to see that players were not receiving it as intended. The wholly randomized nature of spawning enemies would often lead to scenarios where one player did not have enough enemies, or however, as we move more from the early mechanical design development stages into the fully realized production and polishing phase of our development, it has become apparent that we need a way to exercise greater control over our level design, while still making use of the existing systems.
Example of early scriptable wave – enemies of different kinds can be assigned for the wave, with the odds of spawning being determined by their frequency in the list. The length can also be set.
In order to resolve this issue, I set out to make a “Scriptable Sequence” scriptableobject, and managed to create a fairly simple one, along with alternate logic that allowed it to interface with the scriptable waves, so that it would “Spawn” enemies from the pre-instantiated enemy groups that we were already using in our system, which our primary coder had designed.
Example of the method of enemy storage applied within Disco is Dead – numerous disabled enemies are stored in folders,awaiting the call to be enabled.
At Left – Example of the early scriptable enemy sequence – it allowed a designer to place any object as the enemy, and the associated script would then search for it in the appropriate folder based on which player the enemy was assigned to, spawning it if it was found inside said folder. Interval represents the amount of time since the last spawn. This system is workable, but somewhat inflexible and unintuitive, with few safeguards in place. For example, a user can call for a single target zombie to attack both players will find that it does not work, and likewise with a zombie that targets both players. furthermore, there was no way to remove an enemy from the sequence at a position, etc.
This worked well enough in allowing me to test the logic and high-level design of waves, but the goal here was to produce a tool which could allow our team members, specifically our level designer, to work on assembling enemy sequences and level design without coding knowledge, and to make this system as straightforwards as possible. To this end, I started determining features the tool would need in order to be as simple to apply as possible.
Criteria for designing a more user-friendly level sequencer tool:
- A custom editor will need to be used, in order to allow for the simplification of fields, and creation of a more logical-looking interface.
- There should be adjustable fields for which player the enemy can target, either P1, P2, or both, as those are the only options we want designers to have. an extension of this is allowing a pair of enemies to attack at the same time.
- There needs to be a more clear representation of the point at which each enemy will appear – timers should be more clear than being relative to the previous object’s instantiation.
- Designers should not be able to call for the placement of any object that is not in the current wave. The placement of objects that are not enemies at all should also be prohibited – for example, designers should not be able to place a canvas prefab for the sequencer to spawn. (Although it would not act on it regardless, due to the way it is designed.)
- Designers should be able to insert/remove enemies from the list at a specific position, so that they do not need to delete half the list to get rid of one enemy in the middle of the sequence.
Before moving forwards with the full creation of the tool, I had made several classes to support it, namely Sequenced_Enemy and the base Scriptable_Enemy_Sequence class, which are shown below.
At Left: Sequenced enemy is a class for all enemies in a sequence, that holds the enemy game object to spawn, the time at which it should spawn, and an enum for the player it should attack. The enum enemyTypes/EnemyType was added later, to facilitate the ability to use an enum to choose the “enemy” GameObject without giving designers access to the full build’s worth of GameObjects.
Above: The Base Sequence class for which a custom inspector was required – has the length of time that the sequence runs for , the index of the current enemy, and the list of Sequenced_Enemies.
Custom Editor Tool: This was already under construction, and through filling it out and drawing from previous experience, creating this was not overly difficult. My main focus with this was to make it as clear and legible as possible, so as to make its application easier for members of our design team. to this end, the purposes of different fields and sections were clearly labelled, and the whole tool possessed a user-friendly interface.
Above – Example of the final interface layout for an enemy sequence.
Adjustable Fields: For the most part, these were fairly simple, using Unity’s built-in fields to allow users to add information and make changes, though there were some issues with the creation of certain types of fields – for example, in order to prevent designers from trying to spawn game-objects other than enemies, I designed enumerators that affect the enemy types, allowing us to choose which types the designer has access to, in order to make things easier for them.
Clear Representation of When the Enemy Will Appear: This is still an area that will need some refinement, as thus far, I have not been able to figure out how to draw a graph or other visual representation of the enemies relative positions/progression throughout the sequence. In the meantime, I instead opted to change the way that sequencing worked – instead of having each enemy’s “Interval” be relative to the last enemy, making it difficult to picture its placement in the overall time of the sequence, each enemy is now placed at an absolute time within the sequence – 2 seconds, 5 seconds, etc. giving the designer a more accurate view of when they will be spawning in overall. For now, this will be satisfactory, however, I intend to seek further advice on making some kind of visual indicator for the sequence’s layout.
Limiting the Types of Objects That can be Called in: This was a particularly challenging aspect of development, as I had to design code to convert an enum field to game-objects within the sequence, and vice versa. Due to the fact that the “Wave” scriptableObjects that utilize the sequences call downwards to them, the sequence cannot know which wave it will be placed into, so knowing which objects it has/does not have is implausible. due to this, I was unable to implement a means of limiting the enemy types that a sequence would use based on the wave it was in, since it cannot know the contents of the “Wave” it will be a part of while in the inspector.
As a final note, however, It would be possible to allow a user to define the wave that the sequence would be used in through the sequence’s inspector, and then limit their enemy choices based on that. however, this kind of cylical logic might be
Insertion and Removal of Game-objects at Positions in List: One of the most important features was ironically, the easiest. I simply added functions tied to buttons that would allow users to easily delete any object in the sequence, insert new ones before the position they currently had selected, or append them to the end of the list. not overly flashy, but it allows the sequence to be edited with ease, and is fairly self-explanatory.
Above: Clicking any enemy in the list will select it for editing – Enums control the type/target of the enemies, while a slider can be used to adjust when it will spawn – subsequent enemies will have their minimum time for spawning set to the time when the previous enemy in the sequence spawned.
Users can also define the traits of a ScriptableEnemy before adding it to the list, as seen above. In this way, they can quickly add multiple instances of an enemy to the array at the click of a button.
Overall, this aspect should make tweaking sequences for play-testing much easier, and provide our level designers with an effective tool for designing,laying out and testing levels and different combinations of enemies.
Review, Conclusions and Next Steps:
In closing, The construction of this tool was a great learning experience for me, and a way to re-sharpen my skills in tool development within Unity. The tool I designed should serve our team well in the design and creation of levels and game flow for Disco is Dead. The goal for this design tool was to help our team easily design and test levels within our game, as play-testing and feedback from a variety of sources showed that thought out, sequenced levels would contribute to a more interesting, designed player experience, and to this end, the design of this tool needed to support this outcome. There were some significant design challenges to be addressed, including the need for more sequenced, specifically designed levels, (versus randomly generated ones) and it is my belief, based on the criteria I was aiming for in this tools design, and the success I had in achieving them through this custom editor, that it will go a long way in streamlining our design process and facilitating faster testing and iteration of levels through giving our level designer the ability to design sequences on her own, eliminating the coders need to be involved, and freeing up more time for other aspects of our design process.
Moving forwards, I intend to give consideration to the “Scriptable_Enemy_Wave” scriptableobject, and whether a custom editor is needed there as well, as this is the interface that takes the waves and makes use of them. if it is deemed a worthwhile and necessary endeavor in our game’s design, I will be creating a follow-up development log on the subject.