• 【HTML5物理小Demo】用Box2dWeb实现锁链+弹簧效果


    最近开始研究Box2dweb,Box2dweb是一款物理引擎,主要是对物理刚体和关节连接进行了封装,box2dweb很强大当然也有些复杂,不过幸好lufylegend.js做了这方面的封装,在制作时如果用lufylegend配合Box2dweb,那就简单多了。要学习box2dWeb我还是给大家推荐拉登大叔的博客,地址:http://www.ladeng6666.com/blog,写得相当好,话说他的文章中还运用了相当多的修辞手法呢,看他的文章边学技术,边学写作,哈哈。顺便也提一提,本次实现的效果也是模仿拉登大叔一篇文章中的效果,不过大叔的是ActionScript版本的,我是Js版的。

    最后还是祝大家新年快乐吧~虽然这祝福来晚了,不过还是满含我的诚意……偷笑

    好了,费话不多说,直接进入正题。

    首先看截图吧,如下:

    测试链接:http://game.h5stars.com/20141952ee70ee9c117/

    接下来就来讲一讲实现步骤。

     

    一,准备工作

    首先你需要下载lufylegend和box2dweb 这两个引擎。

    box2dweb可以到这里下载:

    http://code.google.com/p/box2dweb/downloads/list

    lufylegend可以到这里:

    http://lufylegend.com/lufylegend

    关于lufylegend怎么用,可以到这里看看API文档:

    http://lufylegend.com/lufylegend/api

    box2dWeb怎么用?其实我也不太清楚,这次主要用lufylegend封装的API,用到原生API的时候我自己来讲讲吧,讲的差不要骂喔~

     

    二,原理

    这个小demo的原来其实很简单,就是将几块矩形小刚体用旋转关节连接起来。当然,说起来很容易,其实做起来还是要深究的。在我看到拉登大叔的文章之前,其实我也没有想到什么方法,于是就借鉴了大叔的文章,原理不是我独创的,所以呢,不好直说,免得大叔看到了不高兴,大家看一看拉登大叔的文章就能明白原理了。

    文章地址如下:http://www.ladeng6666.com/blog/2012/11/25/create-box2d-linkage-or-bridge-effect-using-b2joint/

     

    三,含详细注释的源代码

     

    [javascript] view plaincopy在CODE上查看代码片派生到我的代码片
    1. <!DOCTYPE html>  
    2. <html>  
    3. <head>  
    4.     <meta charset="UTF-8">  
    5.     <title>box2d demo</title>  
    6.     <script type="text/javascript" src="./Box2dWeb-2.1.a.3.min.js"></script>  
    7.     <script type="text/javascript" src="./lufylegend-1.8.7.min.js"></script>  
    8.       
    9.     <script type="text/javascript">  
    10.         init(50,"mylegend",600,400,pageInit);  
    11.         function pageInit(){  
    12.             LStage.setDebug(true);  
    13.             LStage.box2d = new LBox2d();  
    14.   
    15.             if(LStage.canTouch == true){  
    16.                 document.body.style.margin = "0px 0px";  
    17.                 LStage.stageScale = LStageScaleMode.SHOW_ALL;  
    18.                 LSystem.screen(LStage.FULL_SCREEN);  
    19.             }  
    20.   
    21.             var mainObj = new Main();  
    22.             addChild(mainObj);  
    23.         }  
    24.   
    25.         /** 
    26.          *Main 
    27.          *@author: Yorhom 
    28.          *@http://blog.csdn.net/yorhomwang 
    29.         */  
    30.         function Main(){  
    31.             var s = this;  
    32.             base(s,LSprite,[]);  
    33.   
    34.             /**加入围墙*/  
    35.             s.addWall();  
    36.             /**加入锁链桥*/  
    37.             s.addBridge();  
    38.             /**随机加入其他物体*/  
    39.             s.addRandomObj();  
    40.         }  
    41.         Main.prototype.addWall = function(){  
    42.             var s = this;  
    43.               
    44.             //设置围墙大小  
    45.             var wallSize = 10;  
    46.             //设置围墙数据  
    47.             var wallList = [  
    48.                 //左边  
    49.                 [wallSize*0.5, LStage.height*0.5, wallSize, LStage.height],  
    50.                 //右边  
    51.                 [LStage.width-wallSize*0.5, LStage.height*0.5, wallSize, LStage.height],  
    52.                 //上面  
    53.                 [LStage.width*0.5, wallSize*0.5, LStage.width, wallSize],                 
    54.                 //下面  
    55.                 [LStage.width*0.5, LStage.height-wallSize*0.5, LStage.width, wallSize],  
    56.             ];  
    57.             //通过遍历围墙数据,添加四面围墙  
    58.             for(var key in wallList){  
    59.                 //获取数据  
    60.                 var item = wallList[key];  
    61.                 //创建围墙对象  
    62.                 var wallLayer = new LSprite();  
    63.                 //设定对象位置  
    64.                 wallLayer.x = item[0];  
    65.                 wallLayer.y = item[1];  
    66.                 //加入刚体  
    67.                 wallLayer.addBodyPolygon(item[2],item[3],0);  
    68.                 //加入显示列表  
    69.                 s.addChild(wallLayer);  
    70.             }  
    71.         };  
    72.         Main.prototype.addBridge = function(){  
    73.             var s = this;  
    74.   
    75.             //关节向量  
    76.             var vec = new LStage.box2d.b2Vec2();  
    77.             //添加对象数量  
    78.             var amount = 6;  
    79.             //设置对象宽度和高度  
    80.             var bw=50,bh=15;  
    81.             //获取锁链桥总长度  
    82.             var bridgeWidth = bw*amount;  
    83.             //设置锁链桥开始位置  
    84.             var initX=(LStage.width-bridgeWidth)*0.5,  
    85.                 initY=(LStage.height-bh)*0.5+30;  
    86.             //设置用于固定锁链桥的定点半径  
    87.             var anchorR = 15;  
    88.   
    89.             /**用于固定锁链桥的定点A*/  
    90.             var anchorA = new LSprite();  
    91.             anchorA.x = initX-anchorR;  
    92.             anchorA.y = initY-anchorR;  
    93.             anchorA.addBodyCircle(anchorR,anchorR,anchorR,0);  
    94.             s.addChild(anchorA);  
    95.             /**用于固定锁链桥的定点B*/  
    96.             var anchorB = new LSprite();  
    97.             anchorB.x = bridgeWidth+initX-anchorR;  
    98.             anchorB.y = initY-anchorR;  
    99.             anchorB.addBodyCircle(anchorR,anchorR,anchorR,0);  
    100.             s.addChild(anchorB);  
    101.   
    102.             //上一个对象  
    103.             var previousBlock = anchorA;  
    104.   
    105.             /**循环添加刚体*/  
    106.             for(var i=0; i<amount; i++){  
    107.                 //实例化对象  
    108.                 var block = new LSprite();  
    109.                 //设定对象位置  
    110.                 block.x = initX+i*bw+bw*0.5;  
    111.                 block.y = initY;  
    112.                 //加入刚体  
    113.                 block.addBodyPolygon(bw,bh,1);  
    114.                 //设置鼠标拖动  
    115.                 block.setBodyMouseJoint(true);  
    116.                 //加入显示列表  
    117.                 s.addChild(block);  
    118.                 //加入关节  
    119.                 var revoluteJoint = new LStage.box2d.b2RevoluteJointDef();  
    120.                 vec.Set((initX+i*bw)/30, initY/30);  
    121.                 revoluteJoint.Initialize(previousBlock.box2dBody,block.box2dBody,vec);  
    122.                 LStage.box2d.world.CreateJoint(revoluteJoint);  
    123.                 //更改上一个对象  
    124.                 previousBlock = block;  
    125.             }  
    126.             //将最后一个刚体固定  
    127.             var revoluteJoint = new LStage.box2d.b2RevoluteJointDef();  
    128.             vec.Set((initX+i*bw)/30, initY/30);  
    129.             revoluteJoint.Initialize(previousBlock.box2dBody,anchorB.box2dBody,vec);  
    130.             LStage.box2d.world.CreateJoint(revoluteJoint);  
    131.         };  
    132.         Main.prototype.addRandomObj = function(){  
    133.             var s = this;  
    134.             for(var i=0; i<10; i++){  
    135.                 //创建对象  
    136.                 var obj = new LSprite();  
    137.                 //设置对象位置  
    138.                 obj.x = Math.floor(Math.random()*(400-200+1)+200);  
    139.                 obj.y = 0;  
    140.                 //加入显示列表  
    141.                 s.addChild(obj);  
    142.                 //根据随机数添加不同的刚体  
    143.                 if(Math.random() > 0.5){  
    144.                     //获取随机宽度和高度  
    145.                     var w = Math.floor(Math.random()*10)+25;  
    146.                     var h = Math.floor(Math.random()*10)+25;  
    147.                     //重现设置y坐标  
    148.                     obj.y += h*0.5;  
    149.                     //添加矩形刚体  
    150.                     obj.addBodyPolygon(w,h,1);  
    151.                 }else{  
    152.                     //获取随机半径  
    153.                     var r = Math.floor(Math.random()*20)+5;  
    154.                     //重现设置y坐标  
    155.                     obj.y += r;  
    156.                     //添加圆形刚体  
    157.                     obj.addBodyCircle(r,r,r,1);  
    158.                 }  
    159.                 //设置鼠标拖动  
    160.                 obj.setBodyMouseJoint(true);  
    161.             }  
    162.         };  
    163.     </script>  
    164. </head>  
    165. <body>  
    166.     <div id="mylegend"></div>  
    167. </body>  
    168. </html>  

    所有代码都在这里了,还是很少的,对吧。看了拉登大叔的原理讲解,再来看我写的js代码就非常容易了。主要是注意以下几个地方。

     

    1,添加刚体

    在box2dweb中添加刚体超级麻烦,把整个创建过程告诉大家估计大家都会觉得不耐烦,所以,我用到了lufylegend.js添加刚体,这样一来添加刚体就被简化成一步了。非常方便,不是吗?在lufylegend中添加刚体一共有这几个函数:

     

    addBodyCircle() 添加原形刚体
    addBodyPolygon() 添加矩形刚体
    addBodyVertices() 添加不规则图形刚体

     

    具体的参数说明和使用举例可以参见lufylegend的API文档中LSprite的API。文档地址已在文章的准备工作一栏写出。

    2,添加关节

    在lufylegend虽然也有添加关节的封装,但是不能设置关节点。所以我还是用了box2dweb原生方法。添加关节的代码如下:

    [javascript] view plaincopy在CODE上查看代码片派生到我的代码片
    1. var revoluteJoint = new LStage.box2d.b2RevoluteJointDef();  
    2. vec.Set((initX+i*bw)/30, initY/30);  
    3. revoluteJoint.Initialize(previousBlock.box2dBody,block.box2dBody,vec);  
    4. LStage.box2d.world.CreateJoint(revoluteJoint);  

    其他的好理解,主要是vec这个变量,它在这里是一个向量对象,实例化代码如下:

     

     

    [javascript] view plaincopy在CODE上查看代码片派生到我的代码片
    1. var vec = new LStage.box2d.b2Vec2();  

    其实这个b2Vec2这个类可以传参数,和它的成员函数Set的参数是一样的。但是这里又多个关节,所以就没有直接设置,而是在后面用Set方法设置。这个向量可以看成一个json吧,传的参数就是设定关节的位置(x,y)。

     

    在创建了关节之后,千万别忘记下面的代码:

     

    [javascript] view plaincopy在CODE上查看代码片派生到我的代码片
    1. LStage.box2d.world.CreateJoint(revoluteJoint);  

    另外,Initialize方法传的参数是box2dweb的刚体对象,在LSprite中的 box2dBody就可以获取该LSprite添加的刚体。

     

    3,固定动态刚体

    在box2dweb中,刚体分动态和静态。静态刚体比较老实,就呆在原地不动;动态刚体很活泼,不停地在做运动。

    拉登大叔其实也写过相关的文章,但是我没怎么看,于是就自己想了一个方法:首先在锁链的两端建立两个圆形静态刚体,然后把锁链两端的两块矩形刚体分别连在附近的圆形静态刚体上,圆形静态刚体就会帮忙把锁链拽着,不让它乱跑。但由于锁链中每个矩形刚体是动态的,所以他们还是可以互相牵扯的。

    4,如果你自己在用lufylegend+box2dweb编写时看不到图象,该怎么办?

    首先你要检测一下是否在最底层上加了一个LSprite,并且用这个LSprite的graphics画了一个不透明的背景。如果是的话,你可以把这个LSprite去掉,然后再看看有没有。如果还没有,可能有以下几种原因:(1)没有在用之前加入LStage.setDebug(true); (2)没有在使用box2dweb之前加入LStage.box2d = new LBox2d();

     

    源代码在上面已经全部给出了,运行时需要配置一下引擎box2dweb和lufylegend,然后把引擎的压缩版本(文件名含.min)放在与html同级的文件目录下即可运行,测试愉快~

     

    好了,今天就先讲到这里。如果大家有不懂的地方可以用微博@Yorhom或者用邮箱联系我wangyuehao1999(at)gmail.com(这年头写e-mail地址再不把@换成(at)垃圾邮件多得甚至会把你用的邮件程序弄得黑白不分呢)。希望大家多支持。下次还会继续给大家带来更多有趣的小玩意。敬请期待~

     

    ----------------------------------------------------------------

    欢迎大家转载我的文章。

    转载请注明:转自Yorhom's Game Box

    http://blog.csdn.net/yorhomwang

  • 相关阅读:
    Redisson分布式锁学习总结:公平锁 RedissonFairLock#lock 获取锁源码分析
    Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析
    Redisson分布式锁学习总结:公平锁 RedissonFairLock#unLock 释放锁源码分析
    npm更改为淘宝镜像
    博客园统计阅读量
    自动下载MarkDown格式会议论文的程序
    修改linux ll 命令的日期显示格式
    Canal 实战 | 第一篇:SpringBoot 整合 Canal + RabbitMQ 实现监听 MySQL 数据库同步更新 Redis 缓存
    Log4j2 Jndi 漏洞原理解析、复盘
    一个菜鸡技术人员,很另类的总结
  • 原文地址:https://www.cnblogs.com/ghostll/p/3538426.html
Copyright © 2020-2023  润新知