GameSystem ready!

Hi everybody,

Its been a long road for this to squish all the bugs but Ive done it… :smiley:



So excuse the long, and here is the code:



GameSystem


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.gamesystem;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import com.jme.entity2.Entity;
import com.jme.gamesystem.events.EntityEvent;
import com.jme.gamesystem.msg.Message;
import com.jme.scene.Spatial;

/**
 * Controls the entire GameSystem framework. It contains all the entities and updates
 * them accordingly. Messages should be sent through here with the entity's name
 * as the "TO" variable. It can also check for collision with entities. Can fire
 * global events too.
 *
 * @author Ahmed
 * @version: $Id: GameSystem2.java, Sep 13, 2004 3:00:20 PM
 */
public class GameSystem {

   // an instance of this class
   private static GameSystem instance;

   //a hashmap containing all the entities key: entity's name value: entity
   private HashMap entityList;

   // a queue for messages that are to be sent
   // the messages that are sent are removed
   // from the queue
   private ArrayList messageQueue;

   /**
    * Constructor for the AISystem. Initialises different aspects of the
    * AISystem.
    *
    * @see com.jmeai.AISystem#createAISystem()
    */
   private GameSystem() {
      entityList = new HashMap();
      messageQueue = new ArrayList();
   }

   /**
    * <code>createGameSystem</code> creates the Game system if there isn't
    * already an AI system present in the JVM
    *
    * @return GameSystem
    */
   public static GameSystem createGameSystem() {
      if (instance == null) {
         instance = new GameSystem();
      }

      return instance;
   }

   /**
    * Returns the instance of the GameSystem present in the JVM
    *
    * @return GameSystem
    */
   public static GameSystem forceObtainInstance() {
      return instance;
   }

   /**
    * Returns the number of entities in this GameSystem
    *
    * @return int
    */
   public int getQuantity() {
      return entityList.size();
   }
   
   /**
    * Returns a boolean value stating whether this
    * entity is attached to the GameSystem or not.
    * @param t, the entity to check
    * @return boolean
    */
   public boolean hasChild(Entity t) {
      return entityList.containsValue(t);
   }

   /**
    * Detach an entity from the GameSystem
    *
    * @param entity,
    *            the entity to detach
    */
   public Object detachChild(Entity entity) {
      return entityList.remove(entity.getID());
   }

   /**
    * Detach and entity from the GameSystem based on it's name
    *
    * @param name
    */
   public Object detachChild(String name) {
      return entityList.remove(name);
   }

   /**
    * Removes all entities from the GameSystem
    */
   public void detachAll() {
      entityList.clear();
   }

   /**
    * Attaches and Entity to the GameSystem
    *
    * @param entity,
    *            the entity to attach
    */
   public void attachChild(Entity entity) {
      entityList.put(entity.getID(), entity);
   }

   /**
    * Returns an entity with a specified name
    *
    * @param name
    * @return
    */
   public Entity getEntityWithName(String name) {
      return (Entity) entityList.get(name);
   }

   /**
    * Sends a message to an entity
    *
    * @see com.jmeai.msg.Message
    * @param message
    *            the message to send
    */
   public void sendMessage(Message message) {
      MessageHolder mh = new MessageHolder();
      mh.message = message;
   }

   /**
    * Fires a global event. This event will trickle down to all Entities and
    * their fireEvent(AgentEvent) method will be called to perform that action
    *
    * @param aEvent
    *            the AgentEvent to fire
    */
   public void fireGlobalEvent(EntityEvent aEvent) {
      for (Iterator it = entityList.values().iterator(); it.hasNext();) {
         Entity pEntity = (Entity) it.next();
         pEntity.fireEvent(aEvent);
      }
   }

   /**
    * <code>update(float)</code> needs to be updated every frame in order to
    * establish which messages need to be sent now and update the messages's
    * current time. It also updates all the entities attached to this AISystem
    *
    * @param time
    *            the absolute time difference between the frames
    */
   public final void update(float time) {
      // loop through and update all entities
      for (Iterator it = entityList.values().iterator(); it.hasNext();) {
         Entity pEntity = (Entity) it.next();
         pEntity.update(time);
      }

      // loop through and send all the messages
      // that need to be sent
      for (int i = 0; i < messageQueue.size(); i++) {
         MessageHolder mh = (MessageHolder) messageQueue.get(i);
         // add the time interpolation to the current time
         mh.currentTime += time;

         // if its time to deliver, then do so
         if (mh.currentTime >= mh.message.timeOfDelivery) {
            Entity pEntity = (Entity) entityList.get(mh.message.to);
            if (pEntity != null) {
               pEntity.sendMessage(mh.message);
            }
            // remove from the queue if no longer is needed
            messageQueue.remove(i);
         }
      }
   }

   /**
    * Checks to see whether a collision has occured or not with the parameter's
    * entity. If a collision has occured, it will store the Entity that it has
    * collided with in an array. When its done it will return that array. A
    * boolean parameter states whether to go down to the triangle level or not.
    *
    * @param checkEntity,
    *            the Entity to check against all other entities
    * @param triangleAccuracy,
    *            to check with triangle accuracy or not
    * @return Entity[]
    */
   public Entity[] hasCollision(Entity checkEntity, boolean triangleAccuracy) {
      // create a new arraylist to hold all the
      // collided entities
      ArrayList entitiesWithCollision = new ArrayList();
      // obtain an iterator from the hashmap
      Iterator it = entityList.values().iterator();
      // if the iterator still has more items, then loop
      while (it.hasNext()) {
         // obtain the next entity, check to see
         // if that entity is not the entity that the
         // user has supplied.
         Entity pEntity = (Entity) it.next();
         if (pEntity != checkEntity) {
            // obtain the spatial of that entity
            // and check for a collision
            Spatial spat = pEntity.getSpatial();
            boolean collision = checkEntity.getSpatial().hasCollision(spat,
                  triangleAccuracy);
            // if the collision is true, then
            // add that entity to the array list
            if (collision == true) {
               entitiesWithCollision.add(pEntity);
            }
         }
      }

      // when the loop finishes looping, return an array
      // of entities that has collided      
      Object[] obj = entitiesWithCollision.toArray();
      Entity[] returnEntity = (Entity[])obj;

      return returnEntity;
   }

   /**
    * Checks a collision with an entity versus an array of entities. This
    * checks against a specified array of entities and returns an array of
    * entities that the collision has occured with. A boolean parameter
    * specifies whether the collision is triangle accurate or not
    *
    * @param checkEntity,
    *            the entity to check
    * @param arrayOfEntities,
    *            a array of entities to check against
    * @param triangleAccuracy,
    *            whether the collision detection is triangle accurate
    * @return Entity[]
    */
   public Entity[] hasCollision(Entity checkEntity, Entity[] arrayOfEntities,
         boolean triangleAccuracy) {
      // an arraylist which holds the successful collision entities
      ArrayList entitiesWithCollision = new ArrayList();
      // loop through the array
      for (int i = 0; i < arrayOfEntities.length; i++) {
         // obtain the entity and check that it
         // isn't the same as the entity that we are
         // checking for
         Entity pEntity = arrayOfEntities[i];
         if (pEntity != checkEntity) {
            // obtain the spatial and see if a
            // collision has occured
            Spatial spat = pEntity.getSpatial();
            boolean collision = checkEntity.getSpatial().hasCollision(spat,
                  triangleAccuracy);
            // if so, then add to the list
            if (collision == true) {
               entitiesWithCollision.add(pEntity);
            }
         }
      }
      // when the loop finishes looping, return an array
      // of entities that has collided   
      Object[] obj = entitiesWithCollision.toArray();
      Entity[] returnEntity = (Entity[])obj;
      
      return returnEntity;
   }

   /**
    * Checks a collision with an entity versus an array of entities. This
    * checks against a specified array of entities and returns an array of
    * entities that the collision has occured with. A boolean parameter
    * specifies whether the collision is triangle accurate or not. This
    * method differs from hasCollision(...) by checking that
    * the currently looped entity in the array is attached to the
    * AISystem before checking the collision;
    *
    * @param checkEntity,
    *            the entity to check
    * @param arrayOfEntities,
    *            a array of entities to check against
    * @param triangleAccuracy,
    *            whether the collision detection is triangle accurate
    * @return Entity[]
    */
   public Entity[] hasCollisionWithSystem(Entity checkEntity,
         Entity[] arrayOfEntities, boolean triangleAccuracy) {
      // an arraylist which holds the successful collision entities
      ArrayList entitiesWithCollision = new ArrayList();
      // loop through the array
      for (int i = 0; i < arrayOfEntities.length; i++) {
         // obtain the entity and check that it
         // isn't the same as the entity that we are
         // checking for
         Entity pEntity = arrayOfEntities[i];
         if (pEntity != checkEntity
               && entityList.get(pEntity.getID()) != null) {
            // obtain the spatial and see if a
            // collision has occured
            Spatial spat = pEntity.getSpatial();
            boolean collision = checkEntity.getSpatial().hasCollision(spat,
                  triangleAccuracy);
            // if so, then add to the list
            if (collision == true) {
               entitiesWithCollision.add(pEntity);
            }
         }
      }
      // when the loop finishes looping, return an array
      // of entities that has collided
      Entity[] returnEntity = new Entity[entitiesWithCollision.size()];
      for (int i = 0; i < entitiesWithCollision.size(); i++) {
         returnEntity[i] = (Entity) entitiesWithCollision.get(i);
      }

      return returnEntity;
   }

   /*
    * A holding place for the messages. Contains the message that needs to be
    * sent and the current time for that message
    */
   private class MessageHolder {
      public Message message;
      public float currentTime = 0;
   }
}



Entity
Btw, the naming of the package of the Entity is so that it doesn't become confused with the entity that is already there.


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.entity2;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Properties;
import java.util.logging.Level;

import com.jme.gamesystem.action.IEntityAction;
import com.jme.gamesystem.events.EntityEvent;
import com.jme.gamesystem.events.IEntityListener;
import com.jme.gamesystem.msg.Message;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.model.XMLparser.JmeBinaryReader;
import com.jme.util.LoggingSystem;

/**
 * An <code>Entity</code> class that functions as the main functioning pillar
 * of the GameSystem. It work on a message bases whereby when a message is
 * recieved a processMessage() method is called to process the message. A
 * Scripting language should be used to provide a concrete processMessage()
 * method so maximum scalability can be reached. IEntityListeners should be
 * added to listen for EntityEvents. IEntityActions are called on seperate
 * entities too.
 *
 * @author Ahmed
 * @version: $Id: Entity.java, Jul 22, 2004 12:29:19 PM
 */
public class Entity {

   // some properties of this entity
   private Properties properties;

   // the name of this entity
   private String name;

   // the controlled spatial
   private Spatial spatial;

   // the message that has been recieved
   protected Message message;

   // the list of AgentActions that need to be updated
   // every frame
   private ArrayList agentTimeIntervalList;
   private ArrayList agentActionList;

   // a list to hold the AgentActions that are yet to
   // be called
   private ArrayList agentActionQueue;

   // contains a list of all the listeners
   // that are attached to this entity
   private ArrayList listenerList;

   /**
    * Constructor creates a new <code>Entity</code> object. During creation a
    * string id is used to denote a unique entity. No spatial is set for this
    * constructor.
    *
    * @param id
    *            the entity id.
    */
   public Entity(String id) {
      properties = new Properties();
      agentTimeIntervalList = new ArrayList();
      agentActionList = new ArrayList();
      agentActionQueue = new ArrayList();
      listenerList = new ArrayList();
      setID(id);
   }

   /**
    * Constructor creates a new <code>Entity</code> object. During creation a
    * string id is used to denote a unique entity. A spatial is associated with
    * this entity.
    *
    * @param id
    *            the entity id.
    * @param spat
    *            the spatial that is associated with the entity
    */
   public Entity(String id, Spatial spat) {
      properties = new Properties();
      agentTimeIntervalList = new ArrayList();
      agentActionList = new ArrayList();
      agentActionQueue = new ArrayList();
      listenerList = new ArrayList();

      setID(id);
      setSpatial(spat);
   }

   /**
    * Loads a model and sets that model as the its spatial. The model must be
    * in jme format, no other format is supported.
    *
    * @param id
    *            the name of this entity
    * @param src
    *            the source of the model
    * @see com.jme.scene.model.XMLparser.JmeBinaryReader;
    */
   public Entity(String id, String src) {
      Node model = null;
      try {
         JmeBinaryReader jbr = new JmeBinaryReader();
         URL modelURL = Entity.class.getClassLoader().getResource(src);
         model = jbr.loadBinaryFormat(modelURL.openStream());
      } catch (IOException ioe) {
         LoggingSystem.getLogger().log(Level.WARNING,
               "Could not load model: " + src + ", from Entity: "
                     + getID());
      }

      properties = new Properties();
      agentTimeIntervalList = new ArrayList();
      agentActionList = new ArrayList();
      agentActionQueue = new ArrayList();
      listenerList = new ArrayList();

      setID(id);
      setSpatial(model);
   }

   /**
    * <code>setSpatial</code> sets the spatial object used to define the
    * entity's graphical representation.
    *
    * @param spatial
    *            the spatial object used to describe the geometry of the
    *            entity.
    */
   public void setSpatial(Spatial spatial) {
      if (spatial != null) {
         this.spatial = spatial;
      }
   }

   /**
    * <code>getSpatial</code> retrieves the spatial object of the entity.
    *
    * @return the spatial object of the entity.
    */
   public Spatial getSpatial() {
      return spatial;
   }

   /**
    * Sets the name of this entity
    *
    * @param id,
    *            the name of this entity
    */
   public void setID(String id) {
      this.name = id;
   }

   /**
    * Obtains the name of this entity
    *
    * @return String
    */
   public String getID() {
      return name;
   }

   /**
    * Get a property of this entity.
    *
    * @param propertyName
    *            the property name to retrieve.
    * @return The entity's property linked to propertyName.
    */
   public Object getProperty(String propertyName) {
      return properties.get(propertyName);
   }

   /**
    * Binds a property name of the entity with it's property object.
    *
    * @param propertyName
    *            the property name.
    * @param property
    *            the propery to bind with the name.
    */
   public void setProperty(String propertyName, Object property) {
      properties.put(propertyName, property);
   }

   /**
    * @see com.jmeai.entity.Entity#setProperty(String, Object)
    */
   public void setPropertyString(String propertyName, String property) {
      properties.put(propertyName, property);
   }

   /**
    * @see com.jmeai.entity.Entity#getProperty(String)
    */
   public String getPropertyString(String propertyName) {
      return (String) properties.getProperty(propertyName);
   }

   /**
    * @see com.jmeai.entity.Entity#setProperty(String, Object)
    */
   public void setPropertyInt(String propertyName, int property) {
      properties.put(propertyName, new Integer(property));
   }

   /**
    * @see com.jmeai.entity.Entity#getProperty(String)
    */
   public int getPropertyInt(String propertyName) {
      return ((Integer) properties.get(propertyName)).intValue();
   }

   /**
    * @see com.jmeai.entity.Entity#setProperty(String, Object)
    */
   public void setPropertyFloat(String propertyName, float property) {
      properties.put(propertyName, new Float(property));
   }

   /**
    * @see com.jmeai.entity.Entity#getProperty(String)
    */
   public float getPropertyFloat(String propertyName) {
      return ((Float) properties.get(propertyName)).floatValue();
   }

   /**
    * @see com.jmeai.entity.Entity#setProperty(String, Object)
    */
   public void setPropertyBoolean(String propertyName, boolean property) {
      properties.put(propertyName, new Boolean(property));
   }

   /**
    * @see com.jmeai.entity.Entity#getProperty(String)
    */
   public boolean getPropertyBoolean(String propertyName) {
      return ((Boolean) properties.get(propertyName)).booleanValue();
   }

   /**
    * sets the message for this entity, this will call the
    * <code>processMessage()</code> so that the message can be translated
    *
    * @param message
    *            the message that will be processed
    */
   public void sendMessage(Message message) {
      this.message = message;
      processMessage();
   }

   /**
    * This method will be called once a message has been recieved. This method
    * would be best executed from a scripting language of some sort.
    */
   protected void processMessage() {
   }

   /**
    * <code>setAction</code> will be called from the scripting language
    * after it has been delayed for some period of time. It alread has been
    * given a controller and a spatial. So just do the action.
    *
    * @param abstractAction
    *            the action to execute
    * @param delayTime
    *            the delay time before execution
    * @param updatedEveryFrame
    *            whether to execute it everyframe after the delay or not
    */
   public void setAction(IEntityAction abstractAction, float delayTime,
         boolean updatedEveryFrame) {

      EntityActionHolder aah = new EntityActionHolder();
      aah.timeDelay = delayTime;
      aah.agentAction = abstractAction;
      aah.execute = updatedEveryFrame;
      aah.action = EntityActionHolder.ADDING;

      agentActionQueue.add(aah);
   }

   /**
    * <code>removeAction</code> will remove an action from both the updating
    * list and the queue if it exists. The time delay is a parameter to
    * sepecify the removal time
    *
    * @param iAction
    *            the IEntityAction to remove
    * @param delay
    *            the time delay
    */
   public void removeAction(IEntityAction iAction, float delay) {

      EntityActionHolder aah = new EntityActionHolder();
      aah.action = EntityActionHolder.REMOVING;
      aah.agentAction = iAction;
      aah.timeDelay = delay;

      agentActionQueue.add(aah);
   }

   /**
    * <code>removeAction</code> will remove an action from both the updating
    * list and the queue if it exist. It removes the action on the next update
    *
    * @param iAction
    *            the IEntityAction to be removed
    */
   public void removeAction(IEntityAction iAction) {

      EntityActionHolder aah = new EntityActionHolder();
      aah.agentAction = iAction;
      aah.action = EntityActionHolder.REMOVING;
      aah.timeDelay = -1;

      agentActionQueue.add(aah);
   }

   /**
    * Adds a IEntityListener to the listen to an
    * <code>EntityEvent</code> being triggered. The
    * <code>IEntityListener</code> has to check whether the event is
    * associated with it or not.
    *
    * @param listener
    *            the listener to add to the list of listeners
    */
   public boolean addListener(IEntityListener listener) {
      return listenerList.add(listener);
   }

   /**
    * Removes a <code>IEntityListener</code> from the list of listeners
    *
    * @param listener
    *            the IEntityListener to remove
    * @return boolean
    */
   public boolean removeListener(IEntityListener listener) {
      return listenerList.remove(listener);
   }

   /**
    * Fires an <code>EntityEvent</code>. This looks through all the listeners and
    * calls <code>Listener.actionPerformed(EntityEvent evt)</code>. The actual
    * listener would have to check whether that event is associated with it or
    * not.
    *
    * @param evnt
    *            the event to fire
    */
   public void fireEvent(EntityEvent evnt) {
      for (int i = 0; i < listenerList.size(); i++) {
         ((IEntityListener) listenerList.get(i)).actionPerformed(evnt, this);
      }

   }

   /**
    * <code>update</code> will be called from the GameSystem or the update
    * method itself with the frame time interpolation as the argument. This
    * method will update through the "agentActionList" and call the the
    * approperiate methods
    *
    * @param time,
    *            the exact time between two frames
    */
   public final void update(float time) {
      // loop through the agentActionList that need
      // to be updated every frame and call doAction()
      // from the AbstractAgentAction
      for (int i = 0; i < agentActionList.size(); i++) {
         IEntityAction aaa = (IEntityAction) agentActionList.get(i);
         aaa.doAction(time, this);
      }

      // loop through the queues and see if anything needs
      // to be called
      for (int i = 0; i < agentActionQueue.size(); i++) {
         // obtain and add the interpolation time to the
         // action's current time
         EntityActionHolder aah = (EntityActionHolder) agentActionQueue.get(i);
         aah.currentTime += time;

         // is it time to carry out the action?
         // if it is, then check if it needs to be
         // called every frame, and add the action
         // to the agentActionList list.
         // if it needs to be executed at a set interval
         // then add it to a different arraylist than the
         // list that is updated everyframe
         // also, remove from list

         /*
          * check first if the time delay has been fullfilled if so then see
          * if the cotainer is for adding or removing that action. If its
          * adding, then check if it needs to be executed every frame . If
          * thats true, add it to a agentActionList list. And remove this
          * action from the agentActionQueue list. if its removing you want,
          * remove from the agentActionList and from the agentActionQueue
          */
         if (aah.currentTime >= aah.timeDelay) {
            if (aah.action == EntityActionHolder.ADDING) {
               ((IEntityAction) aah.agentAction).doAction(time, this);
               if (aah.execute == true) {
                  agentActionList.add(aah.agentAction);
               }
               agentActionQueue.remove(i);
            } else {
               agentActionList.remove(aah.agentAction);
               agentActionQueue.remove(aah);
            }
         }
      }

      // call doUpdate(float time) so that the developer
      // can also call things on a regular interval from entity
      doUpdate(time);

   }
   /**
    * <code>doUpdate</code> will be called after the update method. If
    * anything that needs updating everyframe, it needs to be inserted here
    *
    * @param time
    */
   public void doUpdate(float time) {
   }

   private class EntityActionHolder {
      public final static boolean ADDING = true;
      public final static boolean REMOVING = false;
      public boolean action;
      public IEntityAction agentAction;
      public float currentTime = 0;
      public float timeDelay;
      public boolean execute;
   }
}



IEntityAction


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
package com.jme.gamesystem.action;

import com.jme.entity2.Entity;

/**
 *
 * An interface that does an action that is called when
 * an event is triggered. The doAction method takes the
 * interpolation between frames as the first argument, the
 * second is the entity which called this method
 *
 * @author Ahmed
 * @version: $Id: IEntityAction.java, Sep 26, 2004 9:00:46 PM
 */
public interface IEntityAction {
   
   /**
    * Does a particular action for an entity.
    *
    * @param time, the interpolation between frames
    * @param parent, the entity that called this method
    * @see com.jmeai.entity.Entity
    */
   public void doAction(float time, Entity parent);

}



EntityEvent


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.gamesystem.events;

/**
 * An identification protocol that seperates EntityListeners from
 * one another. A pool should be used to store these for all events
 * that are fired. Should be used as a base class for more
 * sofisticated events to be fired
 *
 * @author Ahmed
 * @version: $Id: EntityEvent.java, Sep 15, 2004 9:18:58 PM
 */
public class EntityEvent {

   // the type of this event
   private String type;

   /**
    * Constructor for an AgentEvent
    *
    * @param type,
    *            the type of this event
    */
   public EntityEvent(String type) {
      this.type = type;
   }
   
   /**
    * Obtain the type of event this is
    * @return String
    */
   public String getType() {
      return type;
   }
   
   /**
    * Sets the type of event this EntityEvent is
    * @param type
    */
   public void setType(String type) {
      this.type = type;
   }

}



IEntityListener


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
package com.jme.gamesystem.events;

import com.jme.entity2.Entity;

/**
 *
 * An interface to listener that will be fired when a particular
 * event is triggered. More details are given with its method
 *
 * @author Ahmed
 * @version: $Id: IEntityListener.java, Sep 15, 2004 6:53:18 PM
 */
public interface IEntityListener {
   
   /**
    * A method that is fired when an event has been fullfilled.
    * The entity, when an event has been fired, will loop through
    * its listeners and fire their events. Its up to the listener
    * to check whether it is associated with that event or not
    * through a simple if statement.
    *
    * @param evnt, the EntityEvent that is fired
    * @param parent, the parent that fired that event
    */
   public void actionPerformed(EntityEvent evnt, Entity parent);

}



Message


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.gamesystem.msg;

/**
 * <code>Message</code> is a data structure whereby the
 * information relavant is created. This object bears the
 * minimum information required and should be extended to
 * create more data holders
 *
 * @author Ahmed
 * @version: $Id: Message.java, Jul 22, 2004 12:34:47 PM
 */
public class Message {
   
   // what the data variable contains, e.g. damage
   // messages should be created in a scripting environment
   // so as to keep things pre defined
   public String messageID;
   // from which entity's ID was the message from?
   public String from;
   // to which entity is it going?
   public String to;
   // time of delivery, that is how many seconds ahead
   // 1 being 1 second
   public float timeOfDelivery;
   // the data itself, should be predefined
   public String data;
   
   /**
    * Constructor for <code>Message</code>. Initialises the
    * String variables to "" and the float to -1
    */
   public Message() {
      messageID = "";
      from = "";
      to = "";
      timeOfDelivery = -1f;
      data = "";
   }
}



A short demo will follow soon! :)

DP

Nice work.

I just think that using entity2 for package name isn’t very appropriate… Why don’t you put all the files into com.jme.gamesystem ?



Chman

hehe, its because com.jme.entity is already taken by another entity that doesn’t have much functionality.



I only named it entity2 because entity was taken :slight_smile:



DP

This can be considered a major addition, this has AI stuff in it, when we haven’t really discussed AI (as related to jME). As well as a lot of other things. This will be awhile before going in, Cep, Renanse and I are going to need as many demos as you can crank out to show off all the major parts.



So, don’t be discouraged about how long this might take before we are all happy with it, but it’s not going to just be dropped in. We’ll all have to look at it carefully and bug you about stuff.

sure, no problem. Im working on the Airplane one at the mo. I also have a couple showing off Events…etc. Il post them soon.



DP

Hiya DP,

I was just looking at your code and was wondering what the Properties stuff is and how its used/should be used.



I’ve never used it before so not sure what you’re supposed to do with it.



(Responding to this post is a low priority)

Hi DP,



This is great work and can save a lot of time. Do you have a test program to go with this?

tomcat

Well yes, I have an entire game!



VolatileZapper 2 utilises this. I’l upload the source to someplace soonish.



The properties is as you wish. Health, damage parameters, how many kicks the person has done. Anything really.



DP

I’m been staring at this code for a while and as it makes more and more sense I want to use it more and more heh.



Would adding a PhysicsObject into the Entity class make sense if we are using your Physics Library?



Also, technically you could get rid of the Spatial in the Entity class since you can access the spatial through the PhysicsObject if you added that.



From a code design standpoint would that be a good or bad idea??

well, if i was to integrate the physics system with the entity. the entity would be the only thing you kept.



Entity would have the job to keep everything in sync. It would make sure that the physics is in perfect sync ALWAYS with the graphics and vice versa if you wanted to… It would also fire sounds using the proposed sound manager and makes sure that the sounds are in the right places (hehe, yes we do have 3d sound…you should check it out!). So if you were to use Entity, you wouldn’t actually create any physicsObjects, Entity would do that for you…



From your design, you want entity to be a child of physicsObject, and thats not how its supposed to work. Entity should be the topmost object you deal with.



Anyways, i haven’t looked at that code in aaaaaaaaaaages! If you want support for Physics and stuff in it, let me know. Look at the code as it is, is this good for you? Or would you rather make something up yourself…



DP

Well yes, I have an entire game!

VolatileZapper 2 utilises this. I'l upload the source to someplace soonish.


that would be good, areyou going to do some 3d examples in the future it would be nice to see it works such a situation

yes, as a matter of fact, i am working on volatileZapper 3 which uses this game system and is 3d.



DP

"DarkProphet" wrote:
well, if i was to integrate the physics system with the entity. the entity would be the only thing you kept.

Entity would have the job to keep everything in sync. It would make sure that the physics is in perfect sync ALWAYS with the graphics and vice versa if you wanted to... It would also fire sounds using the proposed sound manager and makes sure that the sounds are in the right places (hehe, yes we do have 3d sound..you should check it out!). So if you were to use Entity, you wouldn't actually create any physicsObjects, Entity would do that for you...

From your design, you want entity to be a child of physicsObject, and thats not how its supposed to work. Entity should be the topmost object you deal with.

Anyways, i haven't looked at that code in aaaaaaaaaaages! If you want support for Physics and stuff in it, let me know. Look at the code as it is, is this good for you? Or would you rather make something up yourself...

DP

I was planning on adding a PhysicsObject to entity (not the other way around). So when you create the entity you give it a spatial and Entity in turn will auto create the PhysicsObject for that spatial.

Another question .... lets say you create an entity called ship. You add a spatial to the ship entity which is just a trimesh that looks something like a ship. Now I want to change the rotation of that trimesh. How do you do that without doing something like ENtity.spatial.setTranslation() which I assume is the wrong way to do it.

well, no, that would be the way to do it.



However, having said that. The entity should have an "update" method which it will check if the physics Objects are not in sync with the graphics and forces a sync on it.



So basically, everything becomes alot more natural to use. You rotate the spatial, and you dont need to worry about thinking "is the physics in sync? is the sound given at the right location?" because all of those will be catered for in entity.



Its a long list of things to do, but in the end, it will make things sooo much easier to do. That was the hope for my game system.



DP

Could you elaborate a little bit on the use of messages? Maybe a simple example of how they should be used and what they can do.



I started work on my own entity class (based off yours but simpler ) :slight_smile:



I like the use making one static GameSystem. Never thought of doing things that way. Is that a specific design pattern? Singleton?

yer, singleton is a design pattern. Its like a OO version of a static class :slight_smile:



Well, there are two different messaging departments in my entity stuff. First is the classic event-listener model whereby an event gets fired, all the listeners are notified and you use a simple if statement to see which event it is and carry it out.



The second is a pure “message” or a string. The string can be delievered when ever you want it to and it delivers stuff like speach. “Player X: Hello, how are you?”



And you would process that in which ever way you want it to. For an RPG, creating a neural network and training it up to match strings. And this is an easy way to do it. Messaging could also be used in networked games. I haven’t tried it, but its a possibility.



DP

In the GameSystem class where you have



   public void sendMessage(Message message) {
      MessageHolder mh = new MessageHolder();
      mh.message = message;
   }



Doesn't this method need to add messages to the messageQueue?

You iterate through the messageQueue in update() but I don't see where you ever put messages into it. Maybe I'm looking in the wrong place.
[/quote]

umm…i haven’t looked at the code in around 7 months! So i really can’t comment on it unless i look at it again…



I’l have a look for you definetly



DP

Well after a week of not being able to work on my jME stuff due to school projects I’m finally back at it.



I’m trying to modify the GameEntity class to include a PhysicsObject.



As DarkProphet mentioned it would be best if the PhysicsObject automatically synced with the Geometry anytime you made changes to the geometry.



Unfortunately I haven’t been able to come up with a way to do this. If you manipulate the geometry by calling



Entity.spatial.setTranslation(Vector3f)



then the there’s no way that I can see of being able to tell that the Geometry was changed unless there is some method that you can call which tells you if the Geometry has been manipulated between updates.



Does anyone know of a way to find out if the state of the Geometry has changed?

I just found a limitation with the GameSystem code.



Right now you iterate through the entityList in GameSystem calling update on every Entity in the list.



There is a problem however, if you want to create an EntityAction that removes the entity from the GameSystem.



This will cause a java.util.ConcurrentModificationException because you are trying to remove an entity from a list you are currently iterating.



This limitation means you cannot have any actions which remove the entity from the GameSystem.



I can think of lots of situations where you would need this. For me personally, I create a GameEntity for each missle in my game and create an EntityAction which destroys the object after a set amount of time.



Have you encountered this problem in your use of the GameSystem and would you know of any workarounds off the top of your head?



Thank you.


My Solution

Here's my solution (not sure if it's the most elegant)


I added another HashMap in GameSystem

private HashMap entityRemovalQueue;



I changed the detachChild() method to be as follows

   public void detachChild(GameEntity entity){   
       entityRemovalQueue.put(entity.getName(), entity);
   }



So now when you call detachChild() it adds the entity you want to detach into the removal queue. In the update() method I added the following after calling any actions

      // loop through the entityRemovalQueue and remove entities waiting to be removed
      for (Iterator it = entityRemovalQueue.values().iterator(); it.hasNext();) {
          entityList.remove(((GameEntity)it.next()).getName());
      }
      entityRemovalQueue.clear();



So far it has been working for me.