Box2D是一款用于2D游戏的物理引擎。在Box2D物理世界里,创建出的每个物体都更接近于真实世界中的物体,让游戏中的物体运动起来更加真实可信,让游戏世界看起来更具交互性。
Android平台常见的十几款游戏引擎中,例如:Rokon、AndEngine、libgdx等物理引擎都封装了Box2D物理引擎,可见Box2D在物理引擎中占据了多重要的位置。
Box2D在很多平台都有对应的版本:Flash版本、Iphone版本、Java版本(JBox2D)等等,由于Android语言采用的是Java,所以这里介绍的对应Box2D平台也是Java平台,称为JBox2D,对应的版本为JBox2d 2.1
要使用JBox2d首先对几个概念进行说明一下:
World 这是box2d当中抽象出来的存在引力,坐标范围的一个模拟的世界
Body 刚体,存在于World当中的刚体,我们可以通过BodyDef来设置他得属性。在JBOX2D当中我们基本上不需要去关心刚体碰撞所产生的效果,因为该引擎会根据你刚体的属性来帮你做这些处理。比如说你创建的物体会根据当前世界的引力自动下坠,如果两个钢铁相撞会弹开等等。
BodyDef 用来设置刚体的属性,包括密度density,摩擦力friction,弹性restitution等。
Shape 用来描述Body的形状,像PolygonDef 多边体 CircleDef 球体
现在我们进入正题 如何使用jbox2d创建一个的游戏世界
创建一个android项目 并引入jbox2d jar 包 jar 包怎么引用 不用说了吧,哇哈哈 jbox2d下载地址 http://code.google.com/p/jbox2d/downloads/list?can=1&q=
/**
* 游戏世界
* @author mahaile
*
*/
public class WorldSurfaceView extends SurfaceView implements SurfaceHolder.Callback{
final static int FPS=30; //设置帧数
MyThread myThread; //ui线程
SurfaceHolder surfaceHolder;
Canvas canvas;
Paint paint;
private final float RATE=10.0f; //物理世界与屏幕环境缩放比列
World world; //游戏世界对象
float timeStep = 1f / 60f;; //模拟的频率
// 迭代值,迭代越大模拟越精确,但性能越低
int velocityIterations = 10;
int positionIterations = 8;
public WorldSurfaceView(Context context) {
super(context);
surfaceHolder=this.getHolder();
surfaceHolder.addCallback(this); //添加回调
Vec2 vect=new Vec2(0f,10.0f); //向量 用来标示当前世界的重力方向,第一个参数为水平方向,负数为左,正数为右。第二个参数表示垂直方向
world=new World(vect,true); //初始化游戏世界 这里注意一点 jbox2d 2.1 以后没有 aabb游戏范围 对象了
}
public void onDraw(Canvas canvas){
Log.d("888","11111111111111111111");
canvas.drawColor(Color.WHITE); //清屏
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
myThread=new MyThread();
myThread.flag=true;
myThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
myThread.flag=false;
}
public class MyThread extends Thread{
public boolean flag;
public void run(){
long lastTime=SystemClock.uptimeMillis();
Canvas canvas;
while(flag){
long firstTime=SystemClock.uptimeMillis()-lastTime;
if(firstTime>FPS){
canvas=surfaceHolder.lockCanvas();
try{
Logic();
onDraw(canvas);
}catch(Exception ex){
ex.printStackTrace();
}finally{
surfaceHolder.unlockCanvasAndPost(canvas);
}
}else{
try {
Thread.sleep(Math.max(2,FPS-firstTime));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public void Logic() {
// --开始模拟物理世界--->>
world.step(timeStep,velocityIterations, positionIterations);
}
}
上面的 代码 怎么这么简单呢? 呵呵,这只是最简单一个 游戏世界了,我们没有添加如何游戏组件,所以当我们运行游戏 屏幕上是什么都没有的, 那我们如果给游戏世界添加几个游戏组件呢 会怎么样, box2d 又是怎么工作的呢 我们看下面的代码
package yxqz.com;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.SystemClock;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* 游戏世界 并且添加一个刚体
* @author mahaile
*
*/
public class WorldBodySurfaceView extends SurfaceView implements SurfaceHolder.Callback{
final static int FPS=30; //设置帧数
MyThread myThread; //ui线程
SurfaceHolder surfaceHolder;
Canvas canvas;
Paint paint;
int screenW,screenH;
private final float RATE=10.0f; //物理世界与屏幕环境缩放比列
World world; //游戏世界对象
float timeStep = 1f / 60f;; //模拟的频率
// 迭代值,迭代越大模拟越精确,但性能越低
int velocityIterations = 10;
int positionIterations = 8;
public WorldBodySurfaceView(Context context) {
super(context);
surfaceHolder=this.getHolder();
surfaceHolder.addCallback(this); //添加回调
Vec2 vect=new Vec2(0f,10.0f); //向量 用来标示当前世界的重力方向,第一个参数为水平方向,负数为左,正数为右。第二个参数表示垂直方向
world=new World(vect,true); //初始化游戏世界 这里注意一点 jbox2d 2.1 以后没有 aabb游戏范围 对象了
//设置画笔
paint=new Paint();
paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
paint.setColor(Color.BLUE);
}
public void onDraw(Canvas canvas){
//Log.d("888","11111111111111111111");
canvas.drawColor(Color.WHITE); //清屏
//Body body=world.getBodyList();
//canvas.drawRect(r, paint)
Body body=world.getBodyList();
for(int i=0;i<world.getBodyCount();i++){
BodyBox bodyBox=(BodyBox)body.m_userData;
bodyBox.onDraw(canvas, paint);
body=body.m_next;
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
screenW=this.getWidth();
screenH=this.getHeight();
createPolygon(5, this.getHeight() - 10, this.getWidth() - 10, 2,0.5f, true);
/**创建6个方形,isStatic设置为false,即在物理世界中是动态,收外力作用影响 */
for(int i=0;i<6;i++)
{
createPolygon(screenW-150-30*i,50,20,20,Float.parseFloat("0."+i),false);
}
myThread=new MyThread();
myThread.flag=true;
myThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
myThread.flag=false;
}
public class MyThread extends Thread{
public boolean flag;
public void run(){
long lastTime=SystemClock.uptimeMillis();
Canvas canvas;
while(flag){
long firstTime=SystemClock.uptimeMillis()-lastTime;
if(firstTime>FPS){
//Log.d("**", "-- "+firstTime +" firstTime is :"+firstTime +" lastTime is :"+lastTime);
lastTime+=FPS;
canvas=surfaceHolder.lockCanvas();
try{
Logic();
onDraw(canvas);
}catch(Exception ex){
ex.printStackTrace();
}finally{
surfaceHolder.unlockCanvasAndPost(canvas);
}
}else{
try {
//Log.d("88","FPS-firstTime is :"+(FPS-firstTime)+" firstTime is :"+firstTime +" lastTime is :"+lastTime);
Thread.sleep(Math.max(2,FPS-firstTime));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public void Logic() {
// --开始模拟物理世界--->>
world.step(timeStep,velocityIterations, positionIterations);
/**遍历物理世界中的body,将物理世界仿真出的值反馈给屏幕,
* 改变bird和rect的参数
* */
Body body = world.getBodyList();
for (int i = 1; i < world.getBodyCount(); i++) {
if ((body.m_userData) instanceof BodyBox) {
BodyBox rect = (BodyBox) (body.m_userData);
rect.setX(body.getPosition().x * RATE - (rect.getW()/2));
rect.setY(body.getPosition().y * RATE - (rect.getH()/2));
//rect.setAngle((float)(body.getAngle()*180/Math.PI));
}else // body.m_userData==null时,将body销毁,表示被击毁
{
world.destroyBody(body);
}
body = body.m_next;
}
}
/**
* 创建圆型
* @param x
* @param y
* @param width
* @param height
* @param isStatic 是否为动态
* @return body
*/
public Body createCircle(float x,float y,float r,boolean isStatic){
CircleShape shape=new CircleShape(); //创建一个圆形对象
shape.m_radius=r/RATE; //设置圆形的半径 设置半径需要把屏幕的大小参数转换成世界大小的参数
FixtureDef fDef=new FixtureDef(); //设置物体属性对象 游戏世界会根据此对象 创建刚体
if(isStatic){
fDef.density=0; //设置物品的密度 当密度为0 时 物品不会受世界重力影响
}else{
fDef.density=1; //设置物品的密度 当密度不为0 时 物品会受世界重力影响
}
fDef.friction=1.0f; //设置物品的摩擦力 0~1
fDef.restitution=0.5f; //设置物体的弹力 值越大 弹性就越强 值范围为 0~1
fDef.shape=shape;
BodyDef bodyDef=new BodyDef();//创建一个刚体的属性 对象 比如 动态,位置,旋转角度等
bodyDef.type=isStatic?BodyType.STATIC:BodyType.DYNAMIC;
bodyDef.position.set(x/RATE, y/RATE);
//创建刚体
Body body=world.createBody(bodyDef); //根据刚体属性对象在游戏世界中添加刚体
body.createFixture(fDef); //设置刚体的物理属性
return body;
}
/**
* 创建正方形
* @param x
* @param y
* @param width
* @param height
* @param isStatic 是否为动态
* @return body
*/
public Body createPolygon(float x,float y,float width,float height,float restitution,boolean isStatic){
PolygonShape shape=new PolygonShape(); //创建一个圆形对象
shape.setAsBox(width/2/RATE, height/2/RATE); //设置圆形的半径 设置半径需要把屏幕的大小参数转换成世界大小的参数
FixtureDef fDef=new FixtureDef(); //设置物体属性对象 游戏世界会根据此对象 创建刚体
if(isStatic){
fDef.density=0; //设置物品的密度 当密度为0 时 物品不会受世界重力影响
}else{
fDef.density=1; //设置物品的密度 当密度不为0 时 物品会受世界重力影响
}
fDef.friction=0.0f; //设置物品的摩擦力 0~1
fDef.restitution=restitution; //设置物体的弹力 值越大 弹性就越强 值范围为 0~1
fDef.shape=shape;
BodyDef bodyDef=new BodyDef();//创建一个刚体的属性 对象 比如 动态,位置,旋转角度等
bodyDef.type=isStatic?BodyType.STATIC:BodyType.DYNAMIC;
bodyDef.position.set((x+width/2)/RATE,(y+height/2)/RATE);
//创建刚体
Body body=world.createBody(bodyDef); //根据刚体属性对象在游戏世界中添加刚体
body.createFixture(fDef); //设置刚体的物理属性
body.m_userData=new BodyBox(x,y,width,height);
return body;
}
}
箱子实体类
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
public class BodyBox {
public float x,y,w,h;
public void setType(int type) {
this.type = type;
}
public int type=0; //0 为地板
public BodyBox(float x,float y,float w,float h){
this.x=x;
this.y=y;
this.w=w;
this.h=h;
}
public void onDraw(Canvas canvas,Paint paint){
canvas.drawRect(new RectF(x,y,x+w,y+h), paint);
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getW() {
return w;
}
public void setW(float w) {
this.w = w;
}
public float getH() {
return h;
}
public void setH(float h) {
this.h = h;
}
public int getType() {
return type;
}
}
项目代码: http://download.csdn.net/detail/ma_haile/4067733
下一章我们学习 jbox2d如何检查碰撞的