看到exmaples中的例子:CollisionDetectionExample,看到效果图如下:
其实这个例子跟我们前面的绘制虚拟游戏摇杆很像,不同的就是中间有两个sprite,判断碰撞的关键语句如下:
scene.registerUpdateHandler(new IUpdateHandler() { //场景注册一个UpdateHandler,每次update都运行一次 @Override public void reset() { } @Override public void onUpdate(final float pSecondsElapsed) { if(centerRectangle.collidesWith(face)) { centerRectangle.setColor(1, 0, 0); //如果两个sprite相碰,则方块sprite变为红色 } else { centerRectangle.setColor(0, 1, 0); //如果没有相碰,则变为蓝色 } } });
另外mCamera.isRectangularShapeVisible(face)判断face是否超出摄像机的视界。
Physics中的例子都是模仿现实世界中的重力还有碰壁的反弹等物理效果,看下代表性的例子:PhysicsExample
效果如下:
实现了IAccelerometerListener重力加速度、IOnSceneTouchListener触屏事件等接口
实现物理效果的步骤如下:
1、创建物理世界
this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);
2、创建墙壁并加载到scene中
final Shape ground = new Rectangle(0, CAMERA_HEIGHT - 2, CAMERA_WIDTH, 2); final Shape roof = new Rectangle(0, 0, CAMERA_WIDTH, 2); final Shape left = new Rectangle(0, 0, 2, CAMERA_HEIGHT); final Shape right = new Rectangle(CAMERA_WIDTH - 2, 0, 2, CAMERA_HEIGHT); final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0.5f, 0.5f); //参数分别为密度,弹性,摩擦。 PhysicsFactory.createBoxBody(this.mPhysicsWorld, ground, BodyType.StaticBody, wallFixtureDef); PhysicsFactory.createBoxBody(this.mPhysicsWorld, roof, BodyType.StaticBody, wallFixtureDef); PhysicsFactory.createBoxBody(this.mPhysicsWorld, left, BodyType.StaticBody, wallFixtureDef); PhysicsFactory.createBoxBody(this.mPhysicsWorld, right, BodyType.StaticBody, wallFixtureDef); scene.getBottomLayer().addEntity(ground); scene.getBottomLayer().addEntity(roof); scene.getBottomLayer().addEntity(left); scene.getBottomLayer().addEntity(right);
3、把PhysicsWorld实例用scene.registerUpdateHandler注册到场景
scene.registerUpdateHandler(this.mPhysicsWorld);
4、当上面的物理世界创建好了以后,在外部实现IOnSceneTouchListener接口的方法,一触屏便生出一个sprite。
public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) { if(this.mPhysicsWorld != null) { if(pSceneTouchEvent.getAction() == TouchEvent.ACTION_DOWN) { this.runOnUpdateThread(new Runnable() { @Override public void run() { PhysicsExample.this.addFace(pSceneTouchEvent.getX(), pSceneTouchEvent.getY()); //备注1 } }); return true; } } return false; }
以上几步完成之后便可以完成整个的物理世界的碰撞效果了。
备注1:该例子中有四种不同的sprite碰撞墙壁,关键语句如下:
if(this.mFaceCount % 4 == 0) { face = new AnimatedSprite(pX, pY, this.mBoxFaceTextureRegion); body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF); } else if (this.mFaceCount % 4 == 1) { face = new AnimatedSprite(pX, pY, this.mCircleFaceTextureRegion); body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF); } else if (this.mFaceCount % 4 == 2) { face = new AnimatedSprite(pX, pY, this.mTriangleFaceTextureRegion); body = PhysicsExample.createTriangleBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF); } else { face = new AnimatedSprite(pX, pY, this.mHexagonFaceTextureRegion); body = PhysicsExample.createHexagonBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF); } face.animate(200); face.setUpdatePhysics(false); scene.getTopLayer().addEntity(face);
关于第一种和第二种没什么多说的,和上面创建墙壁的差不多的语法。第三种和第四种创建三角形和六边形的有点复杂,需要计算,看如下原理:
先看三角形的精灵的画法,可以先根据三角形sprite得到该sprite的宽和高,当然这里是把他的宽高去一般,为了方便计算,于是可以得到坐标:
A(0,-H), B(-W,H), C(W,H),
这里是根据android的坐标系统来定的。得到了坐标值,于是可以确定Vector2类,关键便是的到这里的vertor2.
再来看看正六边形的原理:
同样的我们可以得到W和H,根据数学知识得到BG的长度为:0.5*H,至于具体如何算,可以请教中学的数学老师,于是我们可以得到坐标:
A(0,-H), B(W,-0.5H), C(W,0.5H), D(0,H), E(-W,0,.5H), F(-W,-0.5H);
好了,我们坐标都得到了,可以定义我们的Vertor2的数组:
final Vector2[] vertices = { new Vector2(centerX, top), new Vector2(right, higher), new Vector2(right, lower), new Vector2(centerX, bottom), new Vector2(left, lower), new Vector2(left, higher) }; //这里面的点的顺序是从顶点,然后按顺时针,也就是我们上面的ABCDEF顺序
三角形也是一样的,有了vertices,我们便可以得到物理世界的不一样形状的sprite
return PhysicsFactory.createPolygonBody(pPhysicsWorld, pShape, vertices, pBodyType, pFixtureDef);
具体的创造六边形的方法:
private static Body createHexagonBody(final PhysicsWorld pPhysicsWorld, final Shape pShape, final BodyType pBodyType, final FixtureDef pFixtureDef) { /* Remember that the vertices are relative to the center-coordinates of the Shape. */ final float halfWidth = pShape.getWidthScaled() * 0.5f / PIXEL_TO_METER_RATIO_DEFAULT; final float halfHeight = pShape.getHeightScaled() * 0.5f / PIXEL_TO_METER_RATIO_DEFAULT; /* The top and bottom vertex of the hexagon are on the bottom and top of hexagon-sprite. */ final float top = -halfHeight; final float bottom = halfHeight; final float centerX = 0; /* The left and right vertices of the heaxgon are not on the edge of the hexagon-sprite, so we need to inset them a little. */ final float left = -halfWidth + 2.5f / PIXEL_TO_METER_RATIO_DEFAULT; final float right = halfWidth - 2.5f / PIXEL_TO_METER_RATIO_DEFAULT; final float higher = top + 8.25f / PIXEL_TO_METER_RATIO_DEFAULT; final float lower = bottom - 8.25f / PIXEL_TO_METER_RATIO_DEFAULT; final Vector2[] vertices = { new Vector2(centerX, top), new Vector2(right, higher), new Vector2(right, lower), new Vector2(centerX, bottom), new Vector2(left, lower), new Vector2(left, higher) }; return PhysicsFactory.createPolygonBody(pPhysicsWorld, pShape, vertices, pBodyType, pFixtureDef); }
这里面有一点开始我怎么也想不通,就是定义坐标的时候,左右为什么要加减2.5,上下为什么要加减8.25,后来看了图片才知道,纹理png图上的图案并没有全部填满整个纹理png图,上下左右都有一些边距,如下:
改图是被我放大的效果,为了方便大家看得更清楚,看到这里便一下子明白了上面那个问题,至于具体的数字为什么是那么多,肯定是跟像素有关系,有兴趣的同学可以找到这个图片,然后把它放大后慢慢的去数,就会明白啦!