原文地址:http://blog.xoppa.com/using-the-libgdx-3d-physics-bullet-wrapper-part2/
推荐看原文,我就直接上代码了。完整代码原文中有github。
1 package org.forus.game.test; 2 3 import com.badlogic.gdx.ApplicationListener; 4 import com.badlogic.gdx.Gdx; 5 import com.badlogic.gdx.graphics.Color; 6 import com.badlogic.gdx.graphics.GL20; 7 import com.badlogic.gdx.graphics.PerspectiveCamera; 8 import com.badlogic.gdx.graphics.VertexAttributes.Usage; 9 import com.badlogic.gdx.graphics.g3d.*; 10 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; 11 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight; 12 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController; 13 import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; 14 import com.badlogic.gdx.math.MathUtils; 15 import com.badlogic.gdx.math.Matrix4; 16 import com.badlogic.gdx.math.Vector3; 17 import com.badlogic.gdx.physics.bullet.Bullet; 18 import com.badlogic.gdx.physics.bullet.collision.*; 19 import com.badlogic.gdx.physics.bullet.dynamics.*; 20 import com.badlogic.gdx.physics.bullet.linearmath.btMotionState; 21 import com.badlogic.gdx.utils.Array; 22 import com.badlogic.gdx.utils.ArrayMap; 23 import com.badlogic.gdx.utils.Disposable; 24 25 public class CollisionWorldTest2 implements ApplicationListener { 26 final static short GROUND_FLAG = 1<<8; 27 final static short OBJECT_FLAG = 1<<9; 28 final static short ALL_FLAG = -1; 29 30 class MyContactListener extends ContactListener { 31 /** 32 @Override 33 public boolean onContactAdded (int userValue0, int partId0, int index0, int userValue1, int partId1, int index1) { 34 // instances.get(userValue0).moving = false; 35 // instances.get(userValue1).moving = false; 36 // if (userValue0 != 0) 37 // ((ColorAttribute)instances.get(userValue0).materials.get(0).get(ColorAttribute.Diffuse)).color.set(Color.WHITE); 38 // if (userValue1 != 0) 39 // ((ColorAttribute)instances.get(userValue1).materials.get(0).get(ColorAttribute.Diffuse)).color.set(Color.WHITE); 40 if (userValue1 == 0) 41 ((ColorAttribute)instances.get(userValue0).materials.get(0).get(ColorAttribute.Diffuse)).color.set(Color.WHITE); 42 if (userValue0 == 0) 43 ((ColorAttribute)instances.get(userValue1).materials.get(0).get(ColorAttribute.Diffuse)).color.set(Color.WHITE); 44 return true; 45 }*/ 46 47 48 //Note that by default the contact callback filter will be set to zero, so overriding this method without setting the contact callback flag and filter values, will cause the callback never to be triggered. 49 @Override 50 public boolean onContactAdded (int userValue0, int partId0, int index0, boolean match0, 51 int userValue1, int partId1, int index1, boolean match1) { 52 if (match0) 53 ((ColorAttribute)instances.get(userValue0).materials.get(0).get(ColorAttribute.Diffuse)).color.set(Color.WHITE); 54 if (match1) 55 ((ColorAttribute)instances.get(userValue1).materials.get(0).get(ColorAttribute.Diffuse)).color.set(Color.WHITE); 56 return true; 57 } 58 } 59 60 static class MyMotionState extends btMotionState { 61 Matrix4 transform; 62 @Override 63 public void getWorldTransform (Matrix4 worldTrans) { 64 worldTrans.set(transform);//TODO 搞清楚什么时候调用 65 } 66 @Override 67 public void setWorldTransform (Matrix4 worldTrans) { 68 transform.set(worldTrans);//TODO 搞清楚什么时候调用 69 } 70 } 71 72 static class GameObject extends ModelInstance implements Disposable { 73 public final btRigidBody body; 74 public final MyMotionState motionState; 75 76 public GameObject(Model model, String node, btRigidBody.btRigidBodyConstructionInfo constructionInfo) { 77 super(model, node); 78 motionState = new MyMotionState(); 79 motionState.transform = transform; 80 body = new btRigidBody(constructionInfo); 81 body.setMotionState(motionState); 82 } 83 84 @Override 85 public void dispose() { 86 body.dispose(); 87 motionState.dispose(); 88 } 89 90 static class Constructor implements Disposable { 91 public final Model model; 92 public final String node; 93 public final btCollisionShape shape; 94 public final btRigidBody.btRigidBodyConstructionInfo constructionInfo; 95 private static Vector3 localInertia = new Vector3(); 96 97 public Constructor(Model model, String node, btCollisionShape shape, float mass) { //mass--the weight of the object, 单位千克, 其他单位:米,秒 98 this.model = model; 99 this.node = node;100 this.shape = shape;101 //If the mass is equal or less than zero, we simply set the local inertia also to zero. Otherwise we need to calculate the local intertia102 if (mass > 0f)103 shape.calculateLocalInertia(mass, localInertia);104 else105 localInertia.set(0, 0, 0);106 this.constructionInfo = new btRigidBody.btRigidBodyConstructionInfo(mass, null, shape, localInertia);107 }108 109 public GameObject construct() {110 return new GameObject(model, node, constructionInfo);111 }112 113 @Override114 public void dispose() {115 shape.dispose();116 constructionInfo.dispose();117 }118 }119 }120 121 PerspectiveCamera cam;122 CameraInputController camController;123 ModelBatch modelBatch;124 Environment environment;125 Model model;126 Arrayinstances;127 ArrayMap constructors;128 float spawnTimer;129 130 btCollisionConfiguration collisionConfig;131 btDispatcher dispatcher;132 MyContactListener contactListener;133 btBroadphaseInterface broadphase;134 135 btDynamicsWorld dynamicsWorld;136 btConstraintSolver constraintSolver;137 138 float angle, speed = 90f;//ground的参数139 140 @Override141 public void create () {142 Bullet.init();143 144 modelBatch = new ModelBatch();145 environment = new Environment();146 environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));147 environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));148 149 cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());150 cam.position.set(3f, 7f, 10f);151 cam.lookAt(0, 4f, 0);152 cam.near = 1f;153 cam.far = 300f;154 cam.update();155 156 camController = new CameraInputController(cam);157 Gdx.input.setInputProcessor(camController);158 159 ModelBuilder mb = new ModelBuilder();160 mb.begin();161 mb.node().id = "ground";162 mb.part("ground", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.RED)))163 .box(5f, 1f, 5f);164 mb.node().id = "sphere";165 mb.part("sphere", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.GREEN)))166 .sphere(1f, 1f, 1f, 10, 10);167 mb.node().id = "box";168 mb.part("box", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.BLUE)))169 .box(1f, 1f, 1f);170 mb.node().id = "cone";171 mb.part("cone", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.YELLOW)))172 .cone(1f, 2f, 1f, 10);173 mb.node().id = "capsule";174 mb.part("capsule", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.CYAN)))175 .capsule(0.5f, 2f, 10);176 mb.node().id = "cylinder";177 mb.part("cylinder", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal,178 new Material(ColorAttribute.createDiffuse(Color.MAGENTA))).cylinder(1f, 2f, 1f, 10);179 model = mb.end();180 181 constructors = new ArrayMap (String.class, GameObject.Constructor.class);182 constructors.put("ground", new GameObject.Constructor(model, "ground", new btBoxShape(new Vector3(2.5f, 0.5f, 2.5f)), 0f));183 constructors.put("sphere", new GameObject.Constructor(model, "sphere", new btSphereShape(0.5f), 1f));184 constructors.put("box", new GameObject.Constructor(model, "box", new btBoxShape(new Vector3(0.5f, 0.5f, 0.5f)), 1f));185 constructors.put("cone", new GameObject.Constructor(model, "cone", new btConeShape(0.5f, 2f), 1f));186 constructors.put("capsule", new GameObject.Constructor(model, "capsule", new btCapsuleShape(.5f, 1f), 1f));187 constructors.put("cylinder", new GameObject.Constructor(model, "cylinder", new btCylinderShape(new Vector3(.5f, 1f, .5f)), 1f));188 189 collisionConfig = new btDefaultCollisionConfiguration();190 dispatcher = new btCollisionDispatcher(collisionConfig);191 broadphase = new btDbvtBroadphase();192 constraintSolver = new btSequentialImpulseConstraintSolver();193 dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, constraintSolver, collisionConfig);//Discrete离散194 dynamicsWorld.setGravity(new Vector3(0, -10f, 0));195 contactListener = new MyContactListener();196 197 instances = new Array ();198 GameObject object = constructors.get("ground").construct();199 object.body.setCollisionFlags(object.body.getCollisionFlags()200 | btCollisionObject.CollisionFlags.CF_KINEMATIC_OBJECT);201 instances.add(object);202 //dynamicsWorld.addRigidBody(object.body, GROUND_FLAG, ALL_FLAG);203 dynamicsWorld.addRigidBody(object.body);204 object.body.setContactCallbackFlag(GROUND_FLAG);205 object.body.setContactCallbackFilter(0);//他和谁撞都不回调。放心,被撞的会回调的。206 object.body.setActivationState(Collision.DISABLE_DEACTIVATION);207 }208 209 public void spawn () {210 GameObject obj = constructors.values[1 + MathUtils.random(constructors.size - 2)].construct();211 obj.transform.setFromEulerAngles(MathUtils.random(360f), MathUtils.random(360f), MathUtils.random(360f));212 obj.transform.trn(MathUtils.random(-2.5f, 2.5f), 9f, MathUtils.random(-2.5f, 2.5f));213 //obj.body.setWorldTransform(obj.transform);214 obj.body.proceedToTransform(obj.transform);//有了motionState后215 obj.body.setUserValue(instances.size);216 obj.body.setCollisionFlags(obj.body.getCollisionFlags() | btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);217 instances.add(obj);218 //dynamicsWorld.addRigidBody(obj.body, OBJECT_FLAG, GROUND_FLAG);//曾用过addCollisionObject, makes sure that for example gravity is correctly applied to each object219 dynamicsWorld.addRigidBody(obj.body);220 obj.body.setContactCallbackFlag(OBJECT_FLAG);221 //碰撞回调过滤要区别于碰撞过滤!!!前者在于,撞完之后调不调用,后者是能不能有碰撞计算222 obj.body.setContactCallbackFilter(GROUND_FLAG);//和Ground撞时回调223 }224 225 @Override226 public void render () {227 final float delta = Math.min(1f / 30f, Gdx.graphics.getDeltaTime());228 229 angle = (angle + delta * speed) % 360f;230 instances.get(0).transform.setTranslation(0, MathUtils.sinDeg(angle) * 2.5f, 0f);231 //instances.get(0).body.setWorldTransform(instances.get(0).transform);//有motionstate了这里多余232 //instances.get(0).body.activate();//设置了object.body.setActivationState(Collision.DISABLE_DEACTIVATION);233 234 235 // The discrete dynamics world uses a fixed time step.236 // This basically means that it will always use the same delta value to perform calculations.237 // This fixed delta value is supplied as the third argument of stepSimulation.238 // If the actual delta value (the first argument) is greater than the desired fixed delta value, then the calculation will be done multiple times.239 // The maximum number of times that this will be done (the maximum number of sub-steps) is specified by the second argument.240 dynamicsWorld.stepSimulation(delta, 5, 1f/60f);241 /*有motionState了,注释掉242 for (GameObject obj : instances)243 obj.body.getWorldTransform(obj.transform);//看起来像把body的设置给obj244 245 dynamicsWorld.performDiscreteCollisionDetection();246 */247 248 if ((spawnTimer -= delta) < 0) {249 spawn();250 spawnTimer = 1.5f;251 }252 253 camController.update();254 255 Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1.f);256 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);257 258 modelBatch.begin(cam);259 modelBatch.render(instances, environment);260 modelBatch.end();261 }262 263 @Override264 public void dispose () {265 for (GameObject obj : instances)266 obj.dispose();267 instances.clear();268 269 for (GameObject.Constructor ctor : constructors.values())270 ctor.dispose();271 constructors.clear();272 273 dynamicsWorld.dispose();274 constraintSolver.dispose();275 broadphase.dispose();276 dispatcher.dispose();277 collisionConfig.dispose();278 279 contactListener.dispose();280 281 modelBatch.dispose();282 model.dispose();283 }284 285 @Override286 public void pause () {287 }288 289 @Override290 public void resume () {291 }292 293 @Override294 public void resize (int width, int height) {295 }296 }