Event Fired AgentActions

Hi all,



Shmooh mentioned something very interesting in the "AI game structure" thread. Event Fired AgentActions.



Heres my proposal of usage (from a users point of view):



public void Entity::addListener(Listener l) {
  listenersArrayList.attachChild(l);
}




public interface Listener {
  public void actionPerformed();
}



The only question I have regarding implementation is to do with firing events. Id like to do something like this:


if (collision == true) {
  SomeEntity.fireEvents();
}



The thing is tho, there are certain events that don't want to get fired whena collision has occured. So we need to create a Filtering System that will only call some listeners when they are needed. So our code changes to:


if (collision == true) {
  SomeEntity.fireEvents(EventFilter ef);
}



But what should that filter contain?

DP

This turned out to be simpler than expected:



Listener:


public insterface Listener {
  public void actionPerformed(AgentEvent evt, Entity ent);
}



AgentEvent


public class AgentEvent {
  private String id;
 
  public AgentEvent(String id) {
    this.id = id;
  }

  public String getType() {
    return id;
  }

  public void setType(String type) {
    id = type;
  }
}



Now in Entity:


public void addListener(Listener lstnr) {
  eventListener.add(lstnr);
}

public void fireEvent(AgentEvent evnt) {
  for (int i = 0; i < eventListeners.size(); i++) {
    ((Listener)eventListeners.get(i)).actionPerformed(evnt, this);
  }
}



And you use it in the following manner:


private void initPlayer() {
  Node model = loadModel("com/data/models/player.jme");
   playerEntity = new PlayerEntity("Some entity", model);
   playerEntity.addListener(new Listener() {
     public void actionPerformed(AgentEvent evnt, Entity t) {
       if (evnt.getType().equals(AgentEventPool.COLLISION)) {
         RunAction ra = new RunAction("Run");
         t.setAgentAction(ra, 5, false);
       }
     }
   });
}



Ofcourse, you can have predefined Listeners, the above just shows everthing together.

This just removes the need to have messages fire events instead of real listeners and events.

DP :)

Cool stuff, DP. Glad that worked out… I think it will make the system easier to understand and use.



A few quick thoughts/comments…



First… You might want to call it something other than ‘Listener’… Maybe ‘AgentListener’? Makes the code easier to use/understand.





Next… you didn’t post a ‘removeListener’ method? You might want to include one if you haven’t already.





Next… How often will events get fired, typically? Are we talking constantly throughout a game? Seems like this would happen any time there’s a collision with a particular entity, yes?



If so… you might want to consider playing with that ‘fireEvent’ loop a little. I’m guessing your ‘eventListeners’ object is an ArrayList, which I think would be your best choice when using a Collection object. However, you might want to consider using an actual array. Fewer method calls, and no type casting.



It’s not a big deal if the performance is good… (read: not worth doing if it’s good)… but if something is happening every frame (eg, if the player were walking and it was doing some weird collision with the ground? I really don’t know the typical usage, here…) then it should help. It’s not too complicated to re-size an array as needed, and it only happens when you add and remove them.





One final question/comment I have is in your ‘initPlayer()’ example… where you’re creating your Listener. You do:



if (evnt.getType().equals(AgentEventPool.COLLISION))



I assume 'COLLISION' is constant? Is there any reason not to use the much faster '=='? If so, you might want to include that as a note in the javadocs.


I think that's all I've got. Very small/minor details.

As a whole.. cool stuff, DP.


--K

yeah, sorry, was too rushed to include a removeListener method in Entity. I will do so.



I haven’t incoorporated events in my game yet, but my above code is meant to fire an event on every player collision.



The thing is, the getType() is currently a String, I could change it to an int to make == . But the system will not be consistant as the FSM uses Strings rather than ints.



Its not a big deal to change em now anyway. So we have to decide. Strings or ints?



How would I resize an array? Ive never needed to resize one, so I dont know how ://. Something with System.copyarray(…); ?



DP

ive tried both now, and the performance is idential for 10 listeners. Around the amount needed for each entitty. The only thing that the Array way does is complicated code. So ive kept the ArrayList.



The thing is tho, if I switch to ints, Entity’s names have to be ints too (to keep stability and normality through the system), and then that would confuse the system if two entities have the same int.



Using Strings doesn’t mean that you cant use constants. As a matter of fact, im encouraging it in the docs. But I can see your reasons for using ints instead of Strings.



If you remember, we had that discussion whether to use ints or strings at the start of the FSM creation. And we chose Strings because it gives us more variaty. I believe that is the right choice that we made, but unless switch statements make some significat changes to the way things are handled. Il think il stick to strings :slight_smile:



DP