AngryBird简单开发实现(一),使用JBox2D-2.1.2简单开发的demo

来源:互联网 发布:java多线程查询数据库 编辑:程序博客网 时间:2024/06/04 18:43

在自学Android开发近半年之后,终于选定了毕业设计的课题,做一个Angry Bird。

开发了一个简单的demo,目前只有发射和碰撞检测功能,其他功能还需不断完善。


首先要向Himi表示感谢,正是看了Himi的《Android游戏编程之从零开始》,才第一次知道了JBox2D物理学引擎,才第一次使用了物理学引擎(见识鄙陋,不好意思害羞)。

因为AngryBird和Cut the rope是我最喜欢的两款手机游戏,故当我知道了AngryBird是使用JBox2D开发的时,便决定抱着向前辈致敬的态度,自己也来简单的实现下了。

引擎使用的是最新的JBox2D-2.1.2, 一开始创建world时总是报错,说是slf4j有问题,感谢z1074971432,正是通过他的解答,才使得新版的JBox2D-2.1.2可以顺利的在android上跑起来。

下面是比较重要的部分代码

package com.ritterliu.angryBirdTwo;import org.jbox2d.callbacks.ContactImpulse;import org.jbox2d.callbacks.ContactListener;import org.jbox2d.collision.Manifold;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 org.jbox2d.dynamics.contacts.Contact;import com.ritterliu.angryBird.R;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.SurfaceHolder.Callback;public class MySurfaceView extends SurfaceView implements Callback,Runnable,ContactListener{private SurfaceHolder sfh;/**总的运行线程*/private Thread th;/**线程运行标志位*/private boolean flag;private Canvas canvas;private Paint paint;private int screenW,screenH;/**Bird类,用以绘画出小鸟*/Bird bird;/**touchEvent的优化,避免真机调试时频繁响应*/byte[] lock = new byte[0];private final int timePause=50;/**物理世界声明*/World world;//AABB aabb;  //新版的JBox2D已经不需要AABB区域了Vec2 gravity;private final float RATE=30.0f; //物理世界与屏幕环境缩放比列float timeStep=1f/60f;/**新的JBox2D增加到两个控制迭代,参数均按照官方manual上的参数设置的 */int velocityIterations = 10;int positionIterations = 8;public MySurfaceView(Context context) {super(context);sfh=this.getHolder();sfh.addCallback(this);paint=new Paint();paint.setStyle(Style.STROKE);paint.setAntiAlias(true);//aabb=new AABB(); //旧版JBox2D的创建方法//aabb.lowerBound.set(-100, -100);//aabb.upperBound.set(100,100);/**重力初始化*/gravity=new Vec2(0,10f);/**创建物理世界*/world=new World(gravity, true);/**增加物理世界中的碰撞监听*/world.setContactListener(this);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stub/**得到屏幕大小*/this.screenW=this.getWidth();this.screenH=this.getHeight();/**初始化小鸟位置*/AngryBirdActivity.startX=100;AngryBirdActivity.startY=screenH-100;/**初始化橡皮筋长度*/AngryBirdActivity.touchDistance=0.2f*screenH;Bitmap bmpBird=BitmapFactory.decodeResource(this.getResources(), R.drawable.smallbird);bird=new Bird(AngryBirdActivity.startX,AngryBirdActivity.startY,bmpBird.getHeight()/2f,bmpBird,Type.redBird);/** 创建四周的边框,设置 isStatic为true,即在物理世界中是静止的, * Type设置为ground,避免被击毁 * */createPolygon(5, 5, this.getWidth() - 10, 2, true,Type.ground);createPolygon(5, this.getHeight() - 10, this.getWidth() - 10, 2, true,Type.ground);createPolygon(5, 5, 2, this.getHeight() - 10, true,Type.ground);createPolygon(this.getWidth() - 10, 5, 2, this.getHeight() - 10, true,Type.ground);/**创建6个方形,isStatic设置为false,即在物理世界中是动态,收外力作用影响 */for(int i=0;i<6;i++){createPolygon(screenW-150,screenH-50-20*i,20,20, false,Type.wood);}/**创建一个长条型,也是动态的 */createPolygon(screenW-180,screenH-50-20*6-10,80,10, false,Type.wood);/**启动线程*/flag=true;th=new Thread(this);th.start();}/**创建圆形的body*/public Body createCircle(float x,float y,float r,boolean isStatic){/**设置body形状*/    CircleShape circle = new CircleShape();    /**半径,要将屏幕的参数转化到物理世界中 */    circle.m_radius = r/RATE;    /**设置FixtureDef */FixtureDef fDef=new FixtureDef();if(isStatic){/**密度为0时,在物理世界中不受外力影响,为静止的 */fDef.density=0;}else{/**密度不为0时,在物理世界中会受外力影响 */fDef.density=1;}/**设置摩擦力,范围为 0~1 */fDef.friction=1.0f;/**设置物体碰撞的回复力,值越大,物体越有弹性 */fDef.restitution=0.3f;/**添加形状*/fDef.shape=circle;    /**设置BodyDef */BodyDef bodyDef=new BodyDef();/**此处一定要设置,即使density不为0, * 若此处不将body.type设置为BodyType.DYNAMIC,物体亦会静止 * */bodyDef.type=isStatic?BodyType.STATIC:BodyType.DYNAMIC;/**设置body位置,要将屏幕的参数转化到物理世界中 */bodyDef.position.set((x)/RATE, (y)/RATE);/**创建body*/Body body=world.createBody(bodyDef);/**添加 m_userData */body.m_userData=bird;//body.createShape(fDef); //旧版JBox2D的创建方法/**为body创建Fixture*/body.createFixture(fDef); //body.setMassFromShapes();//旧版JBox2D的创建方法return body;}public Body createPolygon(float x,float y,float width,float height,boolean isStatic,Type type){PolygonShape polygon =new PolygonShape();polygon.setAsBox(width/2/RATE, height/2/RATE);FixtureDef fDef=new FixtureDef();if(isStatic){fDef.density=0;}else{fDef.density=1;}fDef.friction=1.0f;fDef.restitution=0.0f;fDef.shape=polygon;BodyDef bodyDef=new BodyDef();bodyDef.type=isStatic?BodyType.STATIC:BodyType.DYNAMIC;//newbodyDef.position.set((x+width/2)/RATE,(y+height/2)/RATE );Body body=world.createBody(bodyDef);body.m_userData=new MyRect(x,y,width,height,type);//body.createShape(polygonDef);//body.setMassFromShapes();body.createFixture(fDef);return body;}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {// TODO Auto-generated method stub}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {// TODO Auto-generated method stubflag=false;}public void draw(){try{canvas=sfh.lockCanvas();if(canvas!=null){/**用白色填充画布*/canvas.drawColor(Color.WHITE);/**画出小鸟*/bird.draw(canvas, paint);/**如果小鸟还没被发射,画出拖动的橡皮筋轨迹*/if(!bird.getIsReleased()){canvas.drawLine(AngryBirdActivity.startX, AngryBirdActivity.startY, bird.getX(), bird.getY(), paint);}/**遍历物理世界,画出Rect */Body body = world.getBodyList();for (int i = 1; i < world.getBodyCount(); i++) {if ((body.m_userData) instanceof MyRect) {MyRect rect = (MyRect) (body.m_userData);rect.draw(canvas, paint);}body = body.m_next;}}}catch(Exception ex){ex.printStackTrace();}finally{if(canvas!=null){sfh.unlockCanvasAndPost(canvas);}}}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 MyRect) {MyRect rect = (MyRect) (body.m_userData);rect.setX(body.getPosition().x * RATE - (rect.getWidth()/2));rect.setY(body.getPosition().y * RATE - (rect.getHeight()/2));rect.setAngle((float)(body.getAngle()*180/Math.PI));}else if ((body.m_userData) instanceof Bird) {Bird bird = (Bird) (body.m_userData);bird.setX(body.getPosition().x * RATE );bird.setY(body.getPosition().y * RATE );bird.setAngle((float)(body.getAngle()*180/Math.PI));}else // body.m_userData==null时,将body销毁,表示被击毁{world.destroyBody(body);}body = body.m_next;}/**发射小鸟,且只有一次,发射过后,不能再拖动了*/if(bird.getIsReleased()&&!bird.getApplyForce()){/**发射时才创建一个body*/Body birdBody=createCircle(bird.getX(),bird.getY(),bird.getR(),false);/**设置bullet属性为true,否则速度过快时可能会有穿越现象 */birdBody.setBullet(true);/**发射力量控制*/float forceRate=50f;/**根据橡皮筋长度和角度设置发射力*/float angle=(float) Math.atan2(bird.getY()-AngryBirdActivity.startY,bird.getX()-AngryBirdActivity.startX);float forceX=-(float) (Math.sqrt(Math.pow(bird.getX()-AngryBirdActivity.startX, 2))*Math.cos(angle));float forceY=-(float) (Math.sqrt(Math.pow(bird.getY()-AngryBirdActivity.startY, 2))*Math.sin(angle));Vec2 force=new Vec2(forceX*forceRate,forceY*forceRate);/**对body应用作用力 */birdBody.applyForce(force, birdBody.getWorldCenter());/**设置已经作用过力,发射后,不能再拖动了 */bird.setApplyForce(true);}}@Overridepublic void run() {while(flag){long start=System.currentTimeMillis();draw();logic();long end=System.currentTimeMillis();try{if(end-start<50){Thread.sleep(50-(end-start));}}catch(Exception ex){ex.printStackTrace();}}}public boolean onTouchEvent(MotionEvent event){if(event.getAction()==MotionEvent.ACTION_DOWN){/**小鸟未发射时点击它*/if(bird.isPressed(event)&&!bird.getIsReleased()){bird.setIsPressed(true);}}else if(event.getAction()==MotionEvent.ACTION_MOVE){/**小鸟未发射时拖动 */if(bird.getIsPressed()){bird.move(event);}}else if(event.getAction()==MotionEvent.ACTION_UP){if(bird.getIsPressed()){bird.setIsReleased(true);bird.setIsPressed(false);}}/**对touchEvent的优化,防止真机调试时过于频繁的响应 */synchronized(lock){try{lock.wait(timePause);}catch(Exception ex){ex.printStackTrace();}}return true;}@Overridepublic void beginContact(Contact arg0) {// TODO Auto-generated method stub}@Overridepublic void endContact(Contact arg0) {// TODO Auto-generated method stub}@Overridepublic void postSolve(Contact arg0, ContactImpulse arg1) {// TODO Auto-generated method stub/**碰撞事件的检测,参数是调试出来的 */if(arg1.normalImpulses[0]>5){if ( (arg0.getFixtureA().getBody().getUserData())instanceof MyRect){MyRect rect=(MyRect)(arg0.getFixtureA().getBody().getUserData());/**只有这几种类型会被击毁 */if(rect.getType()==Type.stone||rect.getType()==Type.wood||rect.getType()==Type.pig||rect.getType()==Type.glass){arg0.getFixtureA().getBody().m_userData=null;}}if ( (arg0.getFixtureB().getBody().getUserData())instanceof MyRect){MyRect rect=(MyRect)(arg0.getFixtureB().getBody().getUserData());if(rect.getType()==Type.stone||rect.getType()==Type.wood||rect.getType()==Type.pig||rect.getType()==Type.glass){arg0.getFixtureB().getBody().m_userData=null;}}}}@Overridepublic void preSolve(Contact arg0, Manifold arg1) {// TODO Auto-generated method stub}}
这个简单的demo只能算是对JBox2D的初步运用,实现了基本的小鸟的发射和物体的击毁,还有很多功能没有完善,比如说屏幕的zoom in/out,小鸟发射后的镜头跟随等等。正如一个前辈所说的,做一个游戏不难,做好一个游戏很难,下面还会不断进行完善的。奋斗

to be continued...

完整源码下载

原创粉丝点击