上篇我们基于HT for Web呈现了A* Search Algorithm的3D寻路效果,这篇我们将采用HT for Web 3D来呈现Box2DJS物理引擎的碰撞效果,同上篇其实Box2DJS只是二维的平面碰撞物理引擎,但同样通过3D的呈现能让人更直观的体验到碰撞效果,先上张最终例子效果图:

基于HT for Web 3D呈现Box2DJS物理引擎-LMLPHP

Box2D最早是Erin Catto在GDC大会上的一个展示例子,后来不断完善成C++的开源物理引擎库,这些年了衍生出Java、ActionScript以及JS等版本,被广泛应用在游戏领域。说其丰富的确很丰富,说乱也够乱的,找个Box2D的JS版就有N多选择,而且不同版本API还有差异,可参考这里的对比 http://stackoverflow.com/questions/7628078/which-box2d-javascript-library-should-i-use

虽然版本较多有点乱,但各个版本的基本原理和API都类似,以下为我基于Box2DJS融合HT for Web写的例子代码。Box2D有很多参数功能点,这里例子我们仅呈现最基础简单的要素,主要让大家理解Box2DJS引擎的基本使用,以及呈现上如何与HT for Web结合。

点击(此处)折叠或打开

  1. function init() {
  2.     dm = new ht.DataModel();
  3.     g3d = new ht.graph3d.Graph3dView(dm);
  4.     g3d.setGridVisible(true);
  5.     g3d.addToDOM();
  6.     g3d.setEye(100, 50, 150);

  7.     // Define the world
  8.     var gravity = new b2Vec2(0, -100);
  9.     var doSleep = false;
  10.     world = new b2World(gravity, doSleep);

  11.     createNode([0, -3, 0], [100, 6, 100], false, 0);
  12.     createNode([-100, -50, 0], [400, 6, 100], false, -Math.PI/8);
  13.     createNode([100, -50, 0], [50, 6, 100], false, Math.PI/6);

  14.     createNode([1, 50, 0], [10, 10, 10], true);
  15.     createNode([-1, 90, 0], [10, 10, 10], true);

  16.     render();
  17. }

  18. function createNode(p3, s3, dynamic, angle) {
  19.     var node = new ht.Node();
  20.     node.p3(p3);
  21.     node.s3(s3);
  22.     node.setRotationZ(angle == null ? Math.PI * Math.random() : angle);
  23.     dm.add(node);

  24.     var fixDef = new b2FixtureDef();
  25.     if (dynamic) {
  26.         fixDef.density = 0.5;
  27.         fixDef.friction = 0.5;
  28.         fixDef.restitution = 0.5;
  29.         node.s({
  30.             'all.color': 'red',
  31.             'batch': 'dynamic'
  32.         });
  33.     } else {
  34.         fixDef.density = 0.0;
  35.     }

  36.     var shape = new b2PolygonShape();
  37.     shape.SetAsBox(s3[0] / 2, s3[1] / 2);
  38.     fixDef.shape = shape;

  39.     var bodyDef = new b2BodyDef();
  40.     bodyDef.type = dynamic ? b2Body.b2_dynamicBody : b2Body.b2_staticBody;
  41.     bodyDef.position.Set(p3[0], p3[1]);
  42.     bodyDef.angle = node.getRotationZ();
  43.     bodyDef.userData = node;

  44.     world.CreateBody(bodyDef).CreateFixture(fixDef);
  45. }

  46. count = 0
  47. function render() {
  48.     count++;
  49.     if(count % 10 === 0){
  50.         createNode([-1, 50, 0], [10, 10, 10], true);
  51.     }
  52.     world.Step(1 / 60, 10, 10);
  53.     var list = world.GetBodyList();
  54.     while (list) {
  55.         var node = list.m_userData;
  56.         if(node){
  57.             var position = list.GetPosition();
  58.             if(position.y < -150 || g3d.isSelected(node)){
  59.                 dm.remove(node);
  60.                 world.DestroyBody(list);
  61.             }else{
  62.                 node.p3(position.x, position.y, 0);
  63.                 node.setRotationZ(list.GetAngle());
  64.             }
  65.         }
  66.         list = list.GetNext();
  67.     }
  68.     requestAnimationFrame(render);
  69. }

以上代码在createNode中即构建的HT for Web的Node对象,同时构建了Box2D的Body对象,并通过userData属性关联在一起,在requestAnimationFrame的渲染过程,先通过world.Step(1 / 60, 10, 10);更新物理引擎的内部运算,然后遍历所有Body元素将运算结果,也就是Body的位置和旋转角度等信息同步到HT for Web的Node对象,从而达到了HT for Web和Box2DJS的强强结合各施其才。

例子中物体掉落到-150以下我就删除了Box2DJS以及HT的DataModel中对应的数据元素,同时选中图元也会自动删除图元,count % 10 === 0 这个用来没十次刷新产生一个新的立方体。Box2D还可以玩出很多花样,如果数据量大也可以考虑参考《3D拓扑自动布局之Web Workers篇》,将Box2DJS的密集运算在WebWork中执行,我没评估过性能的提升幅度,数据量大时WebWork和GUI线程的数据序列化传递也会有负担需注意,最终的例子3D效果玩起来还是挺有趣的:http://v.youku.com/v_show/id_XODM0OTQ0NzEy.html

基于HT for Web 3D呈现Box2DJS物理引擎-LMLPHP


12-17 19:59