Couple physics issues with my pinball game

Hello all,

Been playing around with jme physics by creating a little pinball table.  But I've been seeing some issues I was hoping to get some recommendations on

from the experts here.


  1. When playing, if you let the ball slide to the end of the paddle, it doesn't launch off of it with much force.  Like when you stop the ball by keeping the flipper up, then let it roll to the tip.

    When hitting it mid flipper, it has good effect.  I've tried different things like messing around with mass of the ball vs flippers, and changing addForce to addTorque, etc.  But I can never seem

    to get the desired results of close to a real table.



    2.  Sometime, it seems like the ball will overlap the flipper and the ball gets stuck in it for a second, then goes off with a force not normal.  I am thinking it has to do with the flipper being

    too fast for the ball, or too much time in between updates for how fast of force I am adding to the flipper.



    3.  I was putting this together on a laptop, pretty weak system.  And when I run this same program on my home PC which is much more powerful, the results are a lot faster.  I had thought

    the physics would take into consideration FPS it it's calculation of gravity, etc, but it doesn't seem like it does.  Maybe I could be wrong, and just don't have something set up right.  But I am

    not sure how to make things run the same on all computers.  I'm getting around 170 FPS on my laptop, and much more on my home pc.  When using physics, do I need to cap my FPS at a set

    amount to get it to run the same on all PCs?



    Thanks in advance for any suggestions.  I will post the entire code below, in case it helps to run it to see what's going on.  name of file is "brian5.java".




import com.jme.app.SimpleGame;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jmex.physics.DynamicPhysicsObject;
import com.jmex.physics.PhysicsObject;
import com.jmex.physics.PhysicsWorld;
import com.jmex.physics.StaticPhysicsObject;
import com.jmex.physics.joints.HingeJoint;


public class brian5 extends SimpleGame {
   
   Vector3f axis = new Vector3f(1, 0, 0);
   float angle = .3f;
   
   Box boxFloor;
   Sphere ball;
   DynamicPhysicsObject ballPhysics;
   Box anchorLeft;
   DynamicPhysicsObject anchorLeftp;
   Box padLeft;
   DynamicPhysicsObject padLeftp;
   Box anchorRight;
   DynamicPhysicsObject anchorRightp;
   Box padRight;
   DynamicPhysicsObject padRightp;

   protected void simpleInitGame() {
      display.setTitle("Simple Test");

      cam.getLocation().y = 10;
      cam.update();
      
      KeyBindingManager kb = KeyBindingManager.getKeyBindingManager();
      kb.set("zforce", KeyInput.KEY_Z);
      kb.set("xforce", KeyInput.KEY_X);

      PhysicsWorld.create();
      PhysicsWorld.getInstance().setUpdateRate(100);
      PhysicsWorld.getInstance().setStepSize(2f / 100f);

      // boxFloor
      boxFloor = new Box("boxFloor", new Vector3f(0f, -0.5f, -20f), 10, 0.5f, 20);
      boxFloor.getLocalRotation().fromAngleAxis(angle, axis);
      rootNode.attachChild(boxFloor);
      PhysicsObject boxFloorPhysics = new StaticPhysicsObject(boxFloor);
      PhysicsWorld.getInstance().addObject(boxFloorPhysics);

      // BoxTop
      Box boxTop = new Box("boxTop", new Vector3f(0f, 2f, -41.f), 10, 2f, 1f);
      boxTop.getLocalRotation().fromAngleAxis(angle, axis);
      rootNode.attachChild(boxTop);
      PhysicsObject boxTopPhysics = new StaticPhysicsObject(boxTop);
      PhysicsWorld.getInstance().addObject(boxTopPhysics);

      // BoxLeft
      Box boxLeft = new Box("boxLeft", new Vector3f(-11f, 2f, -20f), 1, 2f, 20f);
      boxLeft.getLocalRotation().fromAngleAxis(angle, axis);
      rootNode.attachChild(boxLeft);
      PhysicsObject boxLeftPhysics = new StaticPhysicsObject(boxLeft);
      PhysicsWorld.getInstance().addObject(boxLeftPhysics);

      // BoxRight
      Box boxRight = new Box("boxRight", new Vector3f(11f, 2f, -20f), 1, 2f, 20f);
      boxRight.getLocalRotation().fromAngleAxis(angle, axis);
      rootNode.attachChild(boxRight);
      PhysicsObject boxRightPhysics = new StaticPhysicsObject(boxRight);
      PhysicsWorld.getInstance().addObject(boxRightPhysics);

      //anchorLeft
      anchorLeft = new Box("anchorLeft", new Vector3f(0f, 0f, 0f), 1f, 1.1f, 1f);
      rootNode.attachChild(anchorLeft);
      anchorLeftp = new DynamicPhysicsObject(anchorLeft, 10000f);
      PhysicsWorld.getInstance().addObject(anchorLeftp);

      //padLeft
      padLeft = new Box("padLeft", new Vector3f(4f, 0f, 0f), 4f, 1f, .1f);
      rootNode.attachChild(padLeft);
      padLeftp = new DynamicPhysicsObject(padLeft, 20f);
      PhysicsWorld.getInstance().addObject(padLeftp);

      //hingeJointLeft
      HingeJoint hingeJointLeft = new HingeJoint("hingeJointLeft", padLeftp, anchorLeftp);
      hingeJointLeft.setMaxAngleStop(1f);
      hingeJointLeft.setMinAngleStop(-.3f);
      hingeJointLeft.attach();
      hingeJointLeft.setAnchor(new Vector3f(1f, 1f, 0f));
      hingeJointLeft.setAxis(new Vector3f(0, 1, 0));

      //anchorRight
      anchorRight = new Box("anchorRight", new Vector3f(0f, 0f, 0f), 1f, 1.1f, 1f);
      rootNode.attachChild(anchorRight);
      anchorRightp = new DynamicPhysicsObject(anchorRight, 10000f);
      PhysicsWorld.getInstance().addObject(anchorRightp);
      //anchorRightp.setEnabled(false);

      //padRight
      padRight = new Box("padRight", new Vector3f(-4f, 0f, 0f), 4f, 1f, .1f);
      rootNode.attachChild(padRight);
      padRightp = new DynamicPhysicsObject(padRight, 20f);
      PhysicsWorld.getInstance().addObject(padRightp);

      //hingeJointRight
      HingeJoint hingeJointRight = new HingeJoint("hingeJointRight", padRightp, anchorRightp);
      hingeJointRight.setMaxAngleStop(.3f);
      hingeJointRight.setMinAngleStop(-1f);
      hingeJointRight.attach();
      hingeJointRight.setAnchor(new Vector3f(-1f, 1f, 0f));
      hingeJointRight.setAxis(new Vector3f(0, 1, 0));
      
      //move Left objects to left of table
      anchorLeft.getLocalTranslation().x = -9f;
      anchorLeft.getLocalTranslation().z = -1f;
      anchorLeft.getLocalTranslation().y = 1f;   
      anchorLeft.getLocalRotation().fromAngleAxis(angle, axis);
      padLeft.getLocalTranslation().x = -9f;
      padLeft.getLocalTranslation().z = 1f;      
      padLeft.getLocalTranslation().y = 1f;   
      padLeft.getLocalRotation().fromAngleAxis(angle, axis);
      anchorLeftp.syncWithGraphical();
      padLeftp.syncWithGraphical();
      
      //move Right objects to the right of table
      anchorRight.getLocalTranslation().x = 9f;
      anchorRight.getLocalTranslation().z = -1f;
      anchorRight.getLocalTranslation().y = 1f;   
      anchorRight.getLocalRotation().fromAngleAxis(angle, axis);
      padRight.getLocalTranslation().x = 9f;
      padRight.getLocalTranslation().z = -1f;      
      padRight.getLocalTranslation().y = 1f;   
      padRight.getLocalRotation().fromAngleAxis(angle, axis);
      anchorRightp.syncWithGraphical();
      padRightp.syncWithGraphical();
      
      // ball
      ball = new Sphere("ball", 30, 30, 1);
      ball.setRandomColors();
      ball.setLocalTranslation(new Vector3f(3, 2, -5));
      rootNode.attachChild(ball);
      DynamicPhysicsObject ballPhysics = new DynamicPhysicsObject(ball, .05f);
      PhysicsWorld.getInstance().addObject(ballPhysics);

      createTableObjects();
      
      //rootNode.setLightCombineMode(LightState.OFF);
      
   }

   private void createTableObjects() {
      
      //Bumper Object 1 (bo1)
      Box bo1 = new Box("bo1", new Vector3f(0f, 0f, 0f), .5f, 2f, 2f);
      bo1.setLocalTranslation(new Vector3f(0f, 6f, -10f));
      bo1.getLocalRotation().fromAngleAxis(angle, axis);
      bo1.setSolidColor(ColorRGBA.blue);
      rootNode.attachChild(bo1);
      PhysicsObject bo1p = new StaticPhysicsObject(bo1);
      PhysicsWorld.getInstance().addObject(bo1p);
      
   }

   protected void cleanup() {
      super.cleanup();
      // Before ending your application you should always clean up the PhysicsWorld.
      PhysicsWorld.getInstance().cleanup();
   }

   protected void simpleUpdate() {
      PhysicsWorld.getInstance().update(tpf);

      KeyBindingManager kb = KeyBindingManager.getKeyBindingManager();


      if (kb.isValidCommand("zforce", true)) {
         padLeftp.addForce(new Vector3f(0, 0, 500));
      } else {
         padLeftp.addForce(new Vector3f(0, 0, -500));
      }

      if (kb.isValidCommand("xforce", true)) {
         padRightp.addForce(new Vector3f(0, 0, 500));
      } else {
         padRightp.addForce(new Vector3f(0, 0, -500));
      }

      //Lock paddle to left
      anchorLeft.getLocalTranslation().x = -9f;
      anchorLeft.getLocalTranslation().z = -1f;
      anchorLeftp.syncWithGraphical();
      
      //Lock paddle to right
      anchorRight.getLocalTranslation().x = 9f;
      anchorRight.getLocalTranslation().z = -1f;
      anchorRightp.syncWithGraphical();
   
   }

   public static void main(String[] args) {
      brian5 app = new brian5();
      app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG, brian5.class
            .getClassLoader()

            .getResource("jmextest/data/images/jmephysics_logo.png"));
      app.start();
   }

}


  1. I'd suggest using setDesiredAngularVelocity (and setMaxTorque) instead of addforce and increase friction between paddle and ball
  2. your assumtions could be right -> decrease step size and increase number of steps per second accordingly
  3. the simulation should be the same on different systems as long as the timer works well. And this might actually be the problem: the lwjgl timer gets inaccurate for very high frame rates (in the hundreds)
Sometime, it seems like the ball will overlap the flipper and the ball gets stuck in it for a second, then goes off with a force not normal.  I am thinking it has to do with the flipper being
too fast for the ball, or too much time in between updates for how fast of force I am adding to the flipper.


I had this issue, I took out any code that forces movement on physics object like this:

//Lock paddle to left
anchorLeft.getLocalTranslation().x = -9f;
anchorLeft.getLocalTranslation().z = -1f;
anchorLeftp.syncWithGraphical();

//Lock paddle to right
anchorRight.getLocalTranslation().x = 9f;
anchorRight.getLocalTranslation().z = -1f;
anchorRightp.syncWithGraphical();


and like irrisor said:
I'd suggest using setDesiredAngularVelocity (and setMaxTorque) instead of addforce and increase friction between paddle and ball


now the issue for me is gone.

Thank you both for the input, I'm looking into them now.  Will let you know the results.



One follow up question about the suggestion to remove anything that locks the movement:



how then would you suggest I lock the flipper to hinge around an anchor.

It does not allow me to hinge to a StaticPhysicsObject, only Dynamic.

And when I tried just making the anchor really heavy, it would still slightly move sometimes, I guess due to the table being tilted,

and the force when the flipper flaps, and hits the ball.


BMan1010 said:

It does not allow me to hinge to a StaticPhysicsObject, only Dynamic.

Ode handles joints that are attached to only one body as joints attached to the world. I think the old jmephysics throws some NPEs if you try to do attach( dynamicObject1, null ) - have a try. For any NPE simply put an if ( object1/2 != null ) and it should be fine. (I have added support for it in the new jmephysics. - btw. maybe we should call it jME Physics 2 to get rid of that new/old wording )

read this post about attaching joint to physics world.

http://www.jmonkeyengine.com/jmeforum/index.php?topic=3007.15

how then would you suggest I lock the flipper to hinge around an anchor.

setAxis(new Vector3f(0, 0, 1)); locks movement around z axis

Thanks again.  That is exactly what I need.



irrisor, quick question.  Might you give a quick example on how to use the setAngularVelocity and setMaxTorqu for what I'm trying to do?

I'm trying to look up some examples of how to use them, but not doing so well.



p.s. I apologize, I hate having to ask folks how to write my code, but I'm pretty new to this and still learning.



I think I understand the logic, that being that instead of adding a straight force in one direction, I need to get the angularvelocity that the padle is rotating,

and apply the torqu that direction? 

I'm not following the object1/2 != null way around the NPE.  Could you elaborate?



thanks again!

There is no example for desired velocities in jmephysics except for the car stuff - which is way too complex. Simply call setDesiredAngularVelocity( 10 ) and setMaxTorque( 200 ) on your paddle joint, test what's happening and play around with the values (especially flip the sign of the value for the desired velocity to change direction, torque will most probably be required to be much larger for a flipper).



I have fixed the NPE to allow attaching a joint to the world. Simply call new HingeJoint( paddlePhysicObject, null ).



Edit: damn cvs server does not let me check in :x - will try again later . . .

… committed! phew