This challenge was a lot more complex than I was expecting. The idea is simple enough, create a powerup with negative consequences. A friend suggested have the enemies create ‘homing lasers’ for a short time and I thought that was a great idea so went with that.
After creating the powerup image, I gave it an id of -1 and prefabbed it as well as throwing it into the spawn queue.
If you’ve been following the powerup programming, you’ll be pretty familiar with implementation, but since this deals with Enemy ships and not the player, we’ll be modifying enemy code as well as the game manager since this is a timed event, we just want the enemies shooting homing missiles for only around 7 seconds.
So we pick our negative powerup up. It will run through the code and hit our modular switch statement:
What’s this? We’re calling something in the game manager? Indeed we are! and this was made possible via making the game manager a singleton. A singleton is what it says it is. Since there’s only going to be one game manager in the entire game, we make a singleton out of it and it makes it easy for other classes to access it as you can see by just using the name and “Instance”.
As a bonus in this article, here’s a basic singleton pattern using the GameManager as an example:
You first make a static variable of GameManager type and assign the name _instance to it. Static variables assure that the variable is available instantly across your program as it’s a member of the class now instead of just an object, meaning you can just access the class directly without the need of importing a gameobject into your script. You do that with the second half of this pattern in creating a public static of the GameManager type and put it into a variable called Instance with a capital I.
It gets a little convoluted, but in the Awake function, _instance becomes the entire class with _instance = this. _instance then gets returned whenever it’s accessed through the public Instance. However if for whatever reason a new GameManger gets instantiated, it will be immediately destroyed when it’s discovered another already exists.
It may be a little confusing at first, but what you need to know is this gives your class a level of protection with the public class so no outside classes can make modifications to the class. In short, instead of assigning a game object to a variable, and then using GetComponent<> to access the class, you can now just use: GameManage.Instance. to access anything inside of it! And that’s in ANY class! We also set a new variable, _negativePowerUpActivated to false. Remember since GameManager.Instance is available anywhere, it means GameManager.Instance._negativePowerUpActivated will be too. I’m sure you see where this is going. :)
So let’s move on to the function being called in the GameManager:
Ok, So the powerup calls ActivateNegativePowerup1, this then starts a corotuine called TemporaryHomingLasers. So far so good! So all that’s happening is it’s setting a boolean called _negativePowerupActivated to true, waiting 7 seconds, and then changing it back to false.
Who would need to know the negative powerup’s been activated? Since the lasers will be changing behavior, we’ll enter the laser code.
In the Start() function, we first tell the laser it will only have a lifespan of 5 seconds before self destructing with a Destroy(gameObject) inside of the DestroyLasers function.
We then do a simple if check to see if the negative powerup’s been activated using the GameManager.Instance._negativePowerupActivated boolean directly! Cool, huh?
Next, we assign the player object into a holding variable since we’ll need to know his position.
the next variable, _laserHomingDirection takes the playerposition, subtracts our position and normalizes the distance. In a nutshell, we keep the direction of the player but not its distance. We just want to know what angle to shoot, everything else is irrelevant.
Next I created a variable called _laserDirection which will be used in the MoveDown function which merely has a skeleton transform.Translate function inside. Depending on the negative powerup, we choose either a homing laser, or a laser that just moves down.
And that’s it! It seems like it’s a complex feature, and it kind of is, but if you break things down into simple English pseudocode as recommended in best practices, the code does practically write itself!
Here it is in action, looks like we need to tweak the game a bit but it works!