Scripter / Sound Designer / Project Manager
About the Game
We love the Colosseum, ten of thousands of people visit it every year. Maybe it’s because our imagination runs wild with epic fights that took place there. The Romans loved these games, but they might have loved chariot races even more! What if you combined those two ancient sports?
Well, you get Chariot Madness of course! So prepare yourself for a frantic, fast paced but light hearted game! Where a tight turn just might save your life, or where a well aimed throw might get you the appraisal of the Emperor himself. Face off in the Arena with deadly weapons at your disposal, because what’s more fun than to lay waste to your friends?
Engine: Unreal Engine 4
Development Time: 2 weeks
Team: 3 Designers, 5 3D Artists
This was our first project at Futuregames we had only been at school for two weeks when this project started. The first week we had meetings at about one hour each day to discuss the design of the game as a group.
I introduced my team members to Favro which is a project management software. Not unlike Trello, but it has some beneficial features such as having multiple boards in the same collection. In that way, we could have one board for the designers and one for the 3D artists. In the end, I think the use of Favro and the daily meetings had a huge impact on the final result.
I scripted a powerup system which I will go into more detail below, and some stage hazards. Apart from that, I made everything related to sound design, I searched for source files, edited them and implemented logic for them in Unreal Engine.
Before this project, I had only used Unreal Engine for about a month. I learned a lot during this project on how to use Blueprints Visual Scripting. It didn’t take me long to understand that it essentially is the same as programming but just presented in another way. I found it easy to adapt to. I have more experience using Unity and I found many similarities in the engine itself as well. With these realizations, I have no doubt that I can learn another engine fast.
The available powerup types in the game are the following: Ball, Discus, Spear, Fake Spawn Box, and Oil. These can further be divided into two main types: projectiles and non-projectiles. To make it easier to build upon I made an inheritance system for the powerups which is shown in the class diagram below. At the top I have the blueprint “PowerupBase_BP”, it has a Static Mesh Component because it’s something that is shared for all types. Below that, I distinguish if the type is a projectile or not. If it’s a projectile I have another base blueprint called “PowerupProjectileBase_BP”, it contains a Projectile Movement Component and some logic specific for projectiles. Children to this blueprint will inherit both the Static Mesh and the Projectile Movement Component.
Powerup Projectile Base
The Projectile Movement Component has an option called Should Bounce which I used, it means that every time it hits something the object will automatically bounce in the opposite direction. I also made two variables “NumberOfBounces” and “CurrentBounces” to keep track of how many bounces an instance could have before being destroyed.
The image below shows what happens if Event Hit gets fired, i.e. if it collided with something. I use a Sequence node for making it easier to distinguish different tasks. The first path from the Sequence checks if the projectile has collided with a player by doing a cast on the object hit, and if so, it calls a custom event on the player called “Take Damage”. The other path from the Sequence increments the number of bounces, and if it exceeds the maximum allowed bounces the projectile will destroy itself.
In total there are three projectile types: Ball, Discus, and Spear. Ball and Discus behave similarly, they just bounce, but the spear is a bit different. It uses another feature from the Projectile Movement Component called Homing.
The image below shows the Begin Play event of the spear. It starts by getting all the players and remove the player who owns the spear. After that, a for each loop is executed. In the loop body, it checks the distance between the spear and one of the players and stores it in a variable called “Min Distance” if it’s lower than the previous value in “Min Distance”. If so, it stores a reference to that player in another variable. When the loop has finished by going through all of the other players I set Homing Target Component, by retrieving it from the Projectile Movement Component to the player stored in the variable. That is all that is needed to be done, Unreal will handle the rest and the spear will find its way to the target. If the spear hits another object on the way it will get destroyed.
When the player rides over a powerup crate the powerup the player will get gets randomized. To know which powerup the player gets a Data Table is used. A Data Table works similar to a database or spreadsheet. To create a Data Table a Struct is needed. I created a struct which contains two fields: one for which powerup to spawn, and the other the probability for it to spawn. In the Data Table instance I added one row for each powerup type and gave it a probability as shown in the image below.
The observant reader might have noticed that the sum of the probabilities exceeds 100, this was a decision I made so I didn’t have to manually sum the probabilities each time I changed them. For this to work I had to sum up all the probabilities when the game starts, this is shown in the image below. The probability for each row is accumulated to the variable “Probability Sum”.
When the player rides over a powerup box a lot of things happen. First, a new powerup box is getting spawned, as shown in the image below, I will explain more about the “SpawnNewPowerup” function below. Secondly, this box will get destroyed.
Lastly, the part which decides which powerup the player will get. A random number is retrieved between 0 and “Probability Sum”, as shown in the first image below. After that a for each loop loops through each row of the data table. For each row, its probability is added to a variable called “Top Probability”. The random number is then checked if it’s less than the accumulated “Top Probability” value. If it is, we have found the powerup type to spawn!
This might need some clarification. Let’s say the random number is 100. First, we will check if it’s less than the first row’s probability which in this case is the Ball with a probability of 60, it clearly is not. Then we check the second row which is the Discus it has a probability of 50, but the accumulated probability will now be 110 (60 + 50), and 100 is definitely less than 110 so now we now that we will spawn the discus. This is shown in the second image. The last image shows the update of the HUD, how many instances of the powerup the player gets, and the logic for showing the corresponding mesh on the player.
This blueprint is used for handling the spawning of the powerup boxes. The boxes can only be spawned at certain locations, these locations are marked by placing instances of “SpawnPoint_BP in the world. When the spawner is initialized it retrieves all instances of “SpawnPoint_BP” and stores them in an array. After that, powerup boxes are initialized/spawned on the spawn points.
A similar function “SpawnNewPowerup”, as mentioned earlier, is called when the player has ridden over a powerup box (in hindsight I just want to point out that this function could have had a better name, for example, “SpawnNewPowerupBox”).
A random number will be generated between 0 and the total number of spawn points. For avoiding spawning a box on a spawn point that already contains a box a while loop is used. It will keep generating a new number until it finds a spawn point that is empty. This is done by having an array with the indices of the spawn points that are occupied. When a spawn point that is empty has been found its index will be added to the array of occupied spawn points indices.
A powerup box is spawned at the chosen spawn point’s location and it also gets attached to the spawn point. Lastly, this function has a parameter called “Index to Remove”, this is passed in when the player rides over a box and is the index of that box’s spawn point. The last thing that happens in the function is that this index is removed from the array. By doing it in the end, it prevents the new box to be spawned at the same spawn point as the player was riding over.