🎮 三行代码做一辆Q弹物理自行车,骑上它去海边吧!

4,884 阅读8分钟

我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛

夏天到了,约上三五好友,骑上自行车,在洒满阳光的海岸上飞驰,咔哒咔哒的链条声,伴随着空气中薄荷的清香,这也许就是夏天的味道吧。

今天给大家分享如何用三行代码创建一辆物理自行车。在分享之前,先来体验一下最终效果 ,三、二、一,上链接:夏日单车🚴‍♀️

物理引擎

首先,我们要实现的是一辆带有物理表现效果的自行车,所以需要基于2D物理引擎来进行开发,目前主流Web2D物理引擎有P2.js、Matter.js、Box2D等,其中Box2D是最具代表性的一款2D物理引擎。

传统的Web开发方式,使用Box2D开发较为繁琐,比如我们要创建一个带有重力的正方体

var gravity = newb2Vec2(0,9.8); //定义重力向量,x轴0 y轴向下,加速度是9.8. 
var world = newb2World(gravity,true); //new 一个 名字叫 world 的世界变量
var bodydef = newb2BodyDef(); //new 一个刚体对象 
bodydef.type = b2Body.b2_dynamicBody; //定义刚体对象为 动态对象. 会受到外力的作用.
varfixDef = newb2FixtureDef(); //创建一个 b2FixtureDef 对象. 
fixDef.density=1.0; // desity 密度,如果密度为0或者null,该物体则为一个静止对象 fixDef.friction=0.9; //摩擦力(0~1) 
fixDef.restitution=1.0; // 弹性(0~1)
fixDef.shape = newb2CircleShape(); // 定义圆的半径是 50px window.PTM_RATIO 是米转换像素的因子. 
fixDef.shape.SetRadius(50 / window.PTM_RATIO);
...

可以看到,传统的web开发方式,仅仅是创建一个矩形,需要写一堆的API,创建刚体、创建形状 、创建材质、设置各种属性,相当繁琐。

所以我们选择使用Cocos Creator游戏引擎来进行开发。 Cocos Creator对Box2D进行了封装,让开发者可以以可视化操作的方式创建物理对象,无需编写任何代码。

游戏引擎

CocosCreator是一款十分容易上手的优秀国产游戏引擎,支持JS/TS,编辑器界面也十分简洁:

image.png

使用CocosCreator创建一个具有物理属性的对象,只需要在节点树中创建一个普通对象,在对象的属性检查器中创建物理碰撞组件即可:

3.gif

与物理相关的所有属性配置,对象绑定,关节绑定等都可以通过可视化的方式进行配置。

开始造车

1. 结构拆解

接下来我们开始使用CococsCreator创建一辆自行车,先来看看自行车的结构:

image.png

2.创建车轮

把车轮图片拖到场景树中,添加CircleCollider圆形碰撞体:

4.gif

3.创建车架

车架是一个不规则图形,这里我们添加一个多边形碰撞体,选择编辑模式,手动调整多边形形状:

5.gif

4.建立关节绑定

有了车架和两个轮子,接下来我们要把轮子固定在车架上,并且可以自由旋转,这就涉及到物理引擎中的一个重要组件:关节。

先来认识一下物理引擎中常用的几个关节的定义和表现:

image.png

  1. 旋转关节:围绕一个固定点自由旋转(无法产生位移),可开启Motor属性,模拟轮子驱动效果。
  2. 焊接关节:收到力的作用后,围绕一个固定点旋转(无法产生位移),当失去里的作用后恢复原状,过程中的表现具有弹力效果。
  3. 距离关节:当刚体受力后受距离关节影响被拉回初始位置,并具有弹力效果。
  4. 棱柱关节:约束刚体运动的方向,通常可以跟距离关节组合使用。
  5. 绳子关节:与距离关节相似,不具有弹力属性。
  6. 马达关节:受力产生位移和旋转后回到初始的位置与初始旋转角度,并具有弹力属性。

下面用一个DEMO来演示下每种关节的不同表现:

6.gif

了解完不同关节的用法,接下来我们选择用旋转关节将轮子与车架进行连接:

7.gif

由于旋转关节是绕着一个固定的点旋转,不具有弹力效果,所以骑上这台车,落地的时候可能有点蛋疼。我们可以借助一个中间看不见的空节点“零件”,来模拟一个避震的效果:

8.gif

实现原理:将中间零件B与车架交接处建立一个距离关节(DistanceJoint),将距离设置为0,弹力(Frenquency)设置成一个合适的弹力值,B跟C就产生了弹力连接的效果,再加上一个棱柱关节(PrismaticJoint),限制距离关节运动的方向仅在Y轴方向运动,最后车轮A跟零件B建立一个自由旋转关节(RevoluteJoint),让轮胎挂在零件B上进行旋转,这样我们就完成一个山地车的车轮避震效果。

按照相同原理将前轮、后轮与车架完成关节绑定后,我们来看看效果:

9.gif

有了前后悬挂,小车车一下就DUANG起来了,骑上它腰也不酸,🥚也不疼了。

最后,添加脚踏板,与车架建立一个旋转关节,自行车就完成了:

10.gif

5.创建骑手

首先我们要找到人体的根骨骼,也是人体质量占比最高的身体部位-上半身与盆骨,将其与车架进行连接,四肢与头部再与身体部位建立关节绑定:

11.gif

我们希望在自行车发生碰撞时,身体能随车辆自然着晃动摇摆,所以我们要选择一个具备弹力属性的关节进行连接,距离关节(DistanceJoint)、焊接关节(WeldJoint)、摩托关节(MotorJoint)都具备弹力属性,三种关节绑定方式都有不同的表现,这里我使用了焊接关节。

在身体节点上加上WeldJoint组件,拖动车架节点到ConnectedBody中完成绑定,设置关节的松紧与阻尼大小,将身体节点的关节锚点拖动到身体外部,与车架锚点重合,完成关节绑定:

12.gif

由于我们需要给上肢和下肢足够的活动空间,所以身体摆动和位移的幅度不宜过大,如果使用距离或摩托关节,受到较大撞击时产生的位移可能导致四肢断裂或者挤压过度的情况,经过测试这里使用WeldJoint效果最佳。

6.创建四肢

在创建各个身体部位时,有一个细节需要注意:每个身体部位的质量大小应该符合正常人体部位质量比例,最终创建的布娃娃物理表现会更接近真实效果:

image.png

接下来我们用自由旋转关节给四肢创建关节绑定,用焊接关节给头部创建关节绑定:

13.gif

将前臂末端与车把手建立旋转关节绑定:

14.gif

7.脚踏齿轮驱动

按照符合现实规律的常规思路,我们如果想通过对双脚施加外力来驱动脚踏齿轮旋转,实现方案可能比较复杂,这里我们进行一波反向操作:将两根小腿的末端与齿轮两端连接,通过齿轮旋转来反向驱动双脚运动,也就是动力学中的一个重要方法:反向动力学(IK - Inverse kinematics)

反向动力学(Inverse kinematics)是一种通过先确定子骨骼的位置,然后反求推导出其所在骨骼链上n级父骨骼位置,从而确定整条骨骼链的方法。

15.gif

完成绑定后,我们看看最终效果:

17.gif

至此我们就完成了一辆Q弹自行车的全部创建工作。

不对?说好的三行代码造一辆自行车?怎么一行代码都没有?上代码:

Game.js
onLoad () {
    // 在入口函数中开启物理模拟,默认是关闭的,并设置重力方向与大小。
    let physicsManager = cc.director.getPhysicsManager();
    physicsManager.enabled = true;
    physicsManager.gravity = cc.v2(0, -640);
}

最后

在开始尝试让自行车跑起来后发现了一些问题:由于人体重心与车辆结构重心均靠后,导致自行车在行驶过程或在空中姿态中容易失去平衡向后翻倒,所以我在车架上加入一个质量远大于车身质量的隐形无重力摆锤(类似大楼振荡器原理),来抵消减小车辆失去平衡产生的扭力,同时施加不同方向的恒力,动态调整车身姿态。

写一个物理游戏,跟写好一个物理游戏,差异往往都在细节当中,对于不同物理组件的灵活运用,每个物理参数配置的细微差异,都影响着整体游戏的表现与体验。

最后,骑上它去海边兜风吧!HAVE FUN!

体验连接::夏日单车🚴‍♀️