This first post will focus a lot on our games second enemy type. The reason for this is simple: It was my focus of work this week.
Using an internal finite state machine, it’s able to maintain a very simple behaviour pattern: Move, Shoot, Repeat.
The Pseudo code that the enemy uses is as follow:
When spawned go into the “Move” state, choose an area on the playfield and move to it. Once you’ve reached the destination, go into the “Attack” state. When in the attack state find where the player is and fire on that location. When the attack has finished, change back into the “Move” state and repeat the process.
During the development process of this behavior pattern, I’ve found that the Fixed update that unity provides through its “FixedUpdate()” function works better than the standard update. This was mainly due to the standard update in unity (as anyone who’s used unity will know) being called every. Single. Frame.
In our specific case, this caused the enemy object to jitter, and jump around on the screen a few times before moving into position, not a huge problem, but heed these words: never forget about the fixed update option.
Now for the attack state:
The “projectile” that our second enemy use is made up of 2-pieces. The projectile that does damage. And a “warning” of where on the screen this damage will have effect.
The attack itself is a sort of “explosion”. Expanding from a small midpoint outwards until it reaches its maximum and then disappears.
I achieved this by making 2 separate objects: a “AoE Attack” -object, and a “Aoe EffectArea” -object.
Why didn’t I make it a parent object and a child object you may ask? Well, it’s simple. When a parent object is scaled, the child object is by default scaled with it. You could probably circumvent this somehow, but for simplicity sake, and due to time restraint, I made it using 2 separate objects.
Both objects are instantiated within the enemy object using the “Instantiate ()” function call. And is passed a Prefab of the object to create, a vector for its position and a rotation.
The actual attack takes 2 parameters and gives 1, while the area of effect warning only takes a single parameter.
The minimum size the attack should spawn in, and expand out from.
The maximum size the attack should expand to. And what is essentially the size of the warning area.
And a Boolean given by the attack, to the enemy object so that the object knows when the attack is done. Allowing it to destroy the objects and then change back into the “Move” state.
The process is then, as stated before, repeated until the enemy is destroyed.
A few post-programming thoughts:
The object that was created, may be one of the simplest AIs to make. But be warned, mistakes were made and time was wasted. I will briefly go through some of the problems I encountered, why they were created to begin with, and what might be worth thinking about so that you don’t end up in the same position as me.
Be warned, a lot of these are sadly obvious, but they are worth repeating to yourself from time to time:
- Communicated with your teammates.
One of the main reasons to why the development process for this object took such a long time, was due to a lacking communication in the start of the development.
This ended up costing valuable time as I made a version of the object that used a completely different attack pattern that wasn’t in the design. It was scrapped, the time: wasted. And I had to start over.
- Plan your code ahead of time!
This should be the most obvious thing to any coder, but be wary, when the pressure of presenting a product on time creeps up on you, it’s easy to make the mistakes I did.
Before you start coding anything. Sit back, and think. What should this code do? How should it look? What functions, tools or others can I use in it to make it work? And most importantly, each object rules their own logic.
The second time I made my attack logic I tried making everything in the enemy script itself. That is: the enemy would spawn the object, with all meshes, properties, etc.
Make it do everything: warn, expand, damage.
And then also remove the attack and all the components it used.
You can imagine how that went…
And that is why I had to make 3 different iterations of a single enemy object.
Which concludes our first post about the second enemy, please enjoy this simple info-graphic of the enemy object, its plan and how it ended up working.