2DShooter Part6

Author: Eric Haines (a.k.a. Eric5h5)

Another scripting suggestion
So, now you've got a kamikaze enemy. Cool. But that's just the beginning--what else can we do with it? Anything! That's the power of scripting. At first, it can be hard to know where to start, though, so here are some suggestions for enhancing the enemy, in order of difficulty, with hints and discussion on how to accomplish them (this is the first suggestion; there are two more in the next section). Just read the hint if you want to figure it out on your own, or read the discussion if you want to see how I would do it. Which is not necessarily the best way and certainly not the only way--different people will come up with different solutions; what's most important is that it works and is bug-free.

1. Shooting (Easy)
How about instead of just heading toward the player, the enemy also shoots at him? That would make it badder, eh? Or if you wanted, you could keep the first enemy as is and make a duplicate with a somewhat changed graphic, and have this new enemy be the one that shoots, which the player could encounter as an "upgrade" over the first enemy. In either case, fortunately, most of the work is already done.

Hint: Look at the TurretTrigger script.

Discussion: Basically, you need to do little more than copy&paste the Shoot function from the TurretTrigger script into the EnemyTrigger script, along with the associated global variables ("bullet", "bulletSpeed", "shootRate", and "shooting"), and add the code from OnTriggerEnter and OnTriggerExit, adapting as necessary. (For example, the "if (shooting || other.tag != "Player") {return;}" line from TurretTrigger can replace the "if (other.tag != "Player") {return;}" line from EnemyTrigger, and if you prefer, you can add "shooting" into the "script.enabled" line instead of having it set true on its own line.) In the first line of the Shoot function, change all instances of "turret" to "enemy" (changing it to "myTransform" or "transform" would also work, since they will all have the same values). You might also want to change the "forward*4" part to a smaller number, depending on how far from the center point of the enemy you want the bullet to start; the "4" refers to the number of units in world space. You'll also need to attach an AudioSource component to the EnemyTrigger prefab; look at the TurretTrigger prefab for suggested settings, though of course they can be changed if you want the enemy to make a different shooting sound. Once you've saved the updated EnemyTrigger script, you can also change the bullet settings on the EnemyTrigger prefab (be sure to drag the bullet prefab onto the appropriate slot).

1a. A script of its own (More advanced)
Say you're planning on having a bunch of different enemies, most of which will shoot bullets. Seems like a bit of a waste to have all of them include the same Shoot function, doesn't it? Wouldn't it be better if we just made a script that did the shooting, and simply attached that to any prefab that we want to have shoot at the player? That way we could have the Shoot function simply send a message to the shooting script, and we'd be able to remove the "bullet", "bulletSpeed", "shootRate" global variables and have them just be in the shooting script instead. (We'd probably want to keep the "shooting" variable and the OnTriggerEnter/Exit code as is, though, for the sake of flexibility.) Sure, the Shoot function doesn't have a lot of code anyway, but it would save at least a little bit of time and effort, and it's also the principle of the thing...what if you add more stuff to this function later? Instead of rewriting every Shoot function in every script, you'd just rewrite the one shooting script and everything that uses it would automatically take advantage of the changes. Much faster and easier to debug. Generally, if you find yourself doing the same thing in code more than twice, it's a good idea to turn that code into a function of its own (if we're talking about the same code in a single script), or its own script (if we're talking about the same function in multiple scripts).

Aside from removing the code from the Shoot function and putting it in its own script, we'd need some way to send a message to that script. SendMessage comes to mind immediately, of course, but there's a bit of a problem with that. Namely, it can take only one parameter, and we'd want to use at least two--we need to tell the bullet its starting location and rotation. (For a perfectly round bullet, the rotation might not seem to matter since it will look the same no matter how it's rotated, but it's important because we need to add a force to the bullet's transform.forward in order for it to go where we want.) One way is to make a new class that holds two Vector3s, then make a single variable using this class that contains both position and rotation, and then pass that variable as a single parameter. That might be a bit of a pain though. It would be easier to use GetComponent in this case. The RaycastCheck script uses SendMessage because it doesn't know the name of other scripts it needs to send "OnCollisionEnter" to (plus it only needs to pass one parameter), but in this case, we'd always know the name of the shooting script. Let's call it ShootBullets. So you'd do this in the EnemyTrigger script:

Then the Shoot function in the ShootBullets script would start like this:

And so on. When done, you'd be able to attach that script to any prefab you like, and have it shoot bullets simply by calling the function, and adjusting the bullet variables on the ShootBullets script from the Inspector as you like.

2D Shooter Index : Previous Part : Next Part