1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package jme3test.helloworld; 34 35 import com.jme3.app.SimpleApplication; 36 import com.jme3.asset.plugins.ZipLocator; 37 import com.jme3.bullet.BulletAppState; 38 import com.jme3.bullet.collision.shapes.CapsuleCollisionShape; 39 import com.jme3.bullet.collision.shapes.CollisionShape; 40 import com.jme3.bullet.control.CharacterControl; 41 import com.jme3.bullet.control.RigidBodyControl; 42 import com.jme3.bullet.util.CollisionShapeFactory; 43 import com.jme3.input.KeyInput; 44 import com.jme3.input.controls.ActionListener; 45 import com.jme3.input.controls.KeyTrigger; 46 import com.jme3.light.AmbientLight; 47 import com.jme3.light.DirectionalLight; 48 import com.jme3.math.ColorRGBA; 49 import com.jme3.math.Vector3f; 50 import com.jme3.scene.Node; 51 import com.jme3.scene.Spatial; 52 53 /** 54 * Example 9 - How to make walls and floors solid. 55 * This collision code uses Physics and a custom Action Listener. 56 * @author normen, with edits by Zathras 57 */ 58 public class HelloCollision extends SimpleApplication 59 implements ActionListener { 60 61 private Spatial sceneModel; 62 private BulletAppState bulletAppState; 63 private RigidBodyControl landscape; 64 private CharacterControl player; 65 private Vector3f walkDirection = new Vector3f(); 66 private boolean left = false, right = false, up = false, down = false; 67 68 public static void main(String[] args) { 69 HelloCollision app = new HelloCollision(); 70 app.start(); 71 } 72 73 public void simpleInitApp() { 74 /** Set up Physics */ 75 bulletAppState = new BulletAppState(); 76 stateManager.attach(bulletAppState); 77 //bulletAppState.getPhysicsSpace().enableDebug(assetManager); 78 79 // We re-use the flyby camera for rotation, while positioning is handled by physics 80 viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f)); 81 flyCam.setMoveSpeed(100); 82 setUpKeys(); 83 setUpLight(); 84 85 // We load the scene from the zip file and adjust its size. 86 assetManager.registerLocator("town.zip", ZipLocator.class.getName()); 87 sceneModel = assetManager.loadModel("main.scene"); 88 sceneModel.setLocalScale(2f); 89 90 // We set up collision detection for the scene by creating a 91 // compound collision shape and a static RigidBodyControl with mass zero. 92 CollisionShape sceneShape = 93 CollisionShapeFactory.createMeshShape((Node) sceneModel); 94 landscape = new RigidBodyControl(sceneShape, 0); 95 sceneModel.addControl(landscape); 96 97 // We set up collision detection for the player by creating 98 // a capsule collision shape and a CharacterControl. 99 // The CharacterControl offers extra settings for 100 // size, stepheight, jumping, falling, and gravity. 101 // We also put the player in its starting position. 102 CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1); 103 player = new CharacterControl(capsuleShape, 0.05f); 104 player.setJumpSpeed(20); 105 player.setFallSpeed(30); 106 player.setGravity(30); 107 player.setPhysicsLocation(new Vector3f(0, 10, 0)); 108 109 // We attach the scene and the player to the rootnode and the physics space, 110 // to make them appear in the game world. 111 rootNode.attachChild(sceneModel); 112 bulletAppState.getPhysicsSpace().add(landscape); 113 bulletAppState.getPhysicsSpace().add(player); 114 } 115 116 private void setUpLight() { 117 // We add light so we see the scene 118 AmbientLight al = new AmbientLight(); 119 al.setColor(ColorRGBA.White.mult(1.3f)); 120 rootNode.addLight(al); 121 122 DirectionalLight dl = new DirectionalLight(); 123 dl.setColor(ColorRGBA.White); 124 dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal()); 125 rootNode.addLight(dl); 126 } 127 128 /** We over-write some navigational key mappings here, so we can 129 * add physics-controlled walking and jumping: */ 130 private void setUpKeys() { 131 inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A)); 132 inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D)); 133 inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W)); 134 inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S)); 135 inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE)); 136 inputManager.addListener(this, "Left"); 137 inputManager.addListener(this, "Right"); 138 inputManager.addListener(this, "Up"); 139 inputManager.addListener(this, "Down"); 140 inputManager.addListener(this, "Jump"); 141 } 142 143 /** These are our custom actions triggered by key presses. 144 * We do not walk yet, we just keep track of the direction the user pressed. */ 145 public void onAction(String binding, boolean value, float tpf) { 146 if (binding.equals("Left")) { 147 if (value) { left = true; } else { left = false; } 148 } else if (binding.equals("Right")) { 149 if (value) { right = true; } else { right = false; } 150 } else if (binding.equals("Up")) { 151 if (value) { up = true; } else { up = false; } 152 } else if (binding.equals("Down")) { 153 if (value) { down = true; } else { down = false; } 154 } else if (binding.equals("Jump")) { 155 player.jump(); 156 } 157 } 158 159 /** 160 * This is the main event loop--walking happens here. 161 * We check in which direction the player is walking by interpreting 162 * the camera direction forward (camDir) and to the side (camLeft). 163 * The setWalkDirection() command is what lets a physics-controlled player walk. 164 * We also make sure here that the camera moves with player. 165 */ 166 @Override 167 public void simpleUpdate(float tpf) { 168 Vector3f camDir = cam.getDirection().clone().multLocal(0.6f); 169 Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f); 170 walkDirection.set(0, 0, 0); 171 if (left) { walkDirection.addLocal(camLeft); } 172 if (right) { walkDirection.addLocal(camLeft.negate()); } 173 if (up) { walkDirection.addLocal(camDir); } 174 if (down) { walkDirection.addLocal(camDir.negate()); } 175 player.setWalkDirection(walkDirection); 176 cam.setLocation(player.getPhysicsLocation()); 177 } 178 } 179