-=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- (c) WidthPadding Industries 1987 0|245|0 -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=-
SoCoder -> Blogs Home -> Blogs


 
JL235
Created : 11 April 2008
Edited : 11 April 2008

Generic Enemies



Sometimes I write code just to get it done. I know it's ugly and might brake. So I package it off behind an interface so I can get rid of it later, or at least so I don't have to look at it anymore. However sometimes I write something really neat and am amazed at how simple yet powerful the solution is. Today I wrote a new EnemyFactory for spawning enemies in the shoot-em-up I'm building.

The basic specification for this EnemyFactory was:
  • to be a collection for storing multiple pieces of spawn information
  • Needs to store the Enemy I am spawning
  • Needs to store it's location of where it is spawned
  • stores when (as in the real time) to spawn the enemy

First Option: Pre-Make them all

Essentially make and store all Enemy instances when the game starts up and add them all to the Factory. When the spawn time is released I just take them out and set them free into the World. It's usage would be something like:

Adding these enemies to the World inside the Factory is then very trivial. Something along the lines of:

However this has lots of redundancy involved. If I have 1,000 Enemies over a level then at the beginning all 1,000 enemies exist. They will also all be in the exact same starting state, so I this felt really sloppy to make them all at the beginning. A better solution is needed.

So instead of making them why not simply say what enemy I want to be spawned, and have it created at runtime, just as you would make an enemy mid-game with any other game.

Second Option: Constants

Using constants to represent the enemies can then allow me to just check and then make them based on what I was given. It's usage would be similar to above:

When it reaches the spawn time and I need to add it to the World, inside the factory I can then just check what constant I was given and then make it. Something like would be like:

I don't like this at all. More efficient then the first but it ties what types of Enemies I am using. Adding a new Enemy type requires updating my code in three places. The type itself needs to be made, then I need to add the constant for the type and finally update the EnemyFactory to support it. Not much work, but it'd be nice if I didn't have to. So...

Third Option: Generics and Reflection

Through some Generics juggling and a little simple reflection I can only describe what I am storing (like with the constant option) but still not tie myself into any class, except for a subclass of Enemy (like in the pre-making option).

It's similar to the constant option in that I pass in the class of the Enemy I want to spawn. This is the ONLY time I make any specific reference to what Enemy I want to spawn. Usage is:

This is thanks to classes using Generics to help say what class they are. The add method signature inside of the EnemyFactory can then be defined as:

The Class<? extends Enemy> means it will accept any Class object as long as it's Generic type is either Enemy or a subclass of Enemy. So EnemyFlyer.class is accepted, but String.class is not because it does not subclass Enemy. Inheritance in Generics is something I've known about for a while, but I've never found it that useful until recently.

Making and adding a new Enemy to the World is then just a simple case of a little reflection.

enemyKlass is of type Class<? extends Enemy so no cast is required when calling newInstance() because I can already guarantee with Genrics that the result will be of type Enemy. Then I just add it to the World. I found this really neat!

Generics can be a really powerful tool, especially when you start using inheritance with it too. Reflection can come in useful, but like most things in Java, it is over-engineered.

Next up I am planning to add lots of methods to the Enemy like startShooting, stopShooting, move, repeatMove, stop, faceLeft, faceRight, turn, setSpeed, setTurnSpeed, repeatTurn, and so on. This is so that I can do something similar and have a time based list of behavior objects that just call these methods at relevant points in the Enemies lifetime. This allows me to have an Enemy start moving continuously, then start shooting after 5 seconds, then stop, then turn, then start shooting, then head off until it's outside the world (in which case it is removed).

This will allow me to easily produce new Enemy behavior very quickly since it's just 'do this at that time, then do that, then this' and the specifics are handled by the Enemy class. Then, an early release should be finished!

Joe.

If your interested in the final code of the EnemyFactory then it is here in full:

 

Comments


Friday, 11 April 2008, 06:50
Jayenkai
A little background work goes a long way.
That's planning ahead for you!

My stuff never gets that far