英文原文地址:http://blog.xoppa.com/using-the-libgdx-3d-physics-bullet-wrapper-part1/
强烈推荐看英文原版教程,通俗易懂全面精确。
============================
我先上代码:
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; 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.Vector3; 15 import com.badlogic.gdx.physics.bullet.Bullet; 16 import com.badlogic.gdx.physics.bullet.collision.*; 17 import com.badlogic.gdx.utils.Array; 18 19 /** 20 * Created by HanHongmin on 14-8-22. 21 */ 22 public class BulletTest implements ApplicationListener { 23 PerspectiveCamera cam;//3D视角 24 CameraInputController camController;//视角控制器 25 ModelBatch modelBatch;//3D模型批渲染器 26 Array<ModelInstance> instances;//3D实例集合 27 Environment environment;//环境属性,光线等 28 29 Model model;//模型 30 ModelInstance ground;//长方体实例 31 ModelInstance ball;//球形实例 32 33 boolean collision;//是否碰撞的标记 34 btCollisionShape groundShape;//对应长方体实例的碰撞形状 35 btCollisionShape ballShape;//对应球形实例的碰撞形状 36 btCollisionObject groundObject;// 37 btCollisionObject ballObject;// 38 39 btCollisionConfiguration collisionConfig;// 40 btDispatcher dispatcher; 41 42 @Override 43 public void create () { 44 Bullet.init();//使用Bullet前必须先初始化 45 modelBatch = new ModelBatch(); 46 47 environment = new Environment(); 48 environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));//环境光 49 environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));//直线光 50 51 cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 52 cam.position.set(3f, 7f, 10f); 53 cam.lookAt(0, 4f, 0); 54 cam.update(); 55 56 camController = new CameraInputController(cam); 57 Gdx.input.setInputProcessor(camController); 58 59 instances = new Array<ModelInstance>(); 60 61 //代码方式构造模型 62 ModelBuilder mb = new ModelBuilder(); 63 mb.begin(); 64 mb.node().id = "ground"; 65 mb.part("box", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.RED))) 66 .box(5f, 1f, 5f); 67 mb.node().id = "ball"; 68 mb.part("sphere", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.GREEN))) 69 .sphere(1f, 1f, 1f, 10, 10); 70 model = mb.end(); 71 //实例化模型 72 ground = new ModelInstance(model, "ground"); 73 ball = new ModelInstance(model, "ball"); 74 ball.transform.setToTranslation(0, 9f, 0); 75 76 instances = new Array<ModelInstance>(); 77 instances.add(ground); 78 instances.add(ball); 79 80 //创建碰撞检测形状 81 ballShape = new btSphereShape(0.5f); 82 groundShape = new btBoxShape(new Vector3(2.5f, 0.5f, 2.5f)); 83 84 //创建碰撞检测体 85 groundObject = new btCollisionObject(); 86 groundObject.setCollisionShape(groundShape); 87 groundObject.setWorldTransform(ground.transform); 88 89 ballObject = new btCollisionObject(); 90 ballObject.setCollisionShape(ballShape); 91 ballObject.setWorldTransform(ball.transform); 92 93 collisionConfig = new btDefaultCollisionConfiguration(); 94 dispatcher = new btCollisionDispatcher(collisionConfig); 95 } 96 97 @Override 98 public void render () { 99 final float delta = Math.min(1f/30f, Gdx.graphics.getDeltaTime()); 100 101 if (!collision) {//尚未碰撞 102 ball.transform.translate(0f, -delta, 0f);//球向下移动 103 ballObject.setWorldTransform(ball.transform);//同时移动碰撞体 104 collision = checkCollision();//碰撞检测 105 } 106 107 camController.update(); 108 109 Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1.f); 110 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 111 112 modelBatch.begin(cam); 113 modelBatch.render(instances, environment); 114 modelBatch.end(); 115 } 116 boolean checkCollision() { 117 CollisionObjectWrapper co0 = new CollisionObjectWrapper(ballObject); 118 CollisionObjectWrapper co1 = new CollisionObjectWrapper(groundObject); 119 120 btCollisionAlgorithmConstructionInfo ci = new btCollisionAlgorithmConstructionInfo();//碰撞算法信息 121 ci.setDispatcher1(dispatcher); 122 btCollisionAlgorithm algorithm = new btSphereBoxCollisionAlgorithm(null, ci, co0.wrapper, co1.wrapper, false);//碰撞算法 123 124 btDispatcherInfo info = new btDispatcherInfo(); 125 btManifoldResult result = new btManifoldResult(co0.wrapper, co1.wrapper); 126 127 algorithm.processCollision(co0.wrapper, co1.wrapper, info, result); 128 129 boolean r = result.getPersistentManifold().getNumContacts() > 0; 130 131 result.dispose(); 132 info.dispose(); 133 algorithm.dispose(); 134 ci.dispose(); 135 co1.dispose(); 136 co0.dispose(); 137 138 return r; 139 } 140 141 @Override 142 public void dispose () { 143 groundObject.dispose(); 144 groundShape.dispose(); 145 146 ballObject.dispose(); 147 ballShape.dispose(); 148 149 dispatcher.dispose(); 150 collisionConfig.dispose(); 151 152 modelBatch.dispose(); 153 model.dispose(); 154 } 155 156 @Override public void pause () {} 157 @Override public void resume () {} 158 @Override public void resize (int width, int height) {} 159 }
Bullet是什么?百度百科:http://baike.baidu.com/view/449998.htm?fr=aladdin
Libgdx扩展库对其提供了封装,也就是说我们可以通过gdx-bullet使用java代码就可以使用Bullet的C++啦。
如果在创建工程的过程中没有勾选bullet,那么把gdx-bullet的库加进来就可以啦。
我是使用IDEA+gradle, 直接在工程的父build.gradle增加库依赖:
core:
compile "com.badlogicgames.gdx:gdx-bullet:$gdxVersion"
ios:
compile "com.badlogicgames.gdx:gdx-bullet:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-bullet-platform:$gdxVersion:natives-ios"
android:
compile "com.badlogicgames.gdx:gdx-bullet:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-bullet-platform:$gdxVersion:natives-armeabi"
natives "com.badlogicgames.gdx:gdx-bullet-platform:$gdxVersion:natives-armeabi-v7a"
natives "com.badlogicgames.gdx:gdx-bullet-platform:$gdxVersion:natives-x86"
desktop:
compile "com.badlogicgames.gdx:gdx-bullet-platform:$gdxVersion:natives-desktop"
html: 貌似不支持
代码中一目了然,每一个3D物体都有一个碰撞形状和碰撞体与之对应(注意构造碰撞形状的时候参数与3D模型的不同)。
在碰撞检测的时候,用CollisionObjectWrapper包装,构造碰撞算法,计算碰撞......
我们使用的Java是可以自己管理释放内存的,但是这里实际的计算是java调用的C++代码,so
Bullet相关的都需要dispose。
===============================
btDispatcher貌似能够通过具体碰撞形状找到合适的算法,就像上面代码我们用到的
btSphereBoxCollisionAlgorithm
在看一下改进的代码:
boolean checkCollision(btCollisionObject obj0, btCollisionObject obj1) { CollisionObjectWrapper co0 = new CollisionObjectWrapper(obj0); CollisionObjectWrapper co1 = new CollisionObjectWrapper(obj1); btCollisionAlgorithm algorithm = dispatcher.findAlgorithm(co0.wrapper, co1.wrapper);//看上去像是能够通过具体形状找到合适的碰撞算法 btDispatcherInfo info = new btDispatcherInfo(); btManifoldResult result = new btManifoldResult(co0.wrapper, co1.wrapper); algorithm.processCollision(co0.wrapper, co1.wrapper, info, result); boolean r = result.getPersistentManifold().getNumContacts() > 0; dispatcher.freeCollisionAlgorithm(algorithm.getCPointer()); result.dispose(); info.dispose(); co1.dispose(); co0.dispose(); return r; }
这样我们只需传入两个btCollisionObject参数就可以做碰撞检测了,而无需关心他们具体是什么形状。
现在我们试试更多的形状,首先我们封装一下:
static class GameObject extends ModelInstance implements Disposable { public final btCollisionObject body; public boolean moving; public GameObject(Model model, String node, btCollisionShape shape) { super(model, node); body = new btCollisionObject(); body.setCollisionShape(shape); } @Override public void dispose () { body.dispose(); } static class Constructor implements Disposable { public final Model model; public final String node; public final btCollisionShape shape; public Constructor(Model model, String node, btCollisionShape shape) { this.model = model; this.node = node; this.shape = shape; } public GameObject construct() { return new GameObject(model, node, shape); } @Override public void dispose () { shape.dispose(); } } }
据说这是工厂模式。
还是直接上全代码吧:
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; 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.Vector3; 16 import com.badlogic.gdx.physics.bullet.Bullet; 17 import com.badlogic.gdx.physics.bullet.collision.*; 18 import com.badlogic.gdx.utils.Array; 19 import com.badlogic.gdx.utils.ArrayMap; 20 import com.badlogic.gdx.utils.Disposable; 21 22 /** 23 * Created by HanHongmin on 14-8-22. 24 */ 25 public class BulletTest implements ApplicationListener { 26 PerspectiveCamera cam;//3D视角 27 CameraInputController camController;//视角控制器 28 ModelBatch modelBatch;//3D模型批渲染器 29 //Array<ModelInstance> instances;//3D实例集合 30 Environment environment;//环境属性,光线等 31 32 Model model;//模型 33 34 float spawnTimer;//用于控制生成随机形状碰撞体 35 36 37 btCollisionConfiguration collisionConfig;// 38 btDispatcher dispatcher; 39 40 Array<GameObject> instances; 41 ArrayMap<String, GameObject.Constructor> constructors; 42 43 @Override 44 public void create () { 45 Bullet.init();//使用Bullet前必须先初始化 46 modelBatch = new ModelBatch(); 47 48 environment = new Environment(); 49 environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));//环境光 50 environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));//直线光 51 52 cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 53 cam.position.set(3f, 7f, 10f); 54 cam.lookAt(0, 4f, 0); 55 cam.update(); 56 57 camController = new CameraInputController(cam); 58 Gdx.input.setInputProcessor(camController); 59 60 ModelBuilder mb = new ModelBuilder(); 61 mb.begin(); 62 mb.node().id = "ground"; 63 mb.part("ground", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.RED))) 64 .box(5f, 1f, 5f); 65 mb.node().id = "sphere"; 66 mb.part("sphere", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.GREEN))) 67 .sphere(1f, 1f, 1f, 10, 10); 68 mb.node().id = "box"; 69 mb.part("box", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.BLUE))) 70 .box(1f, 1f, 1f); 71 mb.node().id = "cone"; 72 mb.part("cone", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.YELLOW))) 73 .cone(1f, 2f, 1f, 10); 74 mb.node().id = "capsule"; 75 mb.part("capsule", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.CYAN))) 76 .capsule(0.5f, 2f, 10); 77 mb.node().id = "cylinder"; 78 mb.part("cylinder", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.MAGENTA))) 79 .cylinder(1f, 2f, 1f, 10); 80 model = mb.end(); 81 82 constructors = new ArrayMap<String, GameObject.Constructor>(String.class, GameObject.Constructor.class); 83 constructors.put("ground", new GameObject.Constructor(model, "ground", new btBoxShape(new Vector3(2.5f, 0.5f, 2.5f)))); 84 constructors.put("sphere", new GameObject.Constructor(model, "sphere", new btSphereShape(0.5f))); 85 constructors.put("box", new GameObject.Constructor(model, "box", new btBoxShape(new Vector3(0.5f, 0.5f, 0.5f)))); 86 constructors.put("cone", new GameObject.Constructor(model, "cone", new btConeShape(0.5f, 2f))); 87 constructors.put("capsule", new GameObject.Constructor(model, "capsule", new btCapsuleShape(.5f, 1f))); 88 constructors.put("cylinder", new GameObject.Constructor(model, "cylinder", new btCylinderShape(new Vector3(.5f, 1f, .5f)))); 89 90 instances = new Array<GameObject>(); 91 instances.add(constructors.get("ground").construct()); 92 93 collisionConfig = new btDefaultCollisionConfiguration(); 94 dispatcher = new btCollisionDispatcher(collisionConfig); 95 } 96 97 @Override 98 public void render () { 99 100 final float delta = Math.min(1f/30f, Gdx.graphics.getDeltaTime()); 101 102 for (GameObject obj : instances) { 103 if (obj.moving) { 104 obj.transform.trn(0f, -delta, 0f); 105 obj.body.setWorldTransform(obj.transform); 106 if (checkCollision(obj.body, instances.get(0).body))//get(0)是ground 107 obj.moving = false; 108 } 109 } 110 111 if ((spawnTimer -= delta) < 0) { 112 spawn(); 113 spawnTimer = 1.5f;//每1.5s生成一个 114 } 115 camController.update(); 116 117 Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1.f); 118 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 119 120 modelBatch.begin(cam); 121 modelBatch.render(instances, environment); 122 modelBatch.end(); 123 } 124 125 public void spawn() { 126 GameObject obj = constructors.values[1+ MathUtils.random(constructors.size - 2)].construct();//-2是除去ground 127 obj.moving = true; 128 obj.transform.setFromEulerAngles(MathUtils.random(360f), MathUtils.random(360f), MathUtils.random(360f));//旋转? 129 obj.transform.trn(MathUtils.random(-2.5f, 2.5f), 9f, MathUtils.random(-2.5f, 2.5f));//设置位置,区别于translate,trn可影响rotate参数,待测试 130 obj.body.setWorldTransform(obj.transform); 131 instances.add(obj); 132 } 133 134 boolean checkCollision(btCollisionObject obj0, btCollisionObject obj1) { 135 CollisionObjectWrapper co0 = new CollisionObjectWrapper(obj0); 136 CollisionObjectWrapper co1 = new CollisionObjectWrapper(obj1); 137 btCollisionAlgorithm algorithm = dispatcher.findAlgorithm(co0.wrapper, co1.wrapper);//看上去像是能够通过具体形状找到合适的碰撞算法 138 btDispatcherInfo info = new btDispatcherInfo(); 139 btManifoldResult result = new btManifoldResult(co0.wrapper, co1.wrapper); 140 algorithm.processCollision(co0.wrapper, co1.wrapper, info, result); 141 142 boolean r = result.getPersistentManifold().getNumContacts() > 0; 143 dispatcher.freeCollisionAlgorithm(algorithm.getCPointer()); 144 result.dispose(); 145 info.dispose(); 146 co1.dispose(); 147 co0.dispose(); 148 149 return r; 150 } 151 152 @Override 153 public void dispose () { 154 155 for (GameObject obj : instances) 156 obj.dispose(); 157 instances.clear(); 158 159 for (GameObject.Constructor ctor : constructors.values()) 160 ctor.dispose(); 161 constructors.clear(); 162 163 dispatcher.dispose(); 164 collisionConfig.dispose(); 165 166 modelBatch.dispose(); 167 model.dispose(); 168 } 169 170 @Override public void pause () {} 171 @Override public void resume () {} 172 @Override public void resize (int width, int height) {} 173 174 static class GameObject extends ModelInstance implements Disposable { 175 public final btCollisionObject body; 176 public boolean moving; 177 public GameObject(Model model, String node, btCollisionShape shape) { 178 super(model, node); 179 body = new btCollisionObject(); 180 body.setCollisionShape(shape); 181 } 182 183 @Override 184 public void dispose () { 185 body.dispose(); 186 } 187 static class Constructor implements Disposable { 188 public final Model model; 189 public final String node; 190 public final btCollisionShape shape; 191 public Constructor(Model model, String node, btCollisionShape shape) {//据说是工厂模式 192 this.model = model; 193 this.node = node; 194 this.shape = shape; 195 } 196 197 public GameObject construct() { 198 return new GameObject(model, node, shape); 199 } 200 201 @Override 202 public void dispose () { 203 shape.dispose(); 204 } 205 } 206 } 207 }
待续...
ContactListener 接触监听
将碰撞逻辑从渲染中分离除去
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; 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.Vector3; 16 import com.badlogic.gdx.physics.bullet.Bullet; 17 import com.badlogic.gdx.physics.bullet.collision.*; 18 import com.badlogic.gdx.utils.Array; 19 import com.badlogic.gdx.utils.ArrayMap; 20 import com.badlogic.gdx.utils.Disposable; 21 22 /** 23 * Created by HanHongmin on 14-8-22. 24 */ 25 public class BulletTest implements ApplicationListener { 26 PerspectiveCamera cam;//3D视角 27 CameraInputController camController;//视角控制器 28 ModelBatch modelBatch;//3D模型批渲染器 29 //Array<ModelInstance> instances;//3D实例集合 30 Environment environment;//环境属性,光线等 31 32 Model model;//模型 33 34 float spawnTimer;//用于控制生成随机形状碰撞体 35 36 37 btCollisionConfiguration collisionConfig;// 38 btDispatcher dispatcher; 39 40 Array<GameObject> instances; 41 ArrayMap<String, GameObject.Constructor> constructors; 42 43 MyContactListener contactListener; 44 45 @Override 46 public void create () { 47 Bullet.init();//使用Bullet前必须先初始化 48 modelBatch = new ModelBatch(); 49 50 environment = new Environment(); 51 environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));//环境光 52 environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));//直线光 53 54 cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 55 cam.position.set(3f, 7f, 10f); 56 cam.lookAt(0, 4f, 0); 57 cam.update(); 58 59 camController = new CameraInputController(cam); 60 Gdx.input.setInputProcessor(camController); 61 62 ModelBuilder mb = new ModelBuilder(); 63 mb.begin(); 64 mb.node().id = "ground"; 65 mb.part("ground", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.RED))) 66 .box(5f, 1f, 5f); 67 mb.node().id = "sphere"; 68 mb.part("sphere", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.GREEN))) 69 .sphere(1f, 1f, 1f, 10, 10); 70 mb.node().id = "box"; 71 mb.part("box", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.BLUE))) 72 .box(1f, 1f, 1f); 73 mb.node().id = "cone"; 74 mb.part("cone", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.YELLOW))) 75 .cone(1f, 2f, 1f, 10); 76 mb.node().id = "capsule"; 77 mb.part("capsule", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.CYAN))) 78 .capsule(0.5f, 2f, 10); 79 mb.node().id = "cylinder"; 80 mb.part("cylinder", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal, new Material(ColorAttribute.createDiffuse(Color.MAGENTA))) 81 .cylinder(1f, 2f, 1f, 10); 82 model = mb.end(); 83 84 constructors = new ArrayMap<String, GameObject.Constructor>(String.class, GameObject.Constructor.class); 85 constructors.put("ground", new GameObject.Constructor(model, "ground", new btBoxShape(new Vector3(2.5f, 0.5f, 2.5f)))); 86 constructors.put("sphere", new GameObject.Constructor(model, "sphere", new btSphereShape(0.5f))); 87 constructors.put("box", new GameObject.Constructor(model, "box", new btBoxShape(new Vector3(0.5f, 0.5f, 0.5f)))); 88 constructors.put("cone", new GameObject.Constructor(model, "cone", new btConeShape(0.5f, 2f))); 89 constructors.put("capsule", new GameObject.Constructor(model, "capsule", new btCapsuleShape(.5f, 1f))); 90 constructors.put("cylinder", new GameObject.Constructor(model, "cylinder", new btCylinderShape(new Vector3(.5f, 1f, .5f)))); 91 92 instances = new Array<GameObject>(); 93 instances.add(constructors.get("ground").construct()); 94 95 collisionConfig = new btDefaultCollisionConfiguration(); 96 dispatcher = new btCollisionDispatcher(collisionConfig); 97 98 contactListener = new MyContactListener(); 99 } 100 101 @Override 102 public void render () { 103 104 final float delta = Math.min(1f/30f, Gdx.graphics.getDeltaTime()); 105 106 for (GameObject obj : instances) { 107 if (obj.moving) { 108 obj.transform.trn(0f, -delta, 0f); 109 obj.body.setWorldTransform(obj.transform); 110 //if (checkCollision(obj.body, instances.get(0).body))//get(0)是ground 111 //obj.moving = false; 112 checkCollision(obj.body, instances.get(0).body); 113 } 114 } 115 116 117 if ((spawnTimer -= delta) < 0) { 118 spawn(); 119 spawnTimer = 1.5f;//每1.5s生成一个 120 } 121 camController.update(); 122 123 Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1.f); 124 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 125 126 modelBatch.begin(cam); 127 modelBatch.render(instances, environment); 128 modelBatch.end(); 129 } 130 131 public void spawn() { 132 GameObject obj = constructors.values[1+ MathUtils.random(constructors.size - 2)].construct();//-2是除去ground 133 obj.moving = true; 134 obj.transform.setFromEulerAngles(MathUtils.random(360f), MathUtils.random(360f), MathUtils.random(360f));//旋转? 135 obj.transform.trn(MathUtils.random(-2.5f, 2.5f), 9f, MathUtils.random(-2.5f, 2.5f));//设置位置,区别于translate,trn可影响rotate参数,待测试 136 obj.body.setWorldTransform(obj.transform); 137 138 obj.body.setUserValue(instances.size); 139 obj.body.setCollisionFlags(obj.body.getCollisionFlags() | btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); 140 141 instances.add(obj); 142 } 143 144 boolean checkCollision(btCollisionObject obj0, btCollisionObject obj1) { 145 CollisionObjectWrapper co0 = new CollisionObjectWrapper(obj0); 146 CollisionObjectWrapper co1 = new CollisionObjectWrapper(obj1); 147 btCollisionAlgorithm algorithm = dispatcher.findAlgorithm(co0.wrapper, co1.wrapper);//看上去像是能够通过具体形状找到合适的碰撞算法 148 btDispatcherInfo info = new btDispatcherInfo(); 149 btManifoldResult result = new btManifoldResult(co0.wrapper, co1.wrapper); 150 algorithm.processCollision(co0.wrapper, co1.wrapper, info, result); 151 152 boolean r = result.getPersistentManifold().getNumContacts() > 0; 153 dispatcher.freeCollisionAlgorithm(algorithm.getCPointer()); 154 result.dispose(); 155 info.dispose(); 156 co1.dispose(); 157 co0.dispose(); 158 159 return r; 160 } 161 162 @Override 163 public void dispose () { 164 165 for (GameObject obj : instances) 166 obj.dispose(); 167 instances.clear(); 168 169 for (GameObject.Constructor ctor : constructors.values()) 170 ctor.dispose(); 171 constructors.clear(); 172 173 dispatcher.dispose(); 174 collisionConfig.dispose(); 175 176 modelBatch.dispose(); 177 model.dispose(); 178 contactListener.dispose(); 179 } 180 181 @Override public void pause () {} 182 @Override public void resume () {} 183 @Override public void resize (int width, int height) {} 184 185 static class GameObject extends ModelInstance implements Disposable { 186 public final btCollisionObject body; 187 public boolean moving; 188 public GameObject(Model model, String node, btCollisionShape shape) { 189 super(model, node); 190 body = new btCollisionObject(); 191 body.setCollisionShape(shape); 192 } 193 194 @Override 195 public void dispose () { 196 body.dispose(); 197 } 198 static class Constructor implements Disposable { 199 public final Model model; 200 public final String node; 201 public final btCollisionShape shape; 202 public Constructor(Model model, String node, btCollisionShape shape) {//据说是工厂模式 203 this.model = model; 204 this.node = node; 205 this.shape = shape; 206 } 207 208 public GameObject construct() { 209 return new GameObject(model, node, shape); 210 } 211 212 @Override 213 public void dispose () { 214 shape.dispose(); 215 } 216 } 217 } 218 219 class MyContactListener extends ContactListener { 220 @Override 221 public boolean onContactAdded (btManifoldPoint cp, btCollisionObjectWrapper colObj0Wrap, int partId0, int index0, 222 btCollisionObjectWrapper colObj1Wrap, int partId1, int index1) { 223 instances.get(colObj0Wrap.getCollisionObject().getUserValue()).moving = false; 224 instances.get(colObj1Wrap.getCollisionObject().getUserValue()).moving = false; 225 return true; 226 } 227 } 228 }
注意要设置CF_CUSTOM_MATERIAL_CALLBACK。
碰撞逻辑,我们只是单纯的把moving标记改了一下,目前很简单,但是很显然我们已经把逻辑分清楚了。什么逻辑就要写到什么地方,这很重要。
这个监听貌似实例化出来就可以用了。可能是因为它里面实质上是C++的原理吧。
优化:
1. 我们现在不需要btManifoldPoint,所以我们不需要btCollisionObjectWrapper包装,可以改为
class MyContactListener extends ContactListener { @Override public boolean onContactAdded (btCollisionObject colObj0, int partId0, int index0, btCollisionObject colObj1, int partId1, int index1) { instances.get(colObj0.getUserValue()).moving = false; instances.get(colObj1.getUserValue()).moving = false; return true; } }
2. 实际上我们具体在碰撞逻辑上只用到了user value,onContactAdded方法也可以做到的。
class MyContactListener extends ContactListener { @Override public boolean onContactAdded (int userValue0, int partId0, int index0, int userValue1, int partId1, int index1) { instances.get(userValue0).moving = false; instances.get(userValue1).moving = false; return true; } }
据说这样,C++在回调的时候会省很多事,可能不需要再找java object了吧。