FireRate

I am finishing off my weapon system. This system has a firerate, in shots pr second.



The way I am doing it now is I count the time a key is pressed, eg each frametime(tpf) where a fire key is pressed, adds to a total fireTimePool.



My weapon controller then looks if fire is true and firePool is positive and begins firing in intervals. Eg 100 shots a second means a shot pr 0.6 sec. Each shots deducts time from firepool and when pool is empty no shots will be fired.



I havent gotten this to work, but I have a feeling that I can do this in a better way. But because I dont completely understand controllers I have no idea on how to proceed.



So any ideas you migth have will be appriciated



Thanks Middy

Basically a controller is a class where you can define game logic that you want to get executed every frame (or acutally every call of the updateGeometricState method of the Spatial the controller is attached to).



You define your logic in the Controller.update(float) method, where the float argument is the time since the last frame.



Sounds like you want your weapon to fire at a steady rate, not in bursts. If that’s the case you might want to take a look at the BallThrower class that is used in the physics tests, paying special attention to the update and isReloaded methods.



/*
 * 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 jmextest.physics;

import com.jme.bounding.BoundingSphere;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.MaterialState;
import com.jme.system.DisplaySystem;
import com.jmex.physics.DynamicPhysicsObject;
import com.jmex.physics.PhysicsWorld;

/**
 * <p>
 * A little controller which makes the space key throw green balls out from
 * the camera.
 * </p>
 *
 * <p>
 * Usage should be something like this:
 * <code>rootNode.addController(new BallThrower(rootNode, 10, 2, 1));</code>
 * </p>
 *
 * <p>
 * This will add a BallThrower to the rootNode, with 10 balls which has a
 * radius of 2 and a mass of 1.
 * </p>
 *
 * <p>
 * <b>Note: since this class creates physics objects you would need to have set
 * up the physics world before creating it.</b>
 * </p>
 *
 * @author Per Thulin
 */
public class BallThrower extends Controller {

   private static final long serialVersionUID = 1L;
   
   // The root of the scene tree.
   private Node rootNode;
   
   // The balls we'll throw.
   private DynamicPhysicsObject[] balls;
   
   // Index of the current selected ball.
   private int ballIndex;
   
   // The amount of seconds it takes to reload.
   private float reloadTime;
   
   // The time elapsed since last shot.
   private float elapsedTime;
   
   // The force in which the balls will be thrown.
   private float shootForce;
   
   // Used to flatline memory usage.
   private Vector3f forceVec;
   
   /**
    * Creates a new <code>BallThrower</code> with the passed properties.
    *
    * @param rootNode The root of the scene tree.
    * @param numOfBalls The desired amount of balls.
    * @param ballRadius The radius of the balls.
    * @param ballMass The mass of the balls.
    */
   public BallThrower(Node rootNode, int numOfBalls, float ballRadius,
         float ballMass) {
      this.rootNode = rootNode;
      KeyBindingManager.getKeyBindingManager().set("BALLTHROWER_SHOOT",
            KeyInput.KEY_SPACE);
      
      shootForce = 10000;
      reloadTime = 0.1f;
      ballIndex = 0;
      
      // We should be ready to shoot.
      elapsedTime = reloadTime;
      
      balls = new DynamicPhysicsObject[numOfBalls];
      Sphere spheres[] = new Sphere[numOfBalls];
      
      // Create a material state with a nice green color, which we
      // will apply to the balls.
      MaterialState material = DisplaySystem.getDisplaySystem().
         getRenderer().createMaterialState();
      material.setEmissive(new ColorRGBA(0f,.2f,0f,1));
      
      for (int i = 0; i < balls.length; i++) {
         // Create the jME Geometry.
         spheres[i] = new Sphere("BallThrower ammo #" + i, 20, 20, ballRadius);
         spheres[i].setModelBound(new BoundingSphere());
         spheres[i].updateModelBound();
         
         spheres[i].setRenderState(material);
         
         // Even though we disable this physics object later down, it
         // will still cause problems with other objects if it spawns
         // in their path. Therefore we move it to a location which we
         // hope noone else sits on ;)
         // TODO: Find a better solution!
         spheres[i].setLocalTranslation(new Vector3f(10000, 10000, 10000));
         
         // Create the physics object from it, and add it to the scene.
         balls[i] = new DynamicPhysicsObject(spheres[i], ballMass);
         rootNode.attachChild(spheres[i]);
         PhysicsWorld.getInstance().addObject(balls[i]);
         
         // Disable and hide it.
         balls[i].setEnabled(false);
         balls[i].getSpatial().setForceCull(true);
      }
   }
   
   /**
    * Will fire a shot if the shoot command is active and we are finished
    * reloading.
    *
    * @see Controller#update(float)
    */
   public void update(float tpf) {
      elapsedTime += tpf;
      // Shoot if the shoot command is active and we are finished reloading.
      if (KeyBindingManager.getKeyBindingManager().
            isValidCommand("BALLTHROWER_SHOOT")) {
         if (isReloaded()) shoot();
      }
   }
   
   /**
    * Shoots a ball in the direction of the camera.
    */
   private void shoot() {
      // Get a reference to the camera.
      Camera cam = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
      
      // Move to the next ball index.
      ballIndex++;
      if (ballIndex >= balls.length) ballIndex = 0;
      
      // Enable and show it in case it wasn't before.
      balls[ballIndex].setEnabled(true);
      balls[ballIndex].getSpatial().setForceCull(false);
      
      // Set the balls position to the cameras.
      balls[ballIndex].getSpatial().getLocalTranslation().set(cam.getLocation());
      balls[ballIndex].syncWithGraphical(true);
      
      // Shoot it in the direction of the camera with the selected force.
      balls[ballIndex].addForce(cam.getDirection().mult(shootForce, forceVec));
      
      // Reset the timer.
      elapsedTime = 0;
   }
   
   /**
    * Returns true if the reload time has run out.
    *
    * @return
    */
   private boolean isReloaded() {
      return (elapsedTime > reloadTime);
   }
   
   /**
    * Sets the force in which the balls are thrown. Default is 10000.
    *
    * @param shootForce The calibre :)
    */
   public void setShootForce(float shootForce) {
      this.shootForce = shootForce;
   }
   
   /**
    * Gets the force in which the balls are thrown. Default is 10000.
    *
    * @return The calibre :)
    */
   public float getShootForce() {
      return shootForce;
   }
   
   /**
    * Sets how many milliseconds the reload should take. Default is 0.1.
    *
    * @param reloadTime The time it should take to reload.
    */
   public void setReloadTime(float reloadTime) {
      this.reloadTime = reloadTime;
   }
   
   /**
    * Gets how many milliseconds the reload should take. Default is 0.1.
    *
    * @return The time it takes to reload.
    */
   public float getReloadTime() {
      return reloadTime;
   }
   
   /**
    * Sets the fire key. Default is space.
    *
    * @param key Should be one of the constants in com.jme.input.KeyInput.
    */
   public void setTriggerKey(int key) {
      KeyBindingManager.getKeyBindingManager().remove("BALLTHROWER_SHOOT");
      KeyBindingManager.getKeyBindingManager().set("BALLTHROWER_SHOOT",key);
   }
}