MATH QUEST POST-MORTEM
Math Quest is a fantasy RPG where first, second, and third graders can explore a medieval setting and vanquish monsters through basic arithmetic battles. The main concept for the battle system is that the player would select two actions whose power levels add up to the health of the enemy. An over-calculation would result in a “miss” while an under-calculation would not finish the job. The game was developed in 2 months with a team of five people for the Fall 2015 semester “Virtual World Design” class at Arizona State University. You can check out the game more here.
WHAT WENT RIGHT
The most difficult but rewarding part about designing the game was how to effectively integrate math concepts without making it look forced. As a team, we tried to reference other similar games– basic arithmetic problems with a ticking timer; or a flurry of numbers pursuing the player; or a Child of Light-esque combat system that relied on timing and precision. In the end, though, all of those options felt forced and broke the immersion of the game. We kept going back to the question: how can we make math part of the game? That is when it hit us. Why not associate attacks with numbers and the enemy’s health as the goal? In retrospect, that looks like every basic RPG battle system. The major difference with ours, though, was the ability for the player to select two attacks and, thereby, implicitly needing to sum the two attacks to the target’s health. When we stumbled upon that major concept, the rest fell in place. The image to the right is a rough sketch of how we thought the battle system would look during design phase. I made a prototype with very basic Unity models that demonstrated the functionality of what I was thinking to the team. Allowing the team to feel and play it allowed us to move forward with our battle system idea.
Very early on, we knew we wanted the game to feel and look like Final Fantasy, typically the first game we think of when discussing the RPG genre. Consequently, we chose art that reflected that fantasy, old medieval style. Since our development team did not have any 3D modelers, we decided to look through the Unity Asset Store for relevant models. Fortunately, we found some nice models with custom animations and used them accordingly. We are extremely satisfied with how the player and enemies act– they breathe, move, and react to damage. Without these simple animations, the world would not feel as lively as it does. Additionally, the music that plays during the overworld and battle scenarios are ripped right out of Final Fantasy. Just like how our tiny minds associated turn-based strategy to these vibrant tunes, we hoped children playing our game would feel the same way.
The culmination of this final project for our “Virtual World Design” class was getting the game in front of our target audience. We gathered roughly six kids ranging from 6 to 10 years old and stuck them in a room with a laptop and mouse. Little to no direction was given while they sat and began exploring our virtual world. The insights taken from observing their play behavior both shocked and delighted us. We asked them to complete a brief survey where they would rate statements from 1 to 5. On the left is a summary of the average results from the survey. The numbers highlighted in green represent the statements that resonated the most with players while the red represented the poorest. Here is a brief summary of what we discovered that we would not have otherwise:
- Kids’ hands are small. Pressing WASD simultaneously poses a challenge for them; they also did not grasp the concept of using a mouse while playing the game. This led to a lot of running into walls and frustration.
- Lack of gender diversity. The main character was a blonde young boy and we had no way for the player to customize their character. As an educational game, having the player embody the main character is vital. It corresponds with the learning theory of constructivism, which insists that a learner must acquire knowledge by constructing the bounds of what they know versus what they don’t. These bounds cannot be constructed if the learner cannot see themselves within the virtual world and our playtesters notified us of that.
- Smoother design decision. The boss battle introduced numbers abruptly, which took some kids time to readjust how they approached these math problems. It didn’t help that this abrupt change took place during a timed battle so a slow readjustment meant punishment.
- More guidance. We noticed that younger children got impatient and did not want to explore our virtual world unless they were told where to go. We thought initially the exploratory nature of the game fostered a way for the player to construct the world around them at their own pace. Unfortunately, this was not the case for younger children with short attention spans.
While these results may seem like this is what the game did wrong, they do contribute to a successful playtesting session. We would have never known these issues had we not taken it in front of our relevant target audience. This was the first project that made me realize the significance behind getting other people to play your game. Otherwise, you develop in a vacuum of your own hubris, improving on nothing.
WHAT WENT WRONG
unclear what to do
We had a functioning mini-map that looked great and allowed the player to anticipate upcoming enemies. We had a dirt path that led the player to necessary objectives. However, none of that was relevant unless we had an explicit arrow pointing the player in the direction they needed to go. We overestimated the capability of young children to navigate a virtual world. It also did not help that our objective system on the bottom-right corner of the screen was ridden with bugs. During the design phase, we had a fleshed-out story that made sense why players were doing certain actions. By the time we got to implementation, we failed to incorporate that story component within the game. The lack of any flow led to a sense of relentless confusion and constant pleas for help from our playtesters.
ambitious design goals
We designed the game like we had three years of development time. Our design document was about forty pages– it consisted of three “zones” with over ten types of enemies including subtraction, a medal progression system, and a store where players could redeem their medals for cosmetic items. In the final product, we had only one zone with a final boss battle, no medal progression system, and no store. Since we only had two months to make the game, we prioritized elements in the game that would prove our concept of arithmetic battles. Fortunately, it worked nicely that those elements happened to reside in the tutorial town and first zone. However, the quickness to finishing the game led to many problems that bubbled up in the playtesting session.
version control fiasco
When we developed this game in Fall of 2015, there was no formal version controlling software for Unity. We first tried using GitHub, but the process to push and pull Unity objects without leading to an unmergeable conflict was too confusing for our team members and led to constant revision and reformatting of our repository. It came to the point where we resorted to a traditional locking system– one team member would spend the day implementing their component and then uploading the whole project on Google Drives for the next team member. This eventually led to an overwriting of one of our work and, in multiple instances, we had lost a significant amount of progress and had to spend more than two hours re-implementing a lot of features.
WHAT I LEARNED…
… as a programmer (the significance of object-oriented design)
To preface, I was slightly familiar with the Unity game engine before the class, but this project was my introduction to C#, and it shows through my flawed BattleSystem.cs code. Instead of making an actual system, my script is heavily specific and can only work for the starting zone. If I were to redesign the script, I would greatly incorporate the object-oriented tenet of inheritance.
A battle should consist of several items. It should instantiate the action buttons in the GUI, instantiate a general enemy model on screen, set the enemy health, process player actions and deduct enemy health accordingly, process enemy actions and deduct player health accordingly, and determine what happens when player health is less than zero. These actions can then be extended depending on what type of battle it is. Here are examples of changes that need to be implemented to accommodate inheritance:
(1) A general enemy model. Instead of importing all the different types of enemy models and then selecting which one to instantiate based on enemy type, I would pass in that specific model for a particular instance. Before I would do that though, I’d make sure all the models are uniformly identical in terms of scale and rotation to prevent misaligned models. If all goes well, that would eliminate lines 231 – 254.
(2) Remove lines that check for enemy type. In the AttackAndGotHit and AttackAndKill functions, I check for enemy types so I can play the appropriate animations for the enemy models and reinstantiate enemy health in boss battles. However, if I instead extend the extent of those functionalities in a child script and execute those “enemy type” actions in overridable functions, I can clean up how messy the code looks and modularize the code. Ideally, I want my code to be as loosely coupled and highly cohesive as possible.
For instance, lines 327 – 348 deal with the different ways the enemy attacks the player. A slime (enemy type 1) calls an animation called “Attack” while a troll (enemy type 3) instantly kills the player by setting their health to 1. Instead, I would replace the lines with a single overridable function called “AttackPlayer”. Then, if I make a child script like SlimeBattle.cs, I would extend that function and call my animation-specific code there. This can be applied in all areas that check for each enemy type– create an overridable function and extend the function in the child class.
(3) Eliminate congested attack functions. Initially, I separated the functionality of attacking and not killing the enemy and killing the enemy due to different enemy type animations and behavior. However, if we eliminate the need to check for enemy type, then we can unify both functions into one and pass in both attack values from the setButtons function. In this function, I would check whether or not the player killed or wounded the enemy and employ different functions then. However, the timing should be nearly identical. This unified attack function would not be extendable, but instead have overridable functions within like the previously explained AttackPlayer function.
(4) Eliminate the duplication of the final boss script. The advent of inheritance would then eliminate the extremely convoluted nature of the FinalBossBattleSystem.cs script. Just opening the script numbs my mind with its sloppy organization and unreadable flow. The main difference between the final boss and any other type of enemy is the way the final boss reciprocates an attack. It goes through a charge attack that tests if the player can input the two actions that sum to the charge attack power before time runs out. It’s a complicated procedure, but it did not require a whole new script with identical components to the BattleSystem.cs script. Instead, the battle system script can have an overridable function that can account for any reciprocating attacks. Then, the FinalBossBattle.cs script would simply extend that function with the contents of the WebChargeShot function minus the shared code.
In the end, my code would greatly benefit from an object-oriented design approach. The variability was minimal– modify starting models, animations, etc. But that variability led to messier code and a lack of flexibility when modifying that code. I could not have learned this better approach if I had not seen the extreme ramifications of non-object oriented code. At times, it became extremely difficult to fix bugs because they would be hard to trace. I once spent about four hours fixing a simple addition bug because I initially did not know where the problem originated. With an object-oriented design in mind, I could have saved time that could be used to implement other exciting features.
… as a developer
All my past group experiences before the “Virtual World Design” class ended poorly. I had similar expectations with this group– I would either have to do all the work myself or fix sub-par code from teammates. To my delight, my teammates actually did their work. My five-person team was the first that I felt like I could delegate tasks to members and trust that they would get them done. As a result, I learned how to delegate and dissect tasks into manageable chunks. I also learned how to code with respect to others. If another team member needed to use one of my script functions, I would design that function similar to an API call. If I needed to perform something related to one of their scripts, I’d ask if there was a simpler way of doing so rather than starting from scratch. The relationship was harmonious and it led to a more successful end product in my opinion.