Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

So lets pretend I have an enemy class that has some generic implementation and inheriting from it I have all the specific enemies of my game. There are points in my code that I need to check whether an enemy is a specific type, but in Java I have found no easier way than this monstrosity...

//Must be a better way to do this
if ( enemy.class.isAssignableFrom(Ninja.class) ) { ... }

My partner on the project saw these and changed them to use an enum system instead

public class Ninja extends Enemy {
    //EnemyType is an enum containing all our enemy types
    public EnemyType = EnemyTypes.NINJA;
}

if (enemy.EnemyType = EnemyTypes.NINJA) { ... }

I also have found no way to generate enemies on varying probabilities besides this

for (EnemyTypes types : enemyTypes) {
    if ( (randomNext = (randomNext - types.getFrequency())) < 0 ) {
        enemy = createEnemy(types.getEnemyType());
        break;
    }
}

private static Enemy createEnemy(EnemyType type) {
    switch (type) {
        case NINJA:
            return new Ninja(new Vector2D(rand.nextInt(getScreenWidth()), 0), determineSpeed());
        case GORILLA:
            return new Gorilla(new Vector2D(rand.nextInt(getScreenWidth()), 0), determineSpeed());
        case TREX:
            return new TRex(new Vector2D(rand.nextInt(getScreenWidth()), 0), determineSpeed());
     //etc
    }
return null
}

I know java is a little weak at dynamic object creation, but is there a better way to implement this in a way such like this

for (EnemyTypes types : enemyTypes) {
    if ( (randomNext = (randomNext - types.getFrequency())) < 0 ) {
        //Change enemyTypes to hold the classes of the enemies I can spawn
        enemy = types.getEnemyType().class.newInstance()
        break;
    }
}

Is the above possible? How would I declare enemyTypes to hold the classes if so? Everything I have tried so far as generated compile errors and general frustration, but I figured I might ask here before I completely give up to the huge mass that is the createEveryEnemy() method. All the enemies do inherit from the Enemy class (which is what the enemy variable is declared as). Also is there a better way to check which type a particular enemy that is shorter than enemy.class.isAssignableFrom(Ninja.class)?

I'd like to ditch the enums entirely if possible, since they seem repetitive when the class name itself holds that information.

share|improve this question
1  
You could try a polymorphic approach using interfaces. Like an IUpdate, IAttack etc etc. But I have no idea if this will bite you in the ass later on or not. Nor do I know how it will have impact on performance in your structure. –  Sidar Sep 19 '12 at 22:23
    
+1 for case GORILLA: –  bobobobo Sep 20 '12 at 2:54
    
you can generate enums with probabilities like this: stackoverflow.com/questions/5269250/…. also you can use instanceof instead of isAssignableFrom –  Ray Tayek Sep 21 '12 at 0:54
    
also see my answer to stackoverflow.com/questions/6542259/… –  Ray Tayek Sep 21 '12 at 1:01
add comment

4 Answers

up vote 5 down vote accepted

Actually, everything you mentioned is possible:

Class<?> cl = Class.forName(className);
Constructor<?> ctor = cl.getConstructor();
DesiredType object = (DesiredType) ctor.newInstance();

or with 1 line:

return (DesiredType) Class.forName(className).getConstructor().newInstance();

You can even have arguments passed to the constructor. See this: http://stackoverflow.com/questions/6094575/creating-an-instance-using-the-class-name-and-calling-constructor

Getting the class name of a created object instance is also possible with: obj.getClass.getClassName()

However I think you should avoid using this kind of code too much, because it is prone to errors, e.g, if you refactor the name of one of your classes, the IDE won't change it in that code.

share|improve this answer
    
Interesting, I was trying stuff like this and it wasn't working for me, in your example is className a string? Is there anyway to save the actual class itself? ie instead of an array of enums, I have an array of classes, and then instantiate the one I randomly fall into picking? –  Kevin DiTraglia Sep 20 '12 at 2:39
    
First question: Yes, className is the complete name of the class: "namePackage.NameClass". Second question: Yes, there is, you can have this: Class[] array = new Class[]{Class.forName(className1),Class.forName(classNam2)}; or this: List<Class<?>> arrayOfClasses = new ArrayList<Class<?>>(); arrayOfClass.add(Class.forName(className); ); –  joxnas Sep 20 '12 at 2:47
    
@KDiTraglia If you want to know more about java reflection stuff, check this out: tutorials.jenkov.com/java-reflection/index.html –  joxnas Sep 20 '12 at 14:23
add comment

You could add some type of flag(maybe an int or short) to each enemy type. Then check the flag for which type it is.

share|improve this answer
    
That is what is currently doing and I am trying to avoid (using an enum) –  Kevin DiTraglia Sep 20 '12 at 0:06
add comment

Like joxnas says in his comment, you can create an array or collection of Class<?> objects (or Class<Enemy> may be more useful) as your enemyTypes. Creating an Enum to describe the type seems to me to be a duplication of information that is already there.

There is a subtle difference between enemy.class.isAssignableFrom(Ninja.class) and enemy.EnemyType == EnemyTypes.NINJA. The former will match subclasses of Ninja (or more generally anything assignable from a Ninja as the name suggests) whereas the latter will not. Another alternative that is equivalent to the latter is enemy.getClass() == Ninja.class. Choosing what operation to perform based on the type of the object is what polymorphism does best and it is usually preferrable to 'switching on type' as you are doing. I don't know your particular use cases and nothing is a silver bullet but it would be worth investigating. Instead of this:

if (enemy.getClass() == Ninja.class) {

    // Throw shuriken
    // Dodge

} else if (enemy.getClass() == Gorilla.class) {

    // Throw banana
}

which will need to be updated every time you add a new enemy class, consider adding abstract throw() and dodge() methods to the Enemy class/interface and just do this:

enemy.throw();
enemy.dodge();

I'm not going to go into how to dynamically instantiate objects because I think joxnas has covered that but I'll note that by using a collection of Class<Enemy>s instead of the Enum you can avoid the need to switch on type during instantiation which could be reduced to this:

for (Class<Enemy> type : enemyTypes) {
    if ((randomNext = (randomNext - getFrequency(type))) < 0) {
        enemy = type.newInstance();
        break;
    }
}

Granted it is a little more complex when you add exception handling and the ability to pass arguments to the constructor.

share|improve this answer
    
Thanks for the information, the enemy.class == Ninja.class syntax is much better than what I was using, I am used to the .NET world so I was trying things like if (enemy is Ninja) which didn't work and finally ended on what I have (the enemies are never subclassed so your method is actually what I meant to write). –  Kevin DiTraglia Sep 20 '12 at 11:42
    
FYI, playing with this now, you need to change it to enemy.getClass() for this to work correctly. enemy.class throws a compile error. –  Kevin DiTraglia Sep 20 '12 at 22:24
    
Right you are, thanks I've fixed it in the answer. –  Gary Buyn Sep 21 '12 at 8:21
    
@KDiTraglia In Java you can (and should) use if (enemy instanceof Ninja) –  msell Sep 21 '12 at 8:45
add comment

Basically I'd question your approach in general, subclassing is in this case not a good idea, from my point of view.

I think to avoid this glitch, you could start using a single enemy class that you can instantiate in a simple way to prevent this inheritance relation which causes you problems later on. Furthermore you could introduce a configuration mechanism, maybe using xml, where you describe your enemies and their attributes. For instantiation then you can create a new randomly chosen enemy from the existing templates, without having to worry about the actual creature type.

For an example you could look into the way the Stendhal project does it:

If you then still need to differentiate things like behaviour, you need to decompose this into singular components from which you can construct your enemy instances.

share|improve this answer
    
Not a bad idea, I'll keep it in mind next game I design, but this would be quite an overhaul of lots of already written code to implement now. –  Kevin DiTraglia Sep 21 '12 at 16:32
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.