AgentAction

Hi all,

I was looking at the features of the "Torque Engine". I was also thinking about the way in which scripted languages will be integrated into jME when it hit me.



The torque engine has a scripting language that is similar to C++, which looks similar to this:



update(float time) {
 agent.setAction("walk($time)");
 agent.setAction("stand($time)");
}



What im proposing is an AgentAction interface. Every action that an agent will do, run, walk, stand, scratch head....etc will implement this interface.
This interface will have a "doAction()" method whereby when setAction is called, it will call this method.

So our code would look like this:

AgentAction


public interface AgentAction {
   public void doAction();
}



And this is how you could implement this interface:

AgentAction_Run


public class AgentAction_Run implements AgentAction{
   
   // this is the controller that controls the agents
   // animations
   private Controller agentModelController;
   
   public void setController(Controller c) {
      agentModelController = c;
   }
   
   public void doAction(){
      agentModelController.setRepeatType(Controller.RT_WRAP);
      agentModelController.setMinTime(170f);
      agentModelController.setMaxTime(175f);
   }

}



So in entity, a new method will be available if agreed upon, and that method is setActions(AgentAction aa) and this method will set the controller and call doAction() on the spatial that is tied with the entity.

so in the entity, the method would look like this:

GangsterEntity


public void setAction(AgentAction agA) {
  Controller c = getSpatial().getChild(0).getController(0);
  agA.setController(c);
  agA.doAction();
}



Any thoughts regarding this?

DP

oh just thought of one more thing. The interface will also have to know about the spatial in order to modify its location somehow.



Also, there will be a special "delayAction(float time)" method whereby a loop (in a seperate thread perhaps? ) occurs in which the next action will be delayed. This will be useful for making the character run for so long before stopping.



DP

You could probely can use reflect to fined the methods to call(Like java beans). I do not know if what your said is a better idea, I am just trying to point out alternative ideas.

sure, that would be an alternative, but its too loose. There isn’t one method you could call. It ends up being a standard you have to follow and its not a very strict standard.



What im proposing is a way to integrate a scripting language (like Groovy) with jME in a standard way.



DP

ive had a shower, cleared my head and Ive come up with a way to scheduel the time delays (its harder than you think!)



Each AgentAction will implement Runnable and it will start a new TimerTask with that AgentAction as the run() method.



I will have a stack of delay’s in seconds (ints) which will act as the delayer. So the scripting code would look something like this:



someRandomMethodWithAnAnimationSequence(Entity t) {
  AgentAction_Run = new AgentAction_Run()
  // do some code here to apply the AgentAction_Run spatial and controller from entity t
 
  AgentAction_Crouch = new AgentAction_Crouch();
  // do some code here to apply the AgentAction_Crouch spatial and controller from entity t

  AgentAction_GetUp = new AgentAction_GetUp ();
  // apply the AgentAction_GetUp spatial and controller from entity t

 
  schedule(AgentAction_Run, getTotalDelay()); // the stack is 0 at the moment
  delay(2); // delay two seconds
  schedule(AgentAction_Crouch getTotalDelay()); // so the total is 2 seconds
  delay(2); // delay a furthur two seconds
  schedule(AgentAction_GetUp, getTotalDelay()); // total is 4 seconds
}



what do you think?

We could hide the schedule calls....

DP

orrrrr what we could do is create a seperate thread for each animation sequence call and the delay would be a sleep call instead. And the sechedule calls will disappear and entityT.setAgentAction(AgentAction_Run_instance);



what do you think?



DP

DP, The AgentAction is a good idea. It seems that it currently deals with charcater animation. I am not sure why they need to be runnable as it wiil creates an extra overhead (for the threads).



I am also thinking that each character is part of a scenegraph. To make it flexible, might make sense to have the ability to detach from one scenegraph and attach to another if needed. So in reality the character is independent and can move between levels.

hope this is useful.

tomcat

the only reason they need to be thread based is because of the delay() action.



The delay must not occur on the rendering thread because if sleep(float time) is called then the rendering thread will sleep which is a big no no.



So there are two methods to deal with this. Create another thread to deal with the whole issue. Or create a seperate thread for each AgentAction and call the schedual thing on it.



Personally, i feel that creating another single thread to deal with it is a good idea because only another thread will be created and delay can be turned into sleep and no stack will be used…etc. And this also makes sure that only one thread calls the entity.setAction(AgentAction aa) method so no need for synchronisation.



Il code that method, see how it works…etc. If anyone is not happy with it, we’l change it.



Also, it doesn’t necessarily have to deal with animation. Thats just one part of it. It can deal with spatial position changes as well. So in essence, it get rids of the entire issue of having to deal with spatial movements and animation controlling with agents.



If it goes well, we’l kick in collision detection as well in there somehow, because I think this is abstract enough to be very usable while providing code decentralistion which is always a good thing in my books.



Sry for the long post :slight_smile:



DP

The problem with using threads is that you have to were about the issue of thread safety. You would probably need to make shore that the second thread only changes the sean during the beginning of updating and stop the updating with sleep wile you do this. In my editor I solved the problem with a class that waits for the top of the update seen and then stops the update until it is reedy.



I recommend that you leave the ability to pose the game wile a menu is up or something.

sure, i get you about synchorisation. But the setAction() method will only be called from the AI thread as there is no need to call it from the rendering thread. Il add a synchorized keyword there just incase.



DP

Ok, eric has comeup with a fabulous idea. But its going to have to change some things…



and update(float time) method will have to be introduced to keep track of the time and such.



So the threads idea has gone out of the window, and a new idea has emerged which seems rather good.



Il keep you all posted.



DP

the threads have gone. However, that has come at a cost of design. I was hoping that this AI code will be fully event driven.



To compromise; I have added an arrayList which will hold the list of AgentActions that need to be updated everyframe using the update(float time) method. I think this will keep the AI event driven and give the availability of a constant update.



Just one little java thing. When overwriting a method, Will the new overwritten method call super.update(time) automagically? or does the user have to do this?



DP

You need to do that yourself.

cool, thx renanse. So I should advice strongly to call the super.update(float time) in the Java docs then.



Oh btw, AgentActions are done. No more threads.



Their use has changed; they are now called on an entity basis:



someEntity.setAction(AgentAction_runForwards, 1f, true);
someEntity.setAction(AgentAction_leanLeft, 5f, false);



Let me explain the arguments.

1) the actual action to do

2) the delay. Now the delay has changed from being absolute to being relative. That means that the action will be called after 5 seconds regardless if the previous has finished or not. So if you set both delays to 1, both actions will be called simultaneously.

3) a boolean value stating whether this action needs to be carried out every frame or not. And yes, you can remove them later on.

And thats it.
DP :)

Three things:

  • If it is absolutely imperative that super.update(float) time is called, I would use a slightly different pattern:


    public abstract class Super {
        void update(float time) {
            /* implementation */
            implUpdate(time);
        }

        abstract void implUpdate(float time);
    }
    This would guarantee that both the API and user code is executed and usage is far clearer. It's also potentially more extensable.

  • What about cyclic actions - how would they be handled? Say, perhaps, some task that is executed once every minute.
  • If this is going to be packaged with jME, I would suggest using Sun-standard coding conventions. i.e. AgentAction_leanLeft would become LeanLeftAgentAction. This is more of an aesthetic concern, but using naming standards might encourage people to check this out.
    [/list:u]

thx eric, i think il use your way. But it still wont stop them from overwriting the update method tho. il add an abstract doUpdate(float time) method and that will be called from the update(float time) method.



About cyclic actions, il think about them. But i dont forsee any action that needs to be done on a time based. Nevertheless il add that feature in.



Il convert everything to sun standards now.



DP

Just a quick note…



You can prevent people from overriding methods by using the ‘final’ keyword:



public final void someMethod() {
     System.out.println("You can't override me.  Ha ha!");
}



You could do that with the update method to prevent that from happening. If somebody tried, they'd get a compile-time error.

Be warned that it will restrict people, though. People will never be able to override it, even if they want/need to.. so use with caution. It's easy to take out later if a situation comes up where overriding is needed, though. It won't break any existing code.


--K

yup, thats what im planning on doing.



The final method: update(float time) will call doUpdate(float time) which the user will be able to update to overwrite.



DP

right, im pleased to announce, that AgentActions have been implemented and are fully functional. Ive still yet to implement cyclic actions.



The new zip can be found at:



http://www.myjavaserver.com/~digiwired/ai-code.zip

just thought of a cool idea. The removeAction(AbstractAgentAction) will now change to removeAction(AbstractAgentAction, float timedelay);



This will mean that the removal from the list can be delayed and doesn’t happen instantly. Ofcourse setting the timedelay to 0 does remove in the next update.



DP