1 package com.badlogic.gdx.tests.bullet; 2 3 import com.badlogic.gdx.Gdx; 4 import com.badlogic.gdx.Input.Keys; 5 import com.badlogic.gdx.graphics.Texture; 6 import com.badlogic.gdx.graphics.VertexAttributes.Usage; 7 import com.badlogic.gdx.graphics.g3d.Material; 8 import com.badlogic.gdx.graphics.g3d.Model; 9 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; 10 import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; 11 import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; 12 import com.badlogic.gdx.math.Matrix4; 13 import com.badlogic.gdx.math.Vector3; 14 import com.badlogic.gdx.physics.bullet.collision.btAxisSweep3; 15 import com.badlogic.gdx.physics.bullet.collision.btBroadphaseProxy; 16 import com.badlogic.gdx.physics.bullet.collision.btCapsuleShape; 17 import com.badlogic.gdx.physics.bullet.collision.btCollisionDispatcher; 18 import com.badlogic.gdx.physics.bullet.collision.btCollisionObject; 19 import com.badlogic.gdx.physics.bullet.collision.btConvexShape; 20 import com.badlogic.gdx.physics.bullet.collision.btDbvtBroadphase; 21 import com.badlogic.gdx.physics.bullet.collision.btDefaultCollisionConfiguration; 22 import com.badlogic.gdx.physics.bullet.collision.btGhostPairCallback; 23 import com.badlogic.gdx.physics.bullet.collision.btPairCachingGhostObject; 24 import com.badlogic.gdx.physics.bullet.dynamics.btDiscreteDynamicsWorld; 25 import com.badlogic.gdx.physics.bullet.dynamics.btDynamicsWorld; 26 import com.badlogic.gdx.physics.bullet.dynamics.btKinematicCharacterController; 27 import com.badlogic.gdx.physics.bullet.dynamics.btSequentialImpulseConstraintSolver; 28 29 public class CharacterTest extends BaseBulletTest { 30 final int BOXCOUNT_X = 5; 31 final int BOXCOUNT_Y = 5; 32 final int BOXCOUNT_Z = 1; 33 34 final float BOXOFFSET_X = -2.5f; 35 final float BOXOFFSET_Y = 0.5f; 36 final float BOXOFFSET_Z = 0f; 37 38 BulletEntity ground; 39 BulletEntity character; 40 41 btGhostPairCallback ghostPairCallback; 42 btPairCachingGhostObject ghostObject; 43 btConvexShape ghostShape; 44 btKinematicCharacterController characterController; 45 Matrix4 characterTransform; 46 Vector3 characterDirection = new Vector3(); 47 Vector3 walkDirection = new Vector3(); 48 49 @Override 50 public BulletWorld createWorld () { 51 // We create the world using an axis sweep broadphase for this test 52 btDefaultCollisionConfiguration collisionConfiguration = new btDefaultCollisionConfiguration(); 53 btCollisionDispatcher dispatcher = new btCollisionDispatcher(collisionConfiguration); 54 btAxisSweep3 sweep = new btAxisSweep3(new Vector3(-1000, -1000, -1000), new Vector3(1000, 1000, 1000)); 55 btSequentialImpulseConstraintSolver solver = new btSequentialImpulseConstraintSolver(); 56 btDiscreteDynamicsWorld collisionWorld = new btDiscreteDynamicsWorld(dispatcher, sweep, solver, collisionConfiguration); 57 ghostPairCallback = new btGhostPairCallback(); 58 sweep.getOverlappingPairCache().setInternalGhostPairCallback(ghostPairCallback); 59 return new BulletWorld(collisionConfiguration, dispatcher, sweep, solver, collisionWorld); 60 } 61 62 @Override 63 public void create () { 64 super.create(); 65 instructions = "Tap to shoot\nArrow keys to move\nR to reset\nLong press to toggle debug mode\nSwipe for next test"; 66 67 // Create a visual representation of the character (note that we don't use the physics part of BulletEntity, we'll do that manually) 68 final Texture texture = new Texture(Gdx.files.internal("data/badlogic.jpg")); 69 disposables.add(texture); 70 final Material material = new Material(TextureAttribute.createDiffuse(texture), ColorAttribute.createSpecular(1,1,1,1), FloatAttribute.createShininess(8f)); 71 final long attributes = Usage.Position | Usage.Normal | Usage.TextureCoordinates; 72 final Model capsule = modelBuilder.createCapsule(2f, 6f, 16, material, attributes); 73 disposables.add(capsule); 74 world.addConstructor("capsule", new BulletConstructor(capsule, null)); 75 character = world.add("capsule", 5f, 3f, 5f); 76 characterTransform = character.transform; // Set by reference 77 78 // Create the physics representation of the character 79 ghostObject = new btPairCachingGhostObject(); 80 ghostObject.setWorldTransform(characterTransform); 81 ghostShape = new btCapsuleShape(2f, 2f); 82 ghostObject.setCollisionShape(ghostShape); 83 ghostObject.setCollisionFlags(btCollisionObject.CollisionFlags.CF_CHARACTER_OBJECT); 84 characterController = new btKinematicCharacterController(ghostObject, ghostShape, .35f); 85 86 // And add it to the physics world 87 world.collisionWorld.addCollisionObject(ghostObject, 88 (short)btBroadphaseProxy.CollisionFilterGroups.CharacterFilter, 89 (short)(btBroadphaseProxy.CollisionFilterGroups.StaticFilter | btBroadphaseProxy.CollisionFilterGroups.DefaultFilter)); 90 ((btDiscreteDynamicsWorld)(world.collisionWorld)).addAction(characterController); 91 92 // Add the ground 93 (ground = world.add("ground", 0f, 0f, 0f)) 94 .setColor(0.25f + 0.5f * (float)Math.random(), 0.25f + 0.5f * (float)Math.random(), 0.25f + 0.5f * (float)Math.random(), 1f); 95 // Create some boxes to play with 96 for (int x = 0; x < BOXCOUNT_X; x++) { 97 for (int y = 0; y < BOXCOUNT_Y; y++) { 98 for (int z = 0; z < BOXCOUNT_Z; z++) { 99 world.add("box", BOXOFFSET_X + x, BOXOFFSET_Y + y, BOXOFFSET_Z + z) 100 .setColor(0.5f + 0.5f * (float)Math.random(), 0.5f + 0.5f * (float)Math.random(), 0.5f + 0.5f * (float)Math.random(), 1f); 101 } 102 } 103 } 104 } 105 106 @Override 107 public void update () { 108 // If the left or right key is pressed, rotate the character and update its physics update accordingly. 109 if (Gdx.input.isKeyPressed(Keys.LEFT)) { 110 characterTransform.rotate(0, 1, 0, 5f); 111 ghostObject.setWorldTransform(characterTransform); 112 } 113 if (Gdx.input.isKeyPressed(Keys.RIGHT)) { 114 characterTransform.rotate(0, 1, 0, -5f); 115 ghostObject.setWorldTransform(characterTransform); 116 } 117 // Fetch which direction the character is facing now 118 characterDirection.set(-1,0,0).rot(characterTransform).nor(); 119 // Set the walking direction accordingly (either forward or backward) 120 walkDirection.set(0,0,0); 121 if (Gdx.input.isKeyPressed(Keys.UP)) 122 walkDirection.add(characterDirection); 123 if (Gdx.input.isKeyPressed(Keys.DOWN)) 124 walkDirection.add(-characterDirection.x, -characterDirection.y, -characterDirection.z); 125 walkDirection.scl(4f * Gdx.graphics.getDeltaTime()); 126 // And update the character controller 127 characterController.setWalkDirection(walkDirection); 128 // Now we can update the world as normally 129 super.update(); 130 // And fetch the new transformation of the character (this will make the model be rendered correctly) 131 ghostObject.getWorldTransform(characterTransform); 132 } 133 134 @Override 135 protected void renderWorld () { 136 // TODO Auto-generated method stub 137 super.renderWorld(); 138 } 139 140 @Override 141 public boolean tap (float x, float y, int count, int button) { 142 shoot(x, y); 143 return true; 144 } 145 146 @Override 147 public void dispose () { 148 ((btDiscreteDynamicsWorld)(world.collisionWorld)).removeAction(characterController); 149 world.collisionWorld.removeCollisionObject(ghostObject); 150 super.dispose(); 151 characterController.dispose(); 152 ghostObject.dispose(); 153 ghostShape.dispose(); 154 ghostPairCallback.dispose(); 155 ground = null; 156 } 157 } 158