Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jmonkeyengine movement too fast for collision detection

I am making a Java game using JMonkeyEngine and I have followed the tutorial on their site to shoot balls (bullets) at a wall. I get how everything works, but when I increase the velocity of the bullet, it passes straight through the wall.

Now I know the reason why this is happening (because the bullet moves too fast per frame for collision to register). I also know how to solve for this and I have found the following method on their website (JMonkeyEngine).

setCcdMotionThreshold(0f)

But can anyone tell me how this is implemented or where I can find a sample of someone using this?

like image 962
heyred Avatar asked May 02 '13 10:05

heyred


1 Answers

Introduction
Continuous collision detection (ccd) deals with the bullet vs paper problem. Where a fast moving object is one side of a thin object at one timestep and the other side at the next time step leading to the physics engine believing that no collision has occurred at all. Continuous collision detection on the other hand doesn't use time steps at all. It creates a swept volume over the entire period of the timestep and finds if that swept volume collides with anything. This is both expensive and inaccurate (because a simple circular collision shape is used for this rather than the full collision shape).

Usage
Continuous collision detection is set on a per object basic, you can have objects that use ccd and objects that don't in the same scene at the same time, ccd is set on a physics object as shown below

RigidBodyControl physicsObject = new RigidBodyControl(mass);
physicsObject.setCcdMotionThreshold(expectedWidthOfThinObject);
physicsObject.setCcdSweptSphereRadius(radiusOfSphereThatWillFullyContainObject);
  • You want to set expectedWidthOfThinObject as high you can get away with; remember ccd is expensive and inaccurate. Setting it as zero will turn ccd off
  • You want to set radiusOfSphereThatWillFullyContainObject as small as possible while fully containing the object

Full example
The following source code will show the difference between using Continuous Collision Detection and using the standard collision detection. It fires two sets of balls, one fast and one slow and turns ccd on and off at 5 second intervals. The slow balls always collide with the paper, the fast ones only when ccd is turned on.

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.font.BitmapText;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;

public class BulletTest extends SimpleApplication {

  public static void main(String args[]) {
    BulletTest app = new BulletTest();
    app.start();
  }

  /** Prepare the Physics Application State (jBullet) */
  private BulletAppState bulletAppState;

  /** Prepare Materials */
  Material wall_mat;
  Material slow_mat;
  Material fast_mat;
  Material floor_mat;


  private RigidBodyControl    brick_phy;
  private static final Box    box;
  private static final Sphere sphere;
  private RigidBodyControl    floor_phy;
  private static final Box    floor;

  /** dimensions used for wall */
  private static final float brickLength = 2f;
  private static final float brickWidth  = 0.015f;
  private static final float brickHeight = 1f;

  static {
    /** Initialize the cannon ball geometry */
    sphere = new Sphere(32, 32, 0.1f, true, false);
    /** Initialize the brick geometry */
    box = new Box(brickWidth, brickHeight, brickLength);
    /** Initialize the floor geometry */
    floor = new Box(10f, 0.1f, 5f);

  }

  @Override
  public void simpleInitApp() {
    /** Set up Physics Game */
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);
    //bulletAppState.getPhysicsSpace().enableDebug(assetManager);

    /** Configure cam to look at scene */
    cam.setLocation(new Vector3f(0, 4f, 6f));
    cam.lookAt(new Vector3f(2, 2, 0), Vector3f.UNIT_Y);

    /** Initialize the scene, materials, and physics space */
    initMaterials();
    initWall();
    initFloor();
    setUpHUDText();
  }

  public static final float FIREPERIOD=0.5f;
  double fireTimer=0;

  public static final float SWEEPPERIOD=5f;
  double sweepTimer=0;
  boolean continuouslySweeping=true;


    @Override
    public void simpleUpdate(float tpf) {
        fireTimer+=tpf;
        sweepTimer+=tpf;

        if (sweepTimer>SWEEPPERIOD){
            sweepTimer=0;
            continuouslySweeping=!continuouslySweeping;
        }

        hudText.setText("ContinouslySweeping=" + continuouslySweeping + "(" + (int)(SWEEPPERIOD-sweepTimer) + ")" );

        if (fireTimer>FIREPERIOD){
            fireTimer=0;
            makeCannonBall(new Vector3f(-4,3,0),new Vector3f(6,4,0),slow_mat,continuouslySweeping); //slow arcing ball
            makeCannonBall(new Vector3f(-4,3,-0.5f),new Vector3f(10,1,0),fast_mat,continuouslySweeping); //fast straight ball
        }
    }

    public BitmapText hudText;

    private void setUpHUDText(){
        hudText = new BitmapText(guiFont, false);          
        hudText.setSize(guiFont.getCharSet().getRenderedSize());      // font size
        hudText.setColor(ColorRGBA.White);                             // font color
        hudText.setText("ContinouslySweeping=true");             // the text
        hudText.setLocalTranslation(300, hudText.getLineHeight(), 0); // position
        guiNode.attachChild(hudText);
    }

  /** Initialize the materials used in this scene. */
  public void initMaterials() {
    wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    wall_mat.setColor("Color", ColorRGBA.Blue);

    fast_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    fast_mat.setColor("Color", ColorRGBA.Red);

    slow_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    slow_mat.setColor("Color", ColorRGBA.Green);

    floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    floor_mat.setColor("Color", ColorRGBA.Gray);
  }

  /** Make a solid floor and add it to the scene. */
  public void initFloor() {
    Geometry floor_geo = new Geometry("Floor", floor);
    floor_geo.setMaterial(floor_mat);
    floor_geo.setLocalTranslation(0, -0.1f, 0);
    this.rootNode.attachChild(floor_geo);
    /* Make the floor physical with mass 0.0f! */
    floor_phy = new RigidBodyControl(0.0f);
    floor_geo.addControl(floor_phy);
    bulletAppState.getPhysicsSpace().add(floor_phy);
  }

  /** This loop builds a wall out of individual bricks. */
  public void initWall() {

        Vector3f location=new Vector3f(2,2,0);
        Geometry brick_geo = new Geometry("brick", box);
        brick_geo.setMaterial(wall_mat);
        rootNode.attachChild(brick_geo);
        /** Position the brick geometry  */
        brick_geo.setLocalTranslation(location);

        //paper thin objects will fall down, mass 0 clamps it in position
        brick_phy = new RigidBodyControl(0); 

        /** Add physical brick to physics space. */
        brick_geo.addControl(brick_phy);
        bulletAppState.getPhysicsSpace().add(brick_phy);
  }


  public void makeCannonBall(Vector3f startPoint, Vector3f initialVelocity, Material material, boolean continuouslySwept) {
        /** Create a cannon ball geometry and attach to scene graph. */
        Geometry ball_geo = new Geometry("cannon ball", sphere);
        ball_geo.setMaterial(material);
        rootNode.attachChild(ball_geo);
        /** Position the cannon ball  */
        ball_geo.setLocalTranslation(startPoint);
        /** Make the ball physcial with a mass > 0.0f */
        RigidBodyControl ball_phy = new RigidBodyControl(1f);
        /** Add physical ball to physics space. */
        ball_geo.addControl(ball_phy);
        bulletAppState.getPhysicsSpace().add(ball_phy);
        /** Accelerate the physcial ball to shoot it. */
        ball_phy.setLinearVelocity(initialVelocity);

        if (continuouslySwept){
            ball_phy.setCcdMotionThreshold(0.015f);
            ball_phy.setCcdSweptSphereRadius(0.01f);
        }

  }


}

With continuous detection on both sets of balls bounce of as expected (balls entering from the top left):
enter image description here

With continuous detection off the fast set of balls (red) passes through the paper as if it wasn't there (and very occasionally a slow one (green) does too):
enter image description here

NB: this code is loosely based on the hello physics code with added functionality from advanced physics

like image 90
Richard Tingle Avatar answered Oct 27 '22 13:10

Richard Tingle