昨天,我们分享了一篇2D物理文档《LayaAirIDE的可视化2D物理使用文档》。
今天,我们针对LayaAir引擎的初学者,以及对物理引擎使用不熟悉的开发者,再来分享一篇3D物理文档,本篇文档全面介绍了3D物理引擎使用的基础能力。方便开发者快速上手。
LayaAir3D引擎集成了世界三大物理引擎之一的Bullet引擎,当在Unity中使用了物理组件,用LayaAir的Unity插件导出后,默认就是采用的Bullet引擎。当然,LayaAir引擎也支持使用比较轻量的Cannon.js物理引擎的基础功能。
阅读本篇时,除非是Cannon物理引擎的专属章节,或者注明是cannon物理引擎,默认介绍都是基于LayaAir封装的Bullet引擎。
一、刚体
1.1什么是刚体
无论是2D,还是3D,物理的开篇,都需要先了解刚体,这是物理引擎的基础之一。
大家都知道,自然界一切有形体的物质,都可以叫物体。
刚体是力学中为了体现物体特性的一种科学抽象概念,也是一种理想状态的力学表达模型,是指在运动中和受到力的作用后,形状和大小不变,而且内部各点的相对位置不变的物体。
然而,现实中不可能存在这种理想模型,物体在受力之后,会根据力、材料、弹性、塑性等综合因素,决定是否改变或改变多少。如果物体本身的变化不影响整个运动过程,为使被研究的问题简化,仍将该物体当作刚体来处理而忽略物体的体积和形状,这样所得结果仍与实际情况相当符合。
1.2常用的刚体属性
isKinematic是否为运动刚体
3D的刚体,默认是动力学刚体。会受力的影响,可以位移。
一旦我们把刚体设置为运动刚体类型后,即将isKinematic的值设置为true。
那么运动刚体可以触发第三方的物理反馈,自己却不受物理影响。例如,运动刚体与动力学刚体发生撞击,动力学刚体会受力反弹,但运动刚体却不会受力的影响,不会产生受力位移,运动刚体的位移只能通过transform改变节点坐标。
与2D的运动学类型刚体不同,LayaAir3D的运动刚体脱离了物理引擎运动,即使设置速度也不可以使其位移。这样做的好处是减少了物理运算,节省了性能。
mass质量
质量是物质的量的量度,Bullet引擎中的质量单位为kg。
刚体的质量越大,运动状态改变越难,比如,不同质量的两个物体相撞,质量大的一方改变更小一些,如动图1的右侧所示:
(动图1)
静态刚体和运动刚体就相当于无限大质量,所以不受力的影响。
gravity重力
自然界中物体受地心吸引的作用而受到的力叫重力,物理引擎中也同样模拟了重力,
动力学刚体在同等的质量下,重力越大,下落的加速度越大。对比效果如动图1-1。
(动图1-1)
linearVelocity线性速度
刚体的linearVelocity属性称为线速度或者线性速度,是指物体的直线运动速度。
动力学刚体的线速度是3维向量Vector3类型值,向量的方向即速度的方向,向量的长度即速度的大小。
动图1-2,是动力学刚体在同样重力值为0的情况下,没有设置线速度和y轴设置了线速度值的对比效果。
(动图1-2)
linearDamping线性阻尼
刚体的linearDamping属性,是指线性速度的阻尼系数,使得线性速度衰减。
动图1-3,是动力学刚体在重力为0并且y轴设置了同样为-1的线速度值情况下,左侧为0.9线性阻尼值和右侧为1线性阻尼值的对比效果。
(动图1-3)
angularVelocity角速度
刚体的angularVelocity属性是角速度,角速度简单理解就是单位时间的角位移,以弧度每秒进行旋转。当我们设置动力学刚体angularVelocity属性为正值的时候,则按顺时针旋转位移。angularVelocity属性为负值的时候,则按逆时针旋转位移。属性值的绝对值越大,旋转位移速度越快。
(动图1-4)
angulaVelocity属性的值是3维向量Vector3类型值,Bullet使用欧拉角来描述物体的旋转,3D向量的每个分量代表绕x、y、z轴旋转的速度,单位是弧度/秒。动图1-4,就是在x轴分别设置了3.14与31.4的对比效果。
angularDamping角阻尼
刚体的角阻尼相当于是为角速度旋转方向施加了相反的力,使得旋转速度衰减。动图1-5,是在同样的31.4角速度下,左侧为1的的角阻尼值,右侧为0.9的角阻尼值,对比效果。
(动图1-5)
二、物理碰撞
碰撞是物理引擎中最基础、最常用的功能。在这个小节里,我们对3D物理碰撞进行全面的认知。
2.1碰撞器与触发器
对于检测3D物理碰撞的方式,有碰撞器与触发器两种。我们先从概念认知开始。
2.1.1碰撞器
在LayaAir引擎2D物理的时候,通过封装的不同形状的碰撞体,就可以直接实现带范围的物理碰撞。
而LayaAir引擎的3D物理,形状不再是最主要的特征,只是碰撞器用于检测碰撞范围的三维形状区域。
完整的3D碰撞器,由碰撞器和碰撞器形状两部分组成。
3D碰撞器根据特点的不同,分为静态碰撞器、刚体碰撞器、角色碰撞器。
这些碰撞器必须要添加三维碰撞器形状(例如:盒形、球形、圆锥形、圆柱形、胶囊形、平面、混合、模型网格),才可以实现有范围的物理碰撞。
(图2)
图2是胶囊形状角色碰撞器的编辑预览效果。
2.1.2触发器
LayaAir3D物理的触发器相当于2D物理里的传感器。
触发器是碰撞器的一个属性,任何碰撞器的触发器属性设置生效后,当前的碰撞器即转变为触发器(比如,刚体碰撞器设置触发器后可称为刚体触发器)。即使发生物体接触,也不会产生碰撞的物理反馈。例如,动图3-1右侧所示。下落的盒子无视物理引擎,直接穿透而过。
(动图3-1)
设置触发器后,虽然失去了物理引擎反馈,但是可以激活触发器的事件生命周期方法,用于检测物体间碰撞接触的发生。
激活触发器生命周期也有特定的情况除外,具体规则会在下面的物理生命周期章节介绍
当触发器isTrigger设置为true时,或者在Unity的碰撞体组件那里勾选IsTrigger并导出使用时,如图3-2所示。触发器即可设置生效。
(图3-2)
通过LayaAir引擎代码设置触发器的方式:
/*……省略若干代码*///创建盒型MeshSprite3Dletbox=scene.addChild(newLaya.MeshSprite3D(Laya.PrimitiveMesh.createBox(sX,sY,sZ)))asLaya.MeshSprite3D;//创建静态碰撞器letstaticCollider:Laya.PhysicsCollider=box.addComponent(Laya.PhysicsCollider);//设置为触发器,取消物理反馈staticCollider.isTrigger=true;/*……省略若干代码*/
2.2理解各种碰撞器
2.2.1静态碰撞器PhysicsCollider
LayaAir的3D物理碰撞器类是PhysicsCollider,为了便于记忆和理解,我们叫他静态碰撞器类。因为它的特性是不受力,不会产生物理移动。
当其与动力学刚体碰撞器或角色碰撞器发生物理碰撞后,可以触发物理碰撞生命周期方法,但不会产生物理的受力位移。
这种碰撞器可以用于不需要物理受力位移的物体,只需要触发碰撞逻辑的应用场景。例如墙体,撞墙后判定游戏结束。
在Unity中,如果我们添加了某种Collider组件,但并没有添加Rigidbody组件,那导出后就是PhysicsCollider。
2.2.2刚体碰撞器Rigidbody3D
LayaAir的2D物理刚体与碰撞体是分开的,而3D物理的刚体与碰撞器是整合的,Rigidbody3D类即是刚体也是碰撞器,我们可称为刚体碰撞器。
默认情况下,Rigidbody3D是动力学类型的刚体碰撞器,这是可以受力影响的刚体类型碰撞器,所以我们通常用动力学刚体碰撞器进行受力的交互反馈。例如,撞击后的反弹、飞出或者倒下,放在空中会受重力影响而掉落,等等。
当我们将刚体Rigidbody3D的isKinematic设置为true后,那么默认的动力学刚体碰撞器就转变为运动刚体碰撞器。
运动刚体碰撞器从表象上看,与静态碰撞器基本上没有什么区别。都是不受重力、不受速度、不受其它力的影响,在物理世界中永远处于静止,只能通过transform去改变节点坐标来移动。
但实质上,运动刚体有物理特性,它可以是施力物体,可以对非运动刚体产生力,例如通过控制节点去移动运动刚体,会推着挡在前面的动力学刚体移动。而静态碰撞器的应用场景则是要永远不动,也无法施加力。并且,通过节点去移动静态碰撞器,也比较消耗性能。如果有移动的碰撞器需求,例如来回移动的跳板或障碍,使用运动刚体碰撞器就可以了。
通过代码设置运动刚体的方式:
/*……省略若干代码*///创建刚体碰撞器let_rigidBody=sphere.addComponent(Laya.Rigidbody3D)asLaya.Rigidbody3D;//开启运动类型刚体_rigidBody.isKinematic=true;/*……省略若干代码*/在Unity中设置运动类型刚体的方式,如图4所示:
(图4)
由于LayaAir的3D物理中有了静态碰撞器PhysicsCollider,所以并没有在Rigidbody3D中去实现静力学类型的刚体碰撞器。有静止的碰撞反馈需求,直接使用静态碰撞器即可。
2.2.3角色碰撞器CharacterController
角色控制器类CharacterController常用于对第一人称和第三人称游戏角色的控制,可以方便的控制角色的跳跃、跳跃速度、降落速度、行走、等。
由于角色控制器继承于PhysicsComponent,也具有碰撞器的特性,可以添加三维碰撞形状,产生碰撞的反馈,因此也称为角色碰撞器,属于碰撞器之一。
与静态碰撞器和刚体碰撞器都继承自物理触发器组件PhysicsTriggerComponent不同,角色控制器直接继承于物理组件的父类PhysicsComponent。所以,角色控制器是无法设置为触发器的。但是,角色碰撞器与触发器进行接触,仍然可以激活触发器事件的生命周期方法。
2.3碰撞形状
碰撞形状是用于检测碰撞接触的范围,只有添加了形状,碰撞器和触发器才能触发物理反馈和生命周期。
LayaAir引擎支持8种3D碰撞形状,分别为:
盒形BoxColliderShape、球形SphereColliderShape、圆柱形CylinderColliderShape、胶囊形CapsuleColliderShape、圆锥形ConeColliderShape、平面形状StaticPlaneColliderShape、复合形状CompoundColliderShape、网格形状MeshColliderShape。
2.3.1Unity中可导出的碰撞形状
Unity中的盒形碰撞体Boxcollider、球形碰撞体SphereCollider、胶囊形碰撞体CapsuleCollider、网格碰撞体MeshCollider,这4种组件是可以通过LayaAir导出插件直接导出使用的。
这些组件包括了碰撞形状,无需通过引擎代码添加碰撞形状,所以对于盒形、球形、胶囊形、网格形、以及由以上基础形状碰撞体组合而成的复合碰撞形状。都建议在Unity里编辑导出使用。
需要注意的是,这些碰撞体组件的节点如果没有添加刚体组件,那导出后在LayaAir引擎属于静态碰撞器,加上Rigidbody组件后,在LayaAir引擎属刚体碰撞器。
下面我们简单介绍一下这些碰撞体形状的基础属性设置
盒形碰撞形状
盒形碰撞形状是通过设置XYZ调整长宽高的长方体(含立方体)形状。常用于盒子外形的长方体物体,如图5-1所示。
(图5-1)在Unity中,为物体节点对象添加BoxCollider组件,设置XYZ各轴的大小,如图5-2所示,导出后即可使用。
(图5-2)
球形碰撞形状
球形碰撞形状是通过设置半径调整球体大小的碰撞形状。常用于球形外观的物体,如图6-1所示。
(图6-1)
在Unity中,为物体节点对象添加SphereCollider组件,设置半径,如图6-2所示,导出后即可使用。
(图6-2)
胶囊形碰撞形状
胶囊形碰撞形状是由两个半球和一个圆柱体组成,需要通过设置球体半径和圆柱体的高来组成胶囊形状。常用于角色碰撞器。如图7-1所示。
(图7-1)
在Unity中,为物体节点对象添加CapsuleCollider组件,设置半径和高,如图7-2所示,导出后即可使用。
(图7-2)
网格形碰撞形状
网格形碰撞形状是利用模型网格资源构建的形状,如图8-1的蜥蜴所示。相对于其它固定规则的碰撞形状(LayaAir内置的3D碰撞基础形状),网格形碰撞形状属于自定义任意外观的碰撞形状,可以适用于任何模型网格。
(图8-1)
在Unity中,为物体节点对象添加MeshCollider组件,设置模型网格,如图8-2所示,导出后即可使用。
(图8-2)
复合碰撞形状
复合碰撞形状是由多个基础形状组合而成的碰撞器形状。例如桌子或者凳子等,可以由多个盒形碰撞形状组成,如图8-3所示。
(图9-1)
LayaAir引擎的复合碰撞形状,其实Unity中并没有直接对应的组件。但是,开发者在Unity中,对同一个节点对象添加多个基础的碰撞体,例如同时添加BoxCollider组件和SphereCollider组件,如图8-4所示,那通过LayaAir的导出插件导出后,会自动识别为复合碰撞形状。
(图9-2)
2.3.2Unity没有的LayaAir碰撞形状
除了Unity碰撞体组件支持的一些形状外,LayaAir引擎中还内置了一些基础的3D碰撞形状。这些只能通过代码的方式进行添加。
分别是:圆柱形、圆锥形、平面形状。
圆柱形碰撞形状
圆柱形碰撞形状是由两个大小相等、相互平行的圆形(底面)以及连接两个底面的一个曲面(侧面)围成的几何体形状,通过设置底面半径和连接高度来调整碰撞形状的大小。常用于场景的柱子等圆柱形外观的物体碰撞。如图10-1所示。
(图10-1)
项目代码里,通过创建一个CylinderColliderShape实例的方式,传入半径和高,即可返回一个圆柱形碰撞形状对象,将这个对象添加给碰撞器的colliderShape属性即可。API说明如图10-2所示。
(图10-2)
圆锥形碰撞形状
圆锥形碰撞形状是以直角三角形的直角边所在直线为旋转轴,其余两边旋转度而成的曲面所围成的几何体形状。需要设置底面半径和锥体高来调整碰撞形状大小。常用于锥体外观的物体碰撞。如图11-1所示。
(图11-1)
项目代码里,通过创建一个ConeColliderShape实例的方式,传入半径和高,即可返回一个圆锥形碰撞形状对象,将这个对象添加给碰撞器的colliderShape属性即可。API说明如图11-2所示。
(图11-2)
平面碰撞形状
平面碰撞形状,是一种无限大的2D平面碰撞形状。通常用于整个场景地面的碰撞形状。通过法线来确定在3维世界的平面朝向,可以通过偏移值来调整距离原点的偏移多少。API说明如图12-1所示。
(图12-1)
通过API,我们可以看到normal是一个3维向量值,表示着平面的法线。例如这个值为Vector3(0,1,0),则表示法线位于Y轴正方向,平面碰撞形状就是处于其垂直的X轴无限大水平面。
图12-2是法线同样位于Y轴正方向,偏移值offset分别为0(左侧)和为1(右侧)的效果对比。
(图12-2)
2.3.3碰撞器的形状添加示例
使用Unity导出的碰撞组件
Unity导出的碰撞组件使用起来最简单,由于组件已经整合了碰撞器和碰撞形状,直接加载就可以使用了。某些情况下甚至可以不写代码,所以我们介绍一下使用Unity的节点对象和刚体,通过代码添加碰撞形状的示例。
在Unity中,是可以直接创建圆柱体这种基础3D对象的,但是Unity没有圆柱形碰撞组件,创建的圆柱体默认是胶囊碰撞体组件CapsuleCollider,所以,我们删除圆柱体对象的胶囊碰撞体组件,添加刚体组件(Rigidbody)导出,编写代码如下所示:
/*……省略若干代码*/Laya.Scene3D.load(Conventional/SampleScene.ls,Laya.Handler.create(null,function(_Scene3D:Laya.Scene3D){//添加3D场景到舞台Laya.stage.addChild(_Scene3D);let_camera=_Scene3D.getChildByName(MainCamera)asLaya.Camera;_camera.clearFlag=Laya.CameraClearFlags.Sky;//从场景中找到圆柱对象let_cylinder=_Scene3D.getChildByName(Cylinder);//从圆柱对象上获得刚体碰撞器(对应Unity的刚体组件)letcyRigid=_cylinder.getComponent(Laya.Rigidbody3D)asLaya.Rigidbody3D;//创建圆柱体形状(通常与圆柱对象的大小保持一致)letcyShape=newLaya.CylinderColliderShape(0.5,2);//为刚体碰撞器添加碰撞形状cyRigid.colliderShape=cyShape;}));/*……省略若干代码*/LayaAir内置的基础碰撞形状使用示例
内置的碰撞器使用思路为,创建节点对象,创建碰撞器,创建碰撞器形状,为碰撞器添加碰撞形状。
我们以创建圆锥形刚体碰撞器为例,编写代码如下所示:
/*……省略若干代码*//**增加圆锥形刚体碰撞器*/privateaddCone():void{//生成随机值半径和高letraidius=Math.random()*0.2+0.2;letheight=Math.random()*0.5+0.8;//创建圆锥形3D模型节点对象letcone=newLaya.MeshSprite3D(Laya.PrimitiveMesh.createCone(raidius,height));//把圆锥形3D节点对象添加到3D场景节点下this.newScene.addChild(cone);//设置随机位置this.tmpVector.setValue(Math.random()*6-2,6,Math.random()*6-2);cone.transform.position=this.tmpVector;//为圆锥形3D节点对象创建刚体碰撞器let_rigidBody=Laya.Rigidbody3D(cone.addComponent(Laya.Rigidbody3D));//创建圆锥形碰撞器形状(使用节点对象的值,保持一致性)letconeShape=newLaya.ConeColliderShape(raidius,height);//为刚体碰撞器添加碰撞器形状_rigidBody.colliderShape=coneShape;}/*……省略若干代码*/其它基础形状的创建可参考