Lesson 8: Collision Detection

Return to the home page

The goal:

Click for full size. Hover over a picture for more details.

Click here to download the code. (If the page opens in the broswer, you can right click on link and click "Save Link As", or right click on the page and click "Save As")

            
        // most applications will want to extend the SimpleGame class
        import com.jme.app.SimpleGame;
        
        // a vector containing three float numbers;
        //   used to store positions and translations
        import com.jme.math.Vector3f;
        import com.jme.math.Quaternion;
        import com.jme.math.Matrix3f;
        
        // geometric shapes used in program
        import com.jme.scene.shape.Box;
        import com.jme.scene.shape.Sphere;
        
        // use predefined colors and create custom colors
        import com.jme.renderer.ColorRGBA;
        
        // work with basic materials
        import com.jme.scene.state.MaterialState;
        
        // process user input
        import com.jme.input.InputHandler;
        import com.jme.input.KeyInput;
        import com.jme.input.KeyBindingManager;
        import com.jme.input.FirstPersonHandler;
        
        // an internal vertex of the scenegraph tree;
        //  use to group together objects,
        //  apply render settings, which affects all child vertices
        import com.jme.scene.Node;
        import com.jme.scene.Spatial;
        import com.jme.scene.Geometry;
        
        // methods to change render settings
        import com.jme.renderer.Renderer;
        
        // collision
        import com.jme.bounding.*;
        import com.jme.intersection.*;
        
        /**
         *   Collide
         */
        public class CollideDemo extends SimpleGame
        {
           // geometric shapes to be rendered
           Sphere mySphere;
           Sphere leftSphere;
           Sphere rightSphere;
           Box floorBox;
        
           // used in collision detection
           BoundingCollisionResults myCollisionList = new BoundingCollisionResults();
           CollisionData myCollision;
           Spatial  targetHit;
           int numCollisions = 0;
           Vector3f lastPosition = new Vector3f();
        
           public static void main(String[] args)
           {
               // create an instance of this program
               CollideDemo app = new CollideDemo();
        
               // before the main program starts, display the graphics configuration window
               //   where the user can choose among screen resolution, color depth, and full screen options
               app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        
               // The start() method runs simpleInitGame(),
               //   then starts the game loop: simpleUpdate(), then simpleRender()
               // To terminate game loop, call finish()
               app.start();
           }
        
           protected void simpleInitGame()
           {
               // make the standard cursor visible.
               org.lwjgl.input.Mouse.setGrabbed(false);
               // SimpleGame class defaults mouse behavior to rotate view; here we disable this.
               ((FirstPersonHandler)input).getMouseLookHandler().setEnabled(false);
        
               // create a material to apply to geometric objects, use objects' default color,
               //  apply to front and back, and assign rendering to root node
               MaterialState customMaterial = display.getRenderer().createMaterialState();
               customMaterial.setColorMaterial(MaterialState.ColorMaterial.AmbientAndDiffuse);
               customMaterial.setMaterialFace(MaterialState.MaterialFace.FrontAndBack);
               rootNode.setRenderState(customMaterial);
        
               mySphere = new Sphere("mySphere", new Vector3f(0,0,0), 30, 30, 1 );
               mySphere.setSolidColor( ColorRGBA.yellow );
               mySphere.setModelBound(new BoundingSphere());
               mySphere.updateModelBound();
               rootNode.attachChild( mySphere );
        
               leftSphere = new Sphere("lefty", new Vector3f(-3,0,0), 30, 30, 1 );
               leftSphere.setSolidColor( ColorRGBA.red );
               leftSphere.setModelBound(new BoundingSphere());
               leftSphere.updateModelBound();
               rootNode.attachChild( leftSphere );
        
               rightSphere = new Sphere("righty", new Vector3f(3,0,0), 30, 30, 1 );
               rightSphere.setSolidColor( ColorRGBA.blue );
               rightSphere.setModelBound(new BoundingSphere());
               rightSphere.updateModelBound();
               rootNode.attachChild( rightSphere );
        
               floorBox = new Box("wally", new Vector3f(0,-3,0), 1, 1, 1);
               floorBox.setSolidColor( ColorRGBA.magenta );
               floorBox.setModelBound(new OrientedBoundingBox());
               floorBox.updateModelBound();
               rootNode.attachChild( floorBox );
        
               buildInput();
        
           }
        
           protected void simpleUpdate()
           {
               try {   Thread.sleep(1000/120);   } // force decrease in frames per second
               catch (Exception e) {             }
        
               processInput();
               processCollisions();
           }
        
           protected void simpleRender()
           {
               // nothing to add
           }
        
           // create input bindings
           public void buildInput()
           {
               // remove all pre-existing key assignments
               KeyBindingManager.getKeyBindingManager().removeAll();
        
               // exit the program
               KeyBindingManager.getKeyBindingManager().set( "exit", KeyInput.KEY_ESCAPE );
               KeyBindingManager.getKeyBindingManager().set( "toggle_wire", KeyInput.KEY_F1 );
               KeyBindingManager.getKeyBindingManager().set( "toggle_bounds", KeyInput.KEY_F2 );
        
               KeyBindingManager.getKeyBindingManager().set( "nodeTranslateX+", KeyInput.KEY_D );
               KeyBindingManager.getKeyBindingManager().set( "nodeTranslateX-", KeyInput.KEY_A );
               KeyBindingManager.getKeyBindingManager().set( "nodeTranslateY+", KeyInput.KEY_W );
               KeyBindingManager.getKeyBindingManager().set( "nodeTranslateY-", KeyInput.KEY_S );
           }
        
           // process input entered since last update.
           public void processInput()
           {
               // defining speed in terms of tpf (time per frame) helps keep speed constant
               //  across varying framerates
               float speed = 2*tpf;
        
               // used for collision with solid walls
               Quaternion oldRotation = mySphere.getLocalRotation().clone();
               Vector3f oldPosition = mySphere.getLocalTranslation().clone();
        
               if (KeyBindingManager.getKeyBindingManager().isValidCommand("nodeTranslateX+", true))
                   mySphere.getLocalTranslation().addLocal(speed,0,0);
               // alternatively, we could write
               // myBox.getLocalTranslation().addLocal( Vector3f.UNIT_X.mult(speed) );
               if (KeyBindingManager.getKeyBindingManager().isValidCommand("nodeTranslateX-", true))
                   mySphere.getLocalTranslation().addLocal(-speed,0,0);
               if (KeyBindingManager.getKeyBindingManager().isValidCommand("nodeTranslateY+", true))
                   mySphere.getLocalTranslation().addLocal(0,speed,0);
               if (KeyBindingManager.getKeyBindingManager().isValidCommand("nodeTranslateY-", true))
                   mySphere.getLocalTranslation().addLocal(0,-speed,0);
        
               // COLLISION DETECTION - WALLS
        
               // next line needed, for otherwise we will be using old data for collision detection,
               //  which is bad
               mySphere.updateGeometricState(0,true);
               // clear list before reusing it!
               myCollisionList.clear();
               // store all collisions of mySphere with descendents of rootNode, store in myCollisionList
               mySphere.findCollisions( rootNode, myCollisionList );
               // determine how many there are
               numCollisions = myCollisionList.getNumber();
               if (numCollisions > 0)
               {
                   // when there are collisions, handle them in a loop.
                   for ( int i = 0; i < numCollisions; i++ )
                   {
                       myCollision   = myCollisionList.getCollisionData(i);
                       targetHit     = myCollision.getTargetMesh();
                       //If the sphere intersects with our floorbox named "wally", we want it to stop
                       if (targetHit.getName().equals("wally"))
                       {
                           //Therefore, when it tries to move inside of wally (causing the collision), put its translation back to where it was before it moved.
                           mySphere.setLocalTranslation( oldPosition );
                           mySphere.setLocalRotation( oldRotation );
                           mySphere.updateGeometricState(0,true);
                       }
                   }
               }
           }
        
           public void processCollisions()
           {
               // COLLISION DETECTION - SPECIAL OBJECTS
        
               // check if two particular bounding volumes intersect. 
               if ( mySphere.getWorldBound().intersects( leftSphere.getWorldBound() ) )
                   mySphere.setSolidColor( ColorRGBA.orange );
               else if ( mySphere.getWorldBound().intersects( rightSphere.getWorldBound() ) )
                   mySphere.setSolidColor( ColorRGBA.green );
        
               else
                   mySphere.setSolidColor( ColorRGBA.yellow );
        
               if ( leftSphere.getWorldBound().intersects( mySphere.getWorldBound() ) )
                   leftSphere.setRandomColors();
               else
                   leftSphere.setSolidColor( ColorRGBA.red );
        
               if ( rightSphere.getWorldBound().intersects( mySphere.getWorldBound() ) )
                   rightSphere.setRandomColors();
               else
                   rightSphere.setSolidColor( ColorRGBA.blue );
        
           }
        }
     

Return to the home page