Game Design 2 - Projectiles

Implementing a variation of projectiles.

Written the by Thomas Cairns.

Introduction

This post is part four of a five part long series regarding the development of game for a school project. For more details about the project please read the previous posts.

Previous posts:

Tech Heavy

The posts for this project so far have been about technical problems, approaches and solutions. They are topics that I love, read about all the time and are of course what I want to share as well. With that said I still want all my posts to be as approachable as possible because I want to share it with as many as I can, so I try to explain concepts and give references. I have tried to do so this time as well but there is the discussion about different solution where I use terminology and concepts that requires previous knowledge and would be entire posts of their own if explained.

Task

Recently I have been working with implementing different projectile types and I would like to share what I have learned while trying to create behaviour modifiers for our scripts in Unity. Before I proceed with the details about each projectile let me first explain the core projectile behaviour.

Projectile Behaviour

For the game concept we are working on the player doesn't have the ability to fire any projectiles, rather the enemies fire all projectiles. The player instead has a shield that can deflect/bounce projectiles back towards enemies and destroy them that way. When a projectile is deflected it will change ownership, meaning that a projectile can either be owned by the enemies or by the player. Either side aren't effected by projectiles that they own.

Illustration of a projectile colliding with shield

The yellow space ship is the player avatar and the protective shield is rotating around the ship. The projectile is blue when owned by enemies and after collision with the shield it changes to yellow as an indicator that the ownership has changed to the player.

As an extension of the mechanic above, the player and enemies are locked within a box making it possible for projectiles to bounce against the boundary. The idea is to create a field where projectiles are bouncing all over the place so the player will have to dodge incoming projectiles while trying to find good angles to deflect them back towards the enemies.

Illustration of multiple projectiles flying across the playing field

Multiple projectiles flying across the playing field.

The basic projectile behaves according to the information above and they just travel in a straight path until they collide with something that makes them bounce.

Variations

During our pre-production we thought about three different projectile type we thought would change the behaviour of the player while at the same time not straying to far from the bounce mechanic.

Linked

First up is a very simple variation that links two basic projectiles together in order to create a larger projectile body. The idea is also to create a bounce that will behave a bit different as one projectile will bring the other into the wall as well and make them deflect in a different angle.

Split

Travels in a straight path just like a basic projectile. The difference is that on a impact the projectile will split into two new projectiles that bounce out at 45 degrees of each side of the original projectile while the original projectile is removed. Note that the angle and number of projectiles are not fully established yet, these are just what we are going with for now.

Homing

These projectiles will home towards a target, meaning that its trajectory will change even though it hasn't collided with anything. When a enemy fires the projectile it will home towards the player. However if the player deflects a homing projectile it will change ownership and lock onto the closest enemy within range.

Implementation Process

We already had the basic projectile behaviour established and implemented so my task become figuring out how to attach a modification while still making it easy to maintain possible changes to the core. Let me bring you through my thought process:

Really got lost in my thoughts along the way so I had to open up a text editor and start documenting each option. The end result became a table of comparison.

Inheritance Variable Component
Multiple behaviours at once No Yes Yes*
Can be changed at runtime No Yes Yes
Part of the Unity call chain Yes No Yes
Execution order is stable Yes No Yes*
Will execute at the same conditions as base Yes Yes No*

Result

The component option doesn't really have a pitch perfect solution for anything but has a work around for everything instead, and since Unity objects are based on components it seems like the natural course of action.

The Component Option

I will try to explain in more detail what each header in the table above actually mean, but I will only go through it from the view of the component option. I will try to answer the three following questions for each part: what does the title/feature actually mean, what is the reason for wanting the specific feature and what does the yes/no/asterisk mean for the component system.

Multiple behaviours at once

The question is, "Can a projectile have different behaviours at once?". The reason for me to want this features is to make it possible to reuse script in order to create new and more advance projectile without having to write more scripts but instead simply allow them to be combined.

The answer for the component system is a yes, however in order to do it an execution order of scripts have to be established, more information about this will follow below in the section.

Can be changed at runtime

The question is, "Can the behaviour of projectile be changed at runtime, after a projectile has been created?". The reason I wanted this feature was in order to allow for projectiles to have a fall back behaviour. For example the homing missile needs to be able to handle cases where its target is destroyed in advance by another projectile. Then it would be very convenient if it could change behaviour to maybe circulate until a new target appears or that it become a basic projectile and starts travelling in a straight path instead.

The answer is a resounding, yes. The component system in Unity is built to handle components being added and removed from an object at runtime. There is also the option to turn component off and on temporarily instead of adding or removing them completely.

Part of the Unity call chain

The question is, "Can the script be of the type MonoBehaviour and be part of the existing call chain through that?". The reason for this feature is purely convenience based because it can be substituted since in my case we have a base script to work from. However being able to utilize the standard library and have the basic call functions is nothing to take lightly.

The answer is also a resounding yes here, because any script that needs to be component has to inherit from the MonoBehaviour-class.

Execution order is stable

The question, "Is the execution order consistent?". This feature is real deal breaker because in this case I need to know that the base behaviour of the projectile has been executed before any other behaviour takes place.

The answer is both yes and no for the component system. Let's start with the no-part, the reason is that the Unity component system executes components in an object in an arbitrary order. For our case that would mean that sometime the specific type script could potentially be called before the base. However Unity exposes a system that allows us to specify relative execution order of certain components, which means the answer turn to a yes if we always need the exact same order for all objects.

Will execute at the same conditions as base

The question, "Will the special behaviour only execute during the same conditions as the base behaviour?". The reason for this is that the bounce of the base doesn't always execute even though a collision takes place.

The answer here is no. Because every method of the MonoBehaviour is part of the Unity call chain as we already talked about previously, so there can't be any conditional dependencies from a script to another without creating a strong coupling. Strong coupling is also how I worked around this problem for now. The projectile base behaviour looks for a component that is type specific and calls an exposed method when appropriate.

Conclusion

In order to sum it all up , something that seemed trivial became a bit more complex then expected while searching for possible solutions and then simple again after settling for a specific option. I feel that I have learned a lot about how Unity intends scripting to work and what workarounds they have created for common problems.

Random Reflection

While writing this post it really hit me that we really haven't tested any of our ideas except for the very core so far. I wouldn't say that it's a bad thing that we have focused on getting the core working however I find it to be potentially dangerous that we haven't expanded and test everything, even in their simplest forms, because discovering results of the variations might potentially change our opinions. As an arbitrary example testing the homing projectile might have made it so we wanted to focus more on the chase and timing. Another arbitrary example could be that the slit projectiles make us interested in the exponential growth of projectile to create a real projectile maze.