zoukankan      html  css  js  c++  java
  • HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分

    这是系列第二部分,之前部分在本博客中找

    源码demo存放在https://github.com/willian12345/Box2D-for-Javascript-Games

     

    向世界添加刚体


    刚体(Bodies)是我们用Box2D创建物理游戏的重要对象。任何你可以移动的或交互 的对象都是刚体(Bodies)。

    愤怒的小鸟(Angry Birds)中创建的小鸟和小猪是刚 体,同样在图腾破坏者(Totem Destroyer)中的黄金神像和图腾砖块也是刚体。

    本章将带你学习创建各种类型的Box2D刚体,此外还有一些其它重要的特性,如下表所列

    • 创建圆形刚体


    • 创建矩形刚体


    • 创建任意多边形刚体

    • 使用DebugDraw()方法测试模拟

    • 定义刚体的类型:static,dynamic或kinimatic


    • 设置材质属性:密度(density),摩擦系数(friction)和恢复系数(resitution)

    • 度量单位


    • 创建合成对象

    通过本章的学习,你将会创建一个你的第一个图腾破坏者类型的游戏。本章有较

    多的知识点,那么我们废话少说,直接开始本章的学习吧!

     

    你的第一个模拟—一个球落地

     


    我们先从简单的任务开始,最简单的物理模拟:一个球落到地面。总之,虽然

    这是一个简单小球落地的模拟,但是它将是你的第一个模拟,并且易于很快实

    现它。

    让我们看看在这次模拟中我们要做些什么:

    • 世界的重力(gravity)


    • 一个受到作用力(例如:重力(gravity))的球

    • 一个不受任何作用力的地面


    • 某种材质,正如我们希望小球在地面弹起的材质

    在之前的学习中,你已经能够配置世界的重力了,所以我们从创建小球开始本章的代

    码编写。

    1. 无论我们是创建球形还是多边形,第一步都是创建一个刚体: 

      var bodyDef =new b2BodyDef();

      b2BodyDef类是一个刚体的定义类,它将持有创建我们刚体所需要的所有数 据。

    2. 现在可以将刚体添加到世界中。因为我们采用的舞台尺寸是640X480,我们将 把球放置在舞台的顶部的中心位置,该位置为(320,30),如下所示: 

      bodyDef.position.Set(10.66,1); 

      通过position属性显示的设置了刚体在世界中的位置,但是我确信你会对我之前 所说的位置为(320,30)的设置而变成(10.66,1)而感到困惑。

      这原因要关系 到度量单位上。虽然Flash是以像素(pixels)为度量单位,但是在Box2D中尝试 模拟真实的世界并采用米(meters)作为度量单位。

      对于米(meters)和像素 (pixels)之间的转换没有通用的标准,但是我们采用下面的转换标准可以有很 好的运行效果: 

      1米 = 30像素

      所以,如果我们定义一个变量来帮助我们将米(meters)转换成像素 (pixels),我们便可以在Box2D世界(world)中进行操作时使用像素 (pixels)而不用使用米(meters)来作为度量单位。

      这样将使我们在制作 Flash游戏时,使用像素来思考,从而变得更加直观。

    3. 打开你在第一章中创建的demo1-1.html,并像下面那样修改它: 

      

    <script>
             function init(){
                var b2Vec2 = Box2D.Common.Math.b2Vec2
                ,b2AABB = Box2D.Collision.b2AABB
                ,b2BodyDef = Box2D.Dynamics.b2BodyDef
                ,b2Body = Box2D.Dynamics.b2Body
                ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
                ,b2Fixture = Box2D.Dynamics.b2Fixture
                ,b2World = Box2D.Dynamics.b2World
                ,b2MassData = Box2D.Collision.Shapes.b2MassData
                ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
                ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
                ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
                ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
                ;
    
                
                var world;
                var worldScale = 30;
                function main(){
                   world = new b2World(new b2Vec2(0, 9.81), true);
                   var bodyDef = new b2BodyDef(); 
                   bodyDef.position.Set(320/worldScale,30/worldScale);
    
                   setInterval(updateWorld, 1000 / 60);
                }
                function updateWorld() {
                   world.Step(1/30,10,10);
                   world.ClearForces(); // 清除作用力
                }
    
                main();
             }
             init();
    </script>

    并且,注意我是怎样创建世界和调用step方法的。这比之前少用了几行代码。

    一旦你创建了刚体定义,那么是时候给它一个形状了。

    创建一个圆形形状


     

    形状(shape)是一个2D几何对象,例如一个圆形或者多边形,在这里必须是凸多边

    形(每一个内角小于180度)。记住,Box2D只能处理凸多边形

    现在,我们从小球开始,所以我们创建一个圆形:

    var circleShape =new b2CircleShape(25/worldScale);

    b2CircleShape是用来创建圆形形状,并且它的构造函数需要一个半径(radius)作为 参数。

    在之前的代码中,我们创建了一个圆形,它的半径为25像素(pixels),由于设 置了worldScale变量。

    从现在起,每次你想要使用像素进行操作时,你只要将它们除以 worldScale即可。你也可以定义一个方法名为pixelsToMeters的方法,在每次你需要将像 素(pixels)转换成米(meters)时调用。

    当我们有了刚体定义和形状时,我们将使用夹具(fixture)来将它们粘合起来。

    创建夹具


    夹具(fixture)用于将形状绑定到刚体上,然后定义它的材质,设置密度 (density),摩擦系数(friction)以及恢复系数(restitution)。

    此刻我们无需去 担心材质,让我们把注意力集中到夹具(fixture)上: 

    1.首先,我们创建夹具(fixture):


      

    var fixtureDef = new b2FixtureDef();
    fixtureDef.shape=circleShape;

      一旦我们通过构造函数创建了夹具(fixture),我们将分配之前创建 的形状给它的shape属性。

    2.最后,我们准备将球添加到世界中:


      

    var theBall =world.CreateBody(bodyDef);
    theBall.CreateFixture(fixtureDef);

    b2Body是刚体的实体:是物质,是通过使用bodyDef属性创建的具 体刚体。

    3.再次说明一下,使用以下步骤将刚体添加到世界中:

      I 创建一个刚体定义,它将持有刚体信息,例如刚体的位置信息。

      II 创建一个形状,它将决定刚体的显示形状

      III. 创建一个夹具,将形状附加到刚体定义上。

      IV. 创建刚体在世界中的实体,使用夹具。

    一旦你知道了每一步的重要性,添加刚体到你的Box2D世界中将会 很容易和有趣

    回到我们的项目。现在的main函数内应该看起来和下面一样: 

    function main(){
                   world = new b2World(new b2Vec2(0, 9.81), true);
                   var bodyDef = new b2BodyDef(); 
                   bodyDef.position.Set(320/worldScale,30/worldScale);
                   var circleShape = new b2CircleShape(25/worldScale);
                   var fixtureDef = new b2FixtureDef();
                   fixtureDef.shape = circleShape;
                   fixtureDef.density = 1;
                   fixtureDef.restitution = .6;
                   fixtureDef.friction = .1;
                   var theBall = world.CreateBody(bodyDef);
                   theBall.CreateFixture(fixtureDef);
    
                   setInterval(updateWorld, 1000 / 60);
    }

    定时保存项目并测试它。准备好看看你的第一个Box2D刚体的活动?运行影片!

    额…,然而你现在运行时还是看不到任何东西。。让我告诉你原因,Box2D只负责模拟物理世界,而不负责显示任何东西。

    这意味着,你的刚体正活跃在你的Box2D世界中,只是你看不到而已。

    使用调试绘图测试你的模拟


    幸运的是,Box2D有一个特性,调试绘图(debug draw),它将帮助你显示出模拟的情况:

    在网页中首先要添加一个canvas如

    <canvas id="canvas" width="640" height="480" style="" ></canvas>

    1.调试绘图(debug draw)将Box2D世界中发生的事情显示出来,在

    updateWorld方法中,我们可以在Step()方法之后调用世界(world)的 DrawDebugData()方法:

           world.DrawDebugData();

    2. 一旦我们告知世界在每次遍历之后显示调试绘图(debug draw),我们需要通 过调试绘图(debug draw)定义视觉设置。如下添加代码到你的main函数内: 

    var debugDraw = new b2DebugDraw();
    debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
    debugDraw.SetDrawScale(worldScale);
    debugDraw.SetFillAlpha(0.5);
    debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
    world.SetDebugDraw(debugDraw);

    3.这里有很多代码,所以让我们来解释一下发生了什么。你已经知道 DrawDebugData()方法代表什么,所以我们将解释其它行代码代表的意思:

      

    var debugDraw = new b2DebugDraw();

    b2DebugDraw是一个类,它支持调试绘图(debug draw)出你的游戏中的物理 实体。

      

     var debugSprite:Sprite = new Sprite();

    debugSprite被添加到显示列表(Display List),准备显示在canvas上。

    debugDraw.SetSprite(debugSprite);

    SetSprite()方法告知debugSprite将要被用来显示调试绘图 (debug draw)。

    debugDraw.SetDrawScale(worldScale);

    因为我们要将米(meters)转变为像素(pixels),我们需要通知调试绘 图(debug draw)我们使用的换算比例。 debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

    SetFlag()方法允许我们决定我们将在调试绘图(debug draw)中描绘的物 理实体的类型。此刻,我们只需要绘制形状。

    补充说明:

    setFlag()方法选择性的绘制Box2D对象的内容。这样可以节省CPU开支。setFlag()方法有一个16进制的参数,这参数的取值只能是b2DebugDraw中定义的下面几个常量

    另外,我们还可以用”或”运算符,同时使用多个Flag

    debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);

    debugDraw.SetFillAlpha(0.5);

    SetFillAlpha()方法是为了便于观看而设置的。形状的轮廓是不透明的,填 充的颜色是半透明的。这将使得调试绘图输出更加易于理解。

    world.SetDebugDraw(debugDraw);

    最后,我们将指派调试绘图(Debug draw)到我们刚刚创建的世界(world)

    4.现在是时候来测试一下你影片了,然后你应该会看到下图所示的样子: 

     

    就这样!你设法看到了你放置在Box2D世界中的刚体。

     

    目前,球体还无法在重力的作用下下落,但是不要担心,我们将在稍后修改它。

     

    现在,让我们来创建一些可以作为地面的东西,例如一个放置在舞台底部边缘的大矩

     

    形。从现在开始一切将更加简单,作为新的刚体将会很快的自动显示在它所添加的世界中。

     

    完整源码在demo2-1.html中查看

    创建矩形形状


     

     

    让我执行下面的步骤:

      1.首先,刚体和夹具的定义可以重指定到我们定义的新的刚体上。这样,我 们无需再去定义bodyDef变量,但是我们要改变原先在创建球时使用的坐 标:


    bodyDef.position.Set(320/worldScale,470/worldScale); 

      2.我们将用b2PolygonShape类创建一个多边形:  

    var polygonShape = new b2PolygonShape(); 

      这样,我们以之前创建圆形形状时,相同的方法创建了一个多边形形状。

      3.多边形形状必须遵守一些限制,但是目前,因为我们只需要一个轴对称的矩 形,SetAsBox()方法便能满足我们的需要: 
 

    polygonShape.SetAsBox(320/worldScale,10/worldScale);

      这个方法需要两个参数:矩形的半宽长和半高长。最后,我们的新多边形形状 的中心在像素(320,470),它的宽度为640像素和高度为20像素——这是我们 刚刚创建的地面的尺寸。 


      4.现在,我们改变定义的夹具的shape属性,附加新的多边形形状: 

    fixtureDef.shape = polygonShape; 

      5.最后,我们可以创建刚体并将夹具附加上去,就像我们在球形上做的那样。

    var theFloor = world.CreateBody(bodyDef); 
    theFloor.CreateFixture(fixtureDef); 

      6.你的main方法应该向下面这样:

    function main(){
                   world = new b2World(new b2Vec2(0, 9.81), true);
                   var bodyDef = new b2BodyDef(); 
                   bodyDef.position.Set(320/worldScale,30/worldScale);
                   var circleShape = new b2CircleShape(25/worldScale);
                   var fixtureDef = new b2FixtureDef();
                   fixtureDef.shape = circleShape;
                   fixtureDef.density = 1;
                   fixtureDef.restitution = .6;
                   fixtureDef.friction = .1;
                   var theBall = world.CreateBody(bodyDef);
                   theBall.CreateFixture(fixtureDef);
    
                   // 定义矩形地面
                   bodyDef.position.Set(320/worldScale, 470/worldScale); 
                   bodyDef.type = b2Body.b2_staticBody;
                   var polygonShape = new b2PolygonShape();
                   polygonShape.SetAsBox(320/worldScale, 10/worldScale);
                   fixtureDef.shape = polygonShape; // 复用夹具
    
                   var theFloor = world.CreateBody(bodyDef);
                   theFloor.CreateFixture(fixtureDef);
    
                    //setup debug draw
                   var debugDraw = new b2DebugDraw();
                   debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
                   debugDraw.SetDrawScale(worldScale);
                   debugDraw.SetFillAlpha(0.5);
                   debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
                   world.SetDebugDraw(debugDraw);
    
                   setInterval(updateWorld, 1000 / 60);
                }

      7.测试影片,你将会看到地面: 

      

    完整源码在demo2-2.html中查看

    你看是不是很简单?我们花了将近一章半去防止我们的第一个刚体,然后只花了很

    少的几行代码添加另一个刚体。

    不同的刚体类型——static,dynamic和 kinematic 


     

    有三种Box2D刚体的类型:staitc,dynamic和kinematic。

     

    一个static类型的刚体不受任何力,冲量或撞击的影响并且不会移动。它只能通过 用户手动移动。默认情况下,所有的Box2D刚体都是static类型的刚体,这就是为什 么球不移动的原因。一个static类型的刚体不会和别的static或kinematic类型的刚体发 生碰撞。

    一个dynamic类型的刚体受力,冲量,撞击以及任何世界事件的影响。它可以通过 手动移动,虽然我建议让它们通过世界的重力,和任何类型刚体的碰撞来移动。

    一个kinematic类型的刚体是一个介于static和dynamic刚体之间的混合刚体。它不 受理的影响,但是可以通过手动和设置它们的速率来移动。它不能和static和 kinematic类型的刚体碰撞。

    现在回到我们的 模拟钟来。那种类型是我们要指派给球和地面的呢?

    地面是static类型的刚体,它无需移动,然而通过世界重力球要移动,所以是 dynamic类型的刚体。

    你只需要设置刚体定义的type属性就能告知Box2D每一个刚体的类型,属性值可以是

    b2Body.b2_staticBody, b2Body.b2_dynamicBody或b2Body.b2_kinematicBody分别对应 static,dynamic或kinematic刚体。

    为球添加上bodyDef.type=b2Body.b2_dynamicBody;

    地面添加上bodyDef.type=b2Body.b2_staticBody;

    你的新main方法向下面这样: 

    function main(){
                   world = new b2World(new b2Vec2(0, 9.81), true);
                   var bodyDef = new b2BodyDef(); 
                   bodyDef.position.Set(320/worldScale,30/worldScale);
                   bodyDef.type = b2Body.b2_dynamicBody;
                   var circleShape = new b2CircleShape(25/worldScale);
                   var fixtureDef = new b2FixtureDef();
                   fixtureDef.shape = circleShape;
                   var theBall = world.CreateBody(bodyDef);
                   theBall.CreateFixture(fixtureDef);
    
                   // 定义矩形地面
                   bodyDef.position.Set(320/worldScale, 470/worldScale); // 复用定义刚体
                   bodyDef.type = b2Body.b2_staticBody;
                   var polygonShape = new b2PolygonShape();
                   polygonShape.SetAsBox(320/worldScale, 10/worldScale);
                   fixtureDef.shape = polygonShape; // 复用夹具
    
                   var theFloor = world.CreateBody(bodyDef);
                   theFloor.CreateFixture(fixtureDef);
    
                    //setup debug draw
                   var debugDraw = new b2DebugDraw();
                   debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
                   debugDraw.SetDrawScale(worldScale);
                   debugDraw.SetFillAlpha(0.5);
                   debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
                   world.SetDebugDraw(debugDraw);
    
                   setInterval(updateWorld, 1000 / 60);
    }

    在恭喜你运行成功你的第一个模拟之前,让我们花点时间来说一下关于当使用调试 绘图(debug draw)时的不同颜色。

    static类型的刚体将会绘制成绿色。dynamic类型的刚体,当它们没有在睡眠状态 时将会绘制成红色,在睡眠状态时将会绘制成灰色。

    kinematic类型的刚体,在之 前的屏幕截图中没有显示,它将会被显示为蓝色。

    现在,我们知道当刚体进入睡眠状态并节约CPU资源这个概念。正如你所见, 当球撞击地面,没有别的里影响它时,所以求可以进入睡眠状态,知道有什么 发生为止。

    现在,有一个新的问题。球在落地后没有弹起。如果我们想要运行一个完美的模

    拟,我们需要给我们的刚体一些更多的属性。

    密度,摩擦和恢复


    正如你已经知道怎样向世界添加刚体,那么我想向你介绍三种将会改变刚体行为

    的属性:密度,摩擦和恢复。

    密度(density)用来设置刚体的质量,按照公斤没平方米。越高的密度意味着越 重的刚体,并且该值不能为负。

    摩擦(friction)在两个刚体在彼此的表面上移动时产生,它是通过一个系数来定 义的,通常它的范围在0(没有摩擦)-1(最大摩擦)之间。它不能为负数。

    恢复(restitution)决定刚体在发生碰撞时反弹的程度。与密度(density)和摩擦 (friction)一样,它不能为负数并且它是一个介于0-1的系数来定义的。

    一个小球 在恢复为0时落向地面,不发生反弹(无弹性碰撞),反之恢复为1时小球将会以此刻撞击时相同的速率弹起(完全弹性碰撞)。

    密度(density),摩擦(friction)和恢复(restitution)必须添加到夹具上,所以在main方法中添加以下几行代码:


    fixtureDef.density=1;

    fixtureDef.restitution=0.6;

    fixtureDef.friction=0.1;

    在你的main函数内看起来应该这样

    function main(){
                   world = new b2World(new b2Vec2(0, 9.81), true);
                   var bodyDef = new b2BodyDef(); 
                   bodyDef.position.Set(320/worldScale,30/worldScale);
                   bodyDef.type = b2Body.b2_dynamicBody;
                   var circleShape = new b2CircleShape(25/worldScale);
                   var fixtureDef = new b2FixtureDef();
                   fixtureDef.shape = circleShape;
                   fixtureDef.density = 1;
                   fixtureDef.restitution = .6;
                   fixtureDef.friction = .1;
                   var theBall = world.CreateBody(bodyDef);
                   theBall.CreateFixture(fixtureDef);
    
                   // 定义矩形地面
                   bodyDef.position.Set(320/worldScale, 470/worldScale); // 复用定义刚体
                   bodyDef.type = b2Body.b2_staticBody;
                   var polygonShape = new b2PolygonShape();
                   polygonShape.SetAsBox(320/worldScale, 10/worldScale);
                   fixtureDef.shape = polygonShape; // 复用夹具
    
                   var theFloor = world.CreateBody(bodyDef);
                   theFloor.CreateFixture(fixtureDef);
    
                    //setup debug draw
                   var debugDraw = new b2DebugDraw();
                   debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
                   debugDraw.SetDrawScale(worldScale);
                   debugDraw.SetFillAlpha(0.5);
                   debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
                   world.SetDebugDraw(debugDraw);
    
                   setInterval(updateWorld, 1000 / 60);
                }

    我向夹具指派一次属性,而所有的刚体都将使用这个相同的夹具。在本书的整个

    讲解过程中,我们将要处理很多夹具的属性,但是目前让我们只需要设置小球弹跳即可。

    测试demo2-3.html,你就会发现小球在弹跳

    祝贺你!你刚刚完成了你的第一个真实的Box2D项目,那么现在你有能力去创建 基础的形状和为它们分配特性和属性。

    接下去让我开始来创建一个准游戏吧…

     

     


    注:转载请注明出处博客园:sheldon-二狗-偷饭猫(willian12345@126.com)

    https://github.com/willian12345

  • 相关阅读:
    selenium登录csdn,urllib抓取数据
    selenium登录12306,requests抓取数据
    加一
    最后一个单词的长度
    最大子序和
    搜索插入位置
    实现strStr()
    二分查找
    内存对齐
    is_pod类型
  • 原文地址:https://www.cnblogs.com/willian/p/5478539.html
Copyright © 2011-2022 走看看