这周末无聊,翻到舍友有本游戏开发的书,就浏览了遍。
因为对游戏以前其实没接触过,可能就简单知道一边游戏绘图和逻辑以及游戏框架绘制的简单概念。
这次主要是看了下游戏开发中最常用和基础的一个物理引擎,Box2D。
对于这个引擎,可能说最好表现和表达出它的用途的,我觉得疯狂的小鸟就是最贴近的一个例子。
小鸟中,小鸟撞击石块产生物体运动。
如果我们自己做呢?
1.首先小鸟被弹出去时,这个运动轨迹就得我们自己根据小鸟重力,初速度,阻力摩擦力。这个一般来说没很清晰的物理知识,挺麻烦的,因为初中物理这块我是快忘完了。
2.小鸟接触到石块堆,碰撞的检测。那我们就需要时时的来说计算小鸟这个物体的像素区是否和某个石块的像素重合了,也就是碰撞了。
3.碰撞完后呢?那就要模拟撞击后物体发生运动改变的效果了。这里又要涉及到,由于不同物体间由于密度啊,碰撞恢复力,摩擦力等等一系列的问题才能模拟出整个撞击后物体运行的效果。
然后Box2D就完全给你把包含在这个物理世界中,物体的各种事件给模拟了,你不过只需每秒多少次的去获取此刻物体在物理世界中模拟处于的状态参数(一般就是坐标和角度)后就可以将其绘制在屏幕上即可。
Box2D有不同语言版本,这边只是介绍Android开发中的这个版本,Jbox2d.
Box2D其实这个来说,入门并不复杂,因为基本看一遍物理世界定义,创建,物体定义创建。可能说一天就了解了
但如果涉及到里面更多的参数,因为对于现实中一个物体来说,涉及到的细节和参数可能是非常多的,如果你想把一个物理世界模拟的接近现实世界,那如何定义这些参数值,都是挺有研究的。
以下是我周六无聊花了个下午时间了解的内容,顺便做下笔记。
******************************************************************************************************************/
Box2D的一些概念。
基本的类 World , AABB , Body ,ShapeDef ,BodyDef
一.世界的创建
首先对于box2d来说,最重要的就是创建一个物理世界,来管理和模拟这个世界里的所有物体状态。
AABB aabb = new AABB(); aabb.lowerBound = new Vec2(-100, -100); aabb.upperBound = new Vec2(100, 100); Vec2 gravity = new Vec2(0, 10); world = new World(aabb, gravity, true);
第一步,通过AABB实例来设置这个世界的边界,在边界外额物体就停止模拟。
第二部,创建一个world世界对象。传入的三个参数分别表示。aabb指定世界的边界,gravity世界的重力矢量,第三个是是否当一个物体停止运动后允许其休眠,无需在模拟该物体。
二.在世界里创建物体。
创建一个刚体涉及到的三个类:
ShapeDef类,形状定义类(当然一般我们都用的是他的子类。)对于把这个定义为形状定义类,其实总觉得怪怪的,因为对于物体的摩擦力恢复力密度等等是通过它类指定的。可能说我们一般认为形状定义可能是物体的外部结构,摩擦力定义在这里可能还说的过去,因为摩擦力可能要涉及到物体外部表层的特性。对于密度,恢复力我一直觉得应该算是一个物体的特有属性吧。
BodyDef类,刚体定义类。主要用来描述物体位置,角度等
Body类,刚体类,也就是通过形状定义和刚体定义完成一个刚体实例。
一个物体的创建,简单就分三步
public static Body createPolygon(World world ,float x, float y, float w, float h, float angle, float density){ PolygonDef pd = new PolygonDef(); pd.density = density;//密度 pd.friction = 0.8f;//摩擦力 pd.restitution = 0.7f;//恢复力 pd.setAsBox(w / 2 / Constant.RATE , h / 2 / Constant.RATE ); BodyDef bd = new BodyDef(); bd.position.set((x+w/2)/Constant.RATE, ( y +h/2)/Constant.RATE); bd.angle = (float) (angle * Math.PI / 180); Body body = world.createBody(bd); body.m_userData = new MyRect(x, y, w, h); body.createShape(pd); body.setMassFromShapes(); return body; }
PolygonDef是Shapedef的子类,称为多边形描述类。定义了密度,摩擦力,恢复力(就指的是我们初中学的弹性形变那玩意,试几次即知道了).
setAsBox是快速创建一个形状盒子,传入的参数是你要创建的盒子的长,宽的一半。这是基本写死的,就是一半,要知道为什么就好比你在稿纸上画图,box2d内部逻辑处理觉得传入这个参数画起来最简单点。
BodyDef,刚体定义,设置物体在物理世界中的坐标,在屏幕中我们是以物体左上角的点为(x,y),早物理世界中是以物体中间点。那么我们传入的是(x,y),要想最后物体绘制和我们原本设想的一样,那么想x,y就要分别往x,y轴方向移动物体长,宽的一半(具体情况实践一下看下效果就很明确了)。
Body,通过物理世界工厂根据刚体定义和形状定义在世界中生成一个物体。
至此,一个物理世界和在物理世界中简单的创建物体方式以实现。
接下来是处理游戏框架中绘图和逻辑处理代码。
private void draw() { try{ canvas = holder.lockCanvas(); canvas.drawColor(Color.BLACK);//清屏 //绘制背景 canvas.drawBitmap(bitmapBg, 0, 0, paint); //遍历绘制Body 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.drawRect(canvas, paint); }if ((body.m_userData) instanceof MyStone) { MyStone title = (MyStone) (body.m_userData); title.drawStone(canvas, paint); } body = body.m_next; } }catch(Exception e){ }finally{ try { if (canvas != null) holder.unlockCanvasAndPost(canvas); } catch (Exception e2) { } } } /** * 逻辑 */ private void logic() { world.step(Constant.stepTime, Constant.iteraTions); ///遍历Body,进行Body与图形之间的传递数据 Body body = world.getBodyList(); for (int i = 1; i < world.getBodyCount(); i++) { //判定m_userData中的数据是否为MyRect实例 if ((body.m_userData) instanceof MyRect) { MyRect rect = (MyRect) (body.m_userData); rect.setX(body.getPosition().x * Constant.RATE - rect.getW() / 2); rect.setY(body.getPosition().y *Constant.RATE - rect.getH() / 2); rect.setAngle((float) (body.getAngle() * 180 / Math.PI)); } else if ((body.m_userData) instanceof MyStone) { //判定m_userData中的数据是否为MyTile实例 MyStone tile = (MyStone) (body.m_userData); tile.setX(body.getPosition().x * Constant.RATE - tile.getW() / 2); tile.setY(body.getPosition().y * Constant.RATE- tile.getH() / 2); tile.setAngle((float) (body.getAngle() * 180 / Math.PI)); } body = body.m_next; } conut++; }
绘图代码中,绘制背景图和物理世界中的图。
对于如何遍历绘制物理世界中的物体,box2d也为我们提供了方法,Body body = world.getBodyList();获取第一个body后再循环指向下一个。
逻辑代码中,对物理逻辑的处理,在这个简单的例子中不过是不停的获取下物理世界模拟中物体的坐标角度和碰撞等。
world.step(Constant.stepTime, Constant.iteraTions);首先首先世界模拟,然后还是迭代的获取坐标角度。
整个来说最简单的例子就完成了。
里面涉及到的几个常量和变量这边解释下:
时间步和迭代计算次数Constant.stepTime,,Constant.iteraTions(这边我是将其保存在常量类中)。
时间及模拟频率,即一秒内模拟几次(一般都是1f/60f),然后是迭代数(一般设置10f),我是这么理解的,即在一个时间步中对物体状态计算次数,越高越耗性能当然数据相对来说就月精准。
像素米比例。Constant.RATE .即屏幕和真实物理世界比例(这边按常规的设置了30.)这个怎么理解呢,比如,你想在屏幕中间画一个物体(物体的x,y点为屏幕中点),那么你传入坐标就是屏幕长度的一般和宽度的一般,按480X800.那么传入的参数就是 240,和400,然后再物理世界中确定该物体,如果没有比例,而上面已经定义过整个物理世界的边框为aabb.lowerBound = new Vec2(-100, -100);
aabb.upperBound = new Vec2(100, 100);
物体要放置在物理世界中的左边为(240+宽度一般,400+宽度一般)。也就说,物体直接跨过边界了。不在地球,在火星了。简单意思就是这样
不用纠结它,试几次就ok了,这个具体值就根据你个人情况再来修改。
下面一个物体掉落的小例子效果图:
demo地址:
http://download.csdn.net/detail/nono_love_lilith/4130991
顺便把两份文档也提供给有需要的,因为这文档在百度文库什么的下载可能需要积分,而想下下不了。
http://download.csdn.net/detail/nono_love_lilith/4131004