汽车引擎是汽车的心脏,其决定了汽车的性能和稳定性,是人们在购车时相当关注的。而游戏中的物理引擎就如汽车的引擎一样,占据了非常重要的位置。一款好的物理引擎可以非常真实地模拟现实世界,使得游戏更加逼真,提供更好的娱乐体验。

一、JBox2D简介

JBox2D是开源物理引擎Box2D的Java版本,可以直接用于Android。由于JBox2D的图形渲染使用的是Processing库,因此在Android平台上使用JBox2D时,图形渲染工作只能自行开发。该引擎能够根据开发人员设定的参数,如重力、密度、摩擦系数和弹性系数等,自动地进行2D刚体物理运动的全方位模拟。

二、示例

1.小球弹跳进阶版

在第1节中小球的下落、碰撞、弹起都是用代码来维护的,下面使用物理引擎来实现,并且加入了刚体之间的碰撞。

(1)常量类Constant

package box2d.bheap;

public class Constant {
	public static final float RATE=10; //屏幕与现实世界的比例
	public static final boolean DRAW_THREAD_FLAG=true; //绘制线程工作标识位
	public static final float TIME_STEP=2.0f/60.0f; //模拟的频率
	public static final int ITERA=10; //迭代次数
	public static int SCREEN_WIDTH; //屏幕宽度
	public static int SCREEN_HEIGHT; //屏幕高度
}

(2)抽象类MyBody

该类为自定义的抽象类,是所有自定义刚体类的基类。由于JBox2D中的刚体类对象仅具有物理仿真计算的功能,并没有提供Android平台下的绘制功能,直接使用不是很方便。因此,这里定义了MyBody对自定义刚体的绘制及JBox2D物理仿真对象进行了封装。

package box2d.bheap;

import org.jbox2d.dynamics.Body;

import android.graphics.Canvas;
import android.graphics.Paint;

public abstract class MyBody {
	Body body;  //JBox2D物理引擎中的刚体 
	int color; //刚体的颜色
	public abstract void drawSelf(Canvas canvas,Paint paint); //绘制的方法
}

(3)圆形刚体类MyCircleColor

package box2d.bheap;

import org.jbox2d.dynamics.Body;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import static box2d.bheap.Constant.*; //静态导入

public class MyCircleColor extends MyBody {
	float radius; //圆形半径
	public MyCircleColor(Body body,float radius,int color) {
		this.body=body;
		this.radius=radius;
		this.color=color;
	}
	@Override
	public void drawSelf(Canvas canvas, Paint paint) {
		paint.setColor(color&0xCFFFFFF); //设置颜色
		float x=body.getPosition().x*RATE;
		float y=body.getPosition().y*RATE;
		canvas.drawCircle(x, y, radius, paint); //画圆
		paint.setStyle(Style.STROKE); //设置空心无填充
		paint.setStrokeWidth(1);
		paint.setColor(color); //画边
		canvas.drawCircle(x, y, radius, paint);
		paint.reset(); //恢复画笔设置
	}

}

(4)矩形刚体类MyRectColor

package box2d.bheap;

import static box2d.bheap.Constant.RATE;

import org.jbox2d.dynamics.Body;

import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;

public class MyRectColor extends MyBody {
	float halfWidth;//半宽
	float halfHeight;//半高
	
	public MyRectColor(Body body,float halfWidth,float halfHeight,int color)
	{
		this.body=body;
		this.halfWidth=halfWidth;
		this.halfHeight=halfHeight;		
		this.color=color;
	}
	
	public void drawSelf(Canvas canvas,Paint paint)
	{ 		  
		paint.setColor(color&0x8CFFFFFF); 
		float x=body.getPosition().x*RATE;
		float y=body.getPosition().y*RATE;
		float angle=body.getAngle();
	    canvas.save();
	    Matrix m1=new Matrix();
	    m1.setRotate((float)Math.toDegrees(angle),x, y);
	    canvas.setMatrix(m1);
		canvas.drawRect(x-halfWidth, y-halfHeight, x+halfWidth, y+halfHeight, paint); 
		paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1);//设置线条宽度
        paint.setColor(color);
        canvas.drawRect(x-halfWidth, y-halfHeight, x+halfWidth, y+halfHeight, paint); 
        paint.reset();      
        canvas.restore();
	}

}

(5)生成刚体形状的工具类Box2DUtil

package box2d.bheap;

import static box2d.bheap.Constant.RATE;

import org.jbox2d.collision.CircleDef;
import org.jbox2d.collision.PolygonDef;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;

public class Box2DUtil {
	/**
	 * 创建矩形物体(颜色)
	 */
	public static MyRectColor createBox (
			float x,
			float y,
			float halfWidth,
			float halfHeight,
			boolean isStatic, //是否为静止的
			World world,
			int color
	) {
		PolygonDef shape=new PolygonDef(); //创建多边形描述对象
		if(isStatic) {
			shape.density=0;
		} else {
			shape.density=1.0f;
		}
		shape.friction=0.0f; //设置摩擦系数
		shape.restitution=0.6f; //设置能量损失率
		shape.setAsBox(halfWidth/RATE, halfHeight/RATE);
		BodyDef bodyDef=new BodyDef(); //创建刚体描述对象
		bodyDef.position.set(x/RATE,y/RATE); //设置位置
		Body bodyTemp=world.createBody(bodyDef); //在世界中创建刚体
		bodyTemp.createShape(shape); //指定刚体形状
		bodyTemp.setMassFromShapes(); //设置物体质量
		return new MyRectColor(bodyTemp, halfWidth, halfHeight, color);
	}
	
	/**
	 * 创建圆形物体(颜色)
	 */
	public static MyCircleColor createCircle (
			float x,
			float y,
			float radius,
			World world,
			int color
	) {
		CircleDef shape=new CircleDef(); //创建圆描述对象
		shape.density=2; //设置密度
		shape.friction=0.0f; //设置摩擦系数
		shape.restitution=0.95f; //设置能量损失率
		shape.radius=radius/RATE;//设置半径
		BodyDef bodyDef=new BodyDef(); //创建刚体描述对象
		bodyDef.position.set(x/RATE,y/RATE); //设置位置
		Body bodyTemp=world.createBody(bodyDef); //在世界中创建刚体
		bodyTemp.createShape(shape); //指定刚体形状
		bodyTemp.setMassFromShapes(); //设置物体质量
		return new MyCircleColor(bodyTemp, radius, color);
	}
		
}

(6)颜色工具类ColorUtil

package box2d.bheap;

public class ColorUtil {
	static int[][] result= 
	   {
		   {56,225,254},   
		   {41,246,239},
		   {34,244,197},
		   {44,241,161},
		   {65,239,106},
		   {45,238,59},
		   {73,244,51},   
		   {99,233,58},
		   {129,243,34},
		   {142,245,44},
		   {187,243,32},
		   {232,250,28},
		   {242,230,46},
		   {248,196,51},
		   {244,125,31},
		   {247,88,46},
		   {249,70,40},
		   {249,70,40},
		   {248,48,48},
		   {250,30,30},
		   {252,15,15},
		   {255,0,0}, 
	   };
	   public static int getColor(int index)
	   {
		   int[] rgb=result[index%result.length];
		   int result=0xff000000;
		   result=result|(rgb[0]<<16);
		   result=result|(rgb[1]<<8);
		   result=result|(rgb[2]);
		   return result;
	   }
}

 

(7)主控制类MyBox2dActivity

package box2d.bheap;     
import java.util.ArrayList;
import java.util.Random;
import org.jbox2d.collision.AABB;   
import org.jbox2d.common.Vec2;    
import org.jbox2d.dynamics.World;     
import android.app.Activity;    
import android.content.pm.ActivityInfo;
import android.os.Bundle;   
import android.util.DisplayMetrics;
import android.view.Window;   
import android.view.WindowManager;  
import static box2d.bheap.Constant.*;
  
public class MyBox2dActivity extends Activity 
{   
    AABB worldAABB;//创建 一个管理碰撞的世界   
    World world;
    Random random=new Random();
    //物体列表
    ArrayList<MyBody> bl=new ArrayList<MyBody>();

    public void onCreate(Bundle savedInstanceState) 
    {   
        super.onCreate(savedInstanceState);
        //设置为全屏
        requestWindowFeature(Window.FEATURE_NO_TITLE);   
        getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN ,   
        WindowManager.LayoutParams. FLAG_FULLSCREEN); 
        //设置为横屏模式
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		
		//获取屏幕尺寸
        DisplayMetrics dm=new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);  
        if(dm.widthPixels<dm.heightPixels)
        {
        	 SCREEN_WIDTH=dm.widthPixels;
             SCREEN_HEIGHT=dm.heightPixels;
        }
        else
        {
        	SCREEN_WIDTH=dm.heightPixels;
            SCREEN_HEIGHT=dm.widthPixels;    
        }
           
        worldAABB = new AABB();   
        
        //上下界,以屏幕的左上方为 原点,如果创建的刚体到达屏幕的边缘的话,会停止模拟   
        worldAABB.lowerBound.set(-100.0f,-100.0f);
        worldAABB.upperBound.set(100.0f, 100.0f);//注意这里使用的是现实世界的单位   
           
        Vec2 gravity = new Vec2(0.0f,10.0f);
        boolean doSleep = true;
        //创建世界 
        world = new World(worldAABB, gravity, doSleep);          
        
        //创建4边
        final int kd=40;//宽度或高度
        MyRectColor mrc=Box2DUtil.createBox(kd/4, SCREEN_HEIGHT/2, kd/4, SCREEN_HEIGHT/2, true,world,0xFFe6e4FF);
        bl.add(mrc);
        mrc=Box2DUtil.createBox(SCREEN_WIDTH-kd/4, SCREEN_HEIGHT/2, kd/4, SCREEN_HEIGHT/2, true,world,0xFFe6e4FF);
        bl.add(mrc);
        mrc=Box2DUtil.createBox(SCREEN_WIDTH/2, kd/4, SCREEN_WIDTH/2, kd/4, true,world,0xFFe6e4FF);
        bl.add(mrc);
        mrc=Box2DUtil.createBox(SCREEN_WIDTH/2, SCREEN_HEIGHT-kd/4, SCREEN_WIDTH/2, kd/4, true,world,0xFFe6e4FF);
        bl.add(mrc);
        
        //创建砖块
        //砖块间距	行间距为20     模块宽度为10 	最多一行为9块
        final int bs=20;
        final int bw=(int)((SCREEN_WIDTH-2*kd-11*bs)/18);
        //============================================================
        for(int i=2;i<10;i++)
        {
        	if((i%2)==0)
        	{
        		//左侧蓝木块
        		for(int j=0;j<9-i;j++)
        		{
        			mrc=Box2DUtil.createBox
        			(
        				kd/2+bs+bw/2+i*(kd+5)/2+j*(kd+5)+3,
        				SCREEN_HEIGHT+bw-i*(bw+kd)/2,
        				bw/2,
        				kd/2,
        				false,
        				world,
        				ColorUtil.getColor(Math.abs(random.nextInt()))
        			);
        			bl.add(mrc);
        		}
        		//右侧蓝木块
        		for(int j=0;j<9-i;j++)
        		{
        			mrc=Box2DUtil.createBox
        			(
        				3*kd/2+bs-bw/2+i*(kd+5)/2+j*(kd+5)-3,
        				SCREEN_HEIGHT+bw-i*(bw+kd)/2,
        				bw/2,
        				kd/2,
        				false,
        				world,
        				ColorUtil.getColor(Math.abs(random.nextInt()))
        			);
        			bl.add(mrc);
        		}
        	}   
        	if((i%2)!=0)
        	{
        		for(int j=0;j<10-i;j++)
        		{
        			mrc=Box2DUtil.createBox
        			(
        				kd/2+bs+kd/2+(i-1)*(kd+5)/2+j*(kd+5),
        				SCREEN_HEIGHT-(kd-bw)/2-(i-1)*(bw+kd)/2,
        				kd/2,
        				bw/2,
        				false,
        				world,
        				ColorUtil.getColor(Math.abs(random.nextInt()))
        			);
        			bl.add(mrc);
        		}
        	}
        }
        mrc=Box2DUtil.createBox
		(
			5*kd+bs+20,
			SCREEN_HEIGHT-(kd+bw)*4-kd,
			bw/2,
			kd/2,
			false,
			world,
			ColorUtil.getColor(Math.abs(random.nextInt()))
		);
		bl.add(mrc);
        //创建球
        MyCircleColor ball=Box2DUtil.createCircle(SCREEN_WIDTH/2-24, kd, kd/2, world,ColorUtil.getColor(Math.abs(random.nextInt())));
        bl.add(ball);
        ball.body.setLinearVelocity(new Vec2(0,50));
           
        GameView gv= new GameView(this);   
        setContentView(gv);   
    }   
}

 

(8)显示界面类GameView

package box2d.bheap;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class GameView extends SurfaceView implements Callback{
	
	MyBox2dActivity activity;
	Paint paint;
	DrawThread dt;

	public GameView(MyBox2dActivity activity) {
		super(activity);
		this.activity=activity;
		this.getHolder().addCallback(this); 
		paint =new Paint();
		paint.setAntiAlias(true);
		dt=new DrawThread(this);
		dt.start();
	}
	
	public void onDraw(Canvas canvas) {
		if(canvas==null) {
			return ;
		}
		canvas.drawARGB(255, 255, 255, 255); //设置背景颜色白色
		for (MyBody mb : activity.bl) {
			mb.drawSelf(canvas, paint);
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
               repaint();
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
	}
	
	public void repaint() {
		SurfaceHolder holder=this.getHolder();
		Canvas canvas=holder.lockCanvas();
		try {
			synchronized(holder){
				onDraw(canvas);
			}
		} catch(Exception e){
			e.printStackTrace();
		} finally {
			if(canvas!=null) {
				holder.unlockCanvasAndPost(canvas); 
			}
		}
	}

}

 

(9)绘制线程类DrawThread

package box2d.bheap;
import static box2d.bheap.Constant.*;

//绘制线程
public class DrawThread extends Thread
{
	GameView gv;
	
	public DrawThread(GameView gv)
	{
		this.gv=gv;
	}
	
	@Override
	public void run()
	{
		while(DRAW_THREAD_FLAG)
		{
			gv.activity.world.step(TIME_STEP, ITERA);//开始模拟
			gv.repaint();
			try 
			{
				Thread.sleep(20);
			} catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
		}
	}
}

 失败了,留坑待改进