1
\$\begingroup\$

I'm trying to figure out how to write a good AIManager class in Java in order to manage the AI of all the objects in my game.

From another post of mine, an user has suggested to do the following:

Your calculations should go into the update method. it's fine to have an AIManager class, that has an update() method itself which gets called in the main update loop. this AIManager could hold a list of all the Objects that need to be updated. Since you're using java, you could use an interface to declare something to have an AI, and use this interface to iterate over your objects (which could then be completely different Classes) and have interactions managed by this AI manager.

It is not so clear to me.

If my Player class implements SeekArriveMovement (for instance), then it should implement the methods of this interface. And AIManager have to implement an update method. But... i don't know. I mean.. how the AIManager should be built and how it should interact with the objects?

Something like this.. ?

public class AIManager {

private List<Object> objects;

public AIManager() {
    objects = new ArrayList<Object>();
}

public void addObject(Object o) {
    objects.add(o);
}

public void removeObject(Object o) {
    objects.remove(o);
}

public void update() {
    for(Object o : objects) {
        if(objects instanceof SeekArriveMovement) { // ?
            RedEnemy re = (RedEnemy)o;
            move(re);
        }
    }
}

private void move(RedEnemy re) {
    // ?
}
\$\endgroup\$
1
\$\begingroup\$

I guess what the user on your other question suggested was something like the following.

Your interface provides the method move

public interface SeekArriveMovement {
    void move();
}

which is implemented by your Player class

public class Player implements SeekArriveMovement {
    @Override
    public void move() {
        // do something
    }
}

In your AIManger you iterate over all objects in need of an update, identify all classes using the SeekArriveMovement interface and call their move method.

public class AIManager {
    private List<Object> objects = new ArrayList<Object>();

    public void update() {
        for (Object object : objects) {
            if (object instanceof SeekArriveMovement)
                ((SeekArriveMovement) object).move();
        }
    }
}

The problem with your update method is that you call instanceof SeekArriveMovement on objects which is your list of objects. Further you're trying to cast the current object of the list (o) to ReadEnemy without knowing if it is an instance of this class which could lead to an exception.

\$\endgroup\$
  • \$\begingroup\$ So... what do you suggest to solve these problems? Therefore, what is the advantage to use this design? \$\endgroup\$ – user3075478 Mar 7 '15 at 20:14
  • \$\begingroup\$ The problems of your update method are solved in my update method ;) I check if the current object implements the desired interface. If yes I cast it to that interface and call its method on that object. The advantage is that you don't have to know the class which implements the interface. If a class implements the interface you can cast it to that interface and call its method on it. \$\endgroup\$ – Kagemusha Mar 8 '15 at 8:36
  • \$\begingroup\$ I meant, what is the advantage to have this AIManager class? \$\endgroup\$ – user3075478 Mar 8 '15 at 8:49
  • \$\begingroup\$ It's the encapsulation of functionality. Of course this depends on the game you're making. If you're making Pong an AIManager would be overkill imo. But if you're going to have many AI controlled entities it's good design to control them in one place. \$\endgroup\$ – Kagemusha Mar 8 '15 at 9:29
1
\$\begingroup\$

I thought this was a good solution for a similar problem, from a question (and answer) by me here on SO.

Abilities here are essentially AI if they are tickable. I have a runnable that just runs through all Actors and their abilities and tries to tick() them.

interface for manipulating abilities:

public interface ActorAbility {

    // doesn't work, but need something to enable
    // instance retrieval for addAbility...
    public ActorAbility getInstance();

    public void act(Actor actor);

    public boolean isTickable();

    // maybe add
    // public boolean isAI();

}

sanitized implementation of interface:

public enum ActorMove implements ActorAbility {
    INSTANCE;

    private ActorMove() {
    }

    public ActorAbility getInstance() {
        return INSTANCE;
    }

    public void act(Actor actor) {
        log.debug("Move");
        // or, AI stuff goes here
    }

    public boolean isTickable() {
        return true;
    }

}

new class:

public enum Ability {
    MOVE(ActorMove.INSTANCE), FIGHT(ActorFight.INSTANCE);

    private ActorAbility ability;

    private Ability(ActorAbility abilityClass) {
        this.ability = abilityClass;
    }

    public ActorAbility getAbility() {
        return this.ability;
    }
}  

StdActor:

public class StdActor implements Actor {
private HashSet<Ability> abilities = new HashSet<>();

public void addAbility(Ability ability) {
    this.abilities.add(ability);
}

subclass:

public class StdCharClass extends StdActor {

    public StdCharClass() {
        addAbility(Ability.MOVE);
    }

}

and finally, usage:

HashSet<Ability> abilities = bob.getAbilities();
for (Ability ability : abilities) {
    ActorAbility abilityClass = ability.getAbility();
    if (abilityClass.isTickable()) {
        abilityClass.act(bob);
    }
}

output on tick()

12:44:15.835 [main] DEBUG ActorMove - Move
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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