物理引擎
物理引擎
一、简介
百度AR引擎已经支持了物理引擎,开发者可以通过简单的配置,向三维模型添加物理属性,让三维模型在场景实现中碰撞与被碰撞、受重力下落以及约束等效果。本篇教程主要内容是物理相关接口教程。如果你对百度AR如何搭建一个场景还不熟悉,请先查看其他教程。
基础:如果你已经搭建好了一个场景,比如有两个三维模型能够显示出来。这时你会发现,这两个三维模型一动不动,(除非你使用了刚体动画的接口或者手势移动三维物体)。如果你希望这两个物体能够像真实物体一样,在场景中受重力影响下落或者在平面上随意滚动,你可以看看下面的内容。
1.1 物理世界坐标系
首先先介绍物理引擎使用的坐标系。物理世界坐标系使用右手坐标系,如图所示。
二.场景的物理世界的创建
2.1 简介
scene是一个场景,它会包含众多的物体在其中。scene中的物理世界创建,会创建一个影响整个场景的物理环境。
注意:
1、创建的场景会影响场景里的所有带有物理效果的实体。
2、如果没有创建物理世界,场景中的物体是不会有物理效果的。
2.2 创建一个物理世界
如果你想要创建一个拥有物理效果的场景,首先一定要调用ARScene类的这个接口:
create_physics_world(ARVec3& gravity)
void create_physics_world(ARVec3& gravity)
为该场景创建一个物理世界
Parameters
- gravity | ARVec3: 重力,用一个三维向量表示
Returns
- void
sample:
scene = app:get_current_scene()
local gravity = ae.ARVec3:new_local(0,-3000,0)
scene:create_physics_world(gravity)
创建物理世界必须要传入重力gravity,重力是使用一个ARVec3来表示的。分别代表x,y,z方向。结合右手坐标系可以看出,如果设置重力为(0, -3000, 0),那么表示重力向下,也就是说,如果你把一个物体放在了空中,那它将会垂直下落!
注意:现实中的重力系数为9.8,如果你设置了9.8或者更小,发现物体并没有像期望一样快速下落,请把重力值调大。
2.3 获取物理世界的实例
当你创建完一个物理世界以后,你可以通过获取物理世界的实例来修改这个世界的部分属性。
获取物理世界实例的接口如下:
get_physics_world()
ARPhysicsWorld& get_physics_world()
获取该场景的物理世界实例
Parameters
- 无
Returns
- ARPhysicsWorld& | 返回一个物理世界的实例
sample:
scene = app:get_current_scene()
local gravity = ae.ARVec3:new_local(0,-3000,0)
scene:create_physics_world(gravity)
physics_world = scene:get_physics_world()
这样就可以获取到场景的物理实例了。
注意:如果你在没有使用创建物理世界的接口就调用了这个接口,那么程序会出错,请小心!
2.4 总结
现在,通过调用创建物理世界函数的接口,你已经成功在场景中生成一个物理世界了,但是你会发现你的三维物体还是没有下落或碰撞。接下来,请继续看下面的内容。再一次强调,一定要调用创建物理世界的接口。
物理世界还有一些能力,诸如创建约束与DebugDraw功能。在后面会提到。你也可以去到DebugDraw功能,先看看如何开启物体碰撞体的显示。
三.为单个物体创建物理碰撞体
3.1 简介
当你创建完物理世界后,接下来你需要做的就是为每一个三维物体创建物理碰撞体,这样这个三维物体才能具有物理属性!
先陈述两个不同的概念:三维物体与碰撞体。
三维模型是我们在渲染引擎中显示的物体,它可能会很复杂,有色彩有动画,这是我们实际可以看见的物体。
碰撞体是通过三维物体来创建的一个虚拟的物体,它可能只是包围在三维物理周围的一个长方体或者一个球体等等。这取决于你如何创建一个碰撞体。有些时候我们不需要如此精确的碰撞,那么我们只需要一个长方体来进行碰撞就可以了。
所以,物体模拟实际模拟的是碰撞体的运动,然后我们会把碰撞体的旋转与移动赋予三维物体,这样可见的三维物体就像是在进行物理模拟一样。
注意:
1、如果你不为三维模型创建物理碰撞体,那么它不具有物理属性。
2、物理世界的重力属性,会对你所有的物理碰撞体都生效。
注意:我们推荐只对三维物体创建物理碰撞体,也就是说,这个节点是一个pod节点或者gltf节点。
3.2 创建一个物理碰撞体
如果你想要让一个三维模型拥有物理效果,首先一定要调用下面这个接口:
create_physics_body(float mass, float restitution, float friction, string body_type, string shape_type, float shape_scale = 1.0)
void create_physics_body(float mass, float restitution, float friction, std::string body_type, std::string shape_type, float shape_scale = 1.0)
为该节点创建一个物理碰撞体
Parameters
- mass | float: 物体的质量
- restitution | float: 弹性系数
- friction | float :摩擦系数
- body_type | string :碰撞体类型,包括:dynamic、kinematic、static
- shape_type | string:碰撞体形状,包括:box、sphere、capsule、cylinder、cone、mesh
- shape_scale | float: 形状放缩系数
Returns
- void
sample:
node:create_physics_body(0.0, 0.5, 0.3,"kinematic", "box");
这些参数包括:质量(mass)、 弹性系数(restitution)、摩擦系数(friction)、 碰撞体类型(body_type)、碰撞体形状(shape_type)、碰撞体形状缩放系数(shape_scale)
接下来的每一个小节中会重点讲述每一个属性的意义以及需要注意的地方。
3.2.1 质量
物体的质量很好理解,对于物理效果来说,如果两个轻重不同的物体相撞,那么轻的那个更容易被撞飞。
小tips:
1、如果你想让一个物体不那么容易被其他物体撞飞,那你可以给它一个大的质量;反之亦然。
2、不要给一个物体质量赋予一个负值,比如-1。如果是负值,那它的质量会是一个默认值。
3.2.2 弹性系数
弹性系数也很好理解,如果一个物体的弹性系数越大,那它碰撞后反弹获取的能量越大。比如,其他属性一定时,弹性系数大的小球与弹性系数小的小球,碰到地上,弹性系数大的小球会弹得高。
小tips:
弹性系数的反弹效果由两个碰撞物体的弹性系数决定。如果两个弹性系数的乘积大于1,则反弹效果越强;如果等于1则反弹效果不变,如果小于1则反弹效果越来越弱。
3.2.3 摩擦系数
摩擦系数越大,物体受到的阻力也越大,意味着它越容易停下来。推荐设置在0-1之间。
3.2.4 碰撞体类型
碰撞体类型有三种,分别是static、kinematic与dynamic。下面的表格具体说明了三种碰撞体的不同。
类型 | 意义 | 备注 |
---|---|---|
static | 一个静态的物体,当被创建之后,它在任何情况下都不能被移动 | 质量一定为0 |
kinematic | 一个可运动的物体,当被创建之后,它的位置不会被重力影响, 也不会被碰撞影响,但是可以位置与旋转可以根据三维物体的位置改变 |
质量一定为0 |
dynamic | 一个完全可以运动的物体,当被创建之后,它的位置会被重力影响, 也会被碰撞影响,它的位置与旋转只能通过施加力与力矩、冲量、线速度和角速度来影响 |
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"static", "box") -- static碰撞体创建
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "box") -- kinematic碰撞体创建
node:create_physics_body(20, 0.5, 0.5,"dynamic", "box") -- dynamic碰撞体创建
Tips:
如果对这三种类型的具体含义还不是特别清楚,请看下面的例子:
1.如果你需要一个可以碰撞的地面,而且你能够确保在任何时候都不会去变更它的位置或者旋转,那你可以采用static来创建。注意:如果你通过set_world_position改变了三维物体的位置,但是static碰撞体还是会在原处!
2.如果你需要创建一个可以移动的墙,那么你可以使用kinematic来创建。当创建完毕后,它也是不动的,不会下落也不会被其他物理撞飞(但是它可以撞飞一个dynamic的物体)。如果你需要移动它的位置,你可以调用node节点的set_world_position方法。换句话中,物理世界的重力与碰撞对它不会产生任何影响,但是它的位置永远和三维物体保持一致。
3.如果你想要一个可以碰撞的小球,那么你可以使用dynamic来创建,当创建完毕后,这个物体就会受物理规律的影响,如下落或者被碰撞离开。需要注意的是,这个三维物体的位置与旋转完全被碰撞体控制,如果你通过set_world_position接口是无法改变它的位置的。如果你想改变它的位置,你需要对它的碰撞体施加力或者速度!
3.2.5 碰撞体形状
如之前所说,三维物体会被抽象为一个碰撞体进行碰撞。所以碰撞体的形状有简单有复杂,这取决你需要多么精确地碰撞。
我们提供了6种碰撞体形状,分别是box、sphere、capsule(包括x,y,z三个不同的轴)、cylinder(包括x,y,z三个不同的轴)、cone(包括x,y,z三个不同的轴)、mesh。
注意:在生成碰撞体形状的时候,如果你对形状的旋转有些不解,那你可以先把场景json配置文件中节点的rotation改为"0,0,0",这样你就会得到一个处在标准右手坐标系下的碰撞体。如果你确定你需要的碰撞体就是这样,你再去修改rotation字段。碰撞体也同样会旋转。
接下来会通过图片的形式展示每一个碰撞体的样子。
3.2.5.1 box(长方体)
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "box")
通过传入“box”字段创建一个长方体的碰撞体。
3.2.5.2 sphere(球体)
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "sphere")
通过传入“sphere”字段创建一个球体的碰撞体。
3.2.5.3 capsule(胶囊体)
胶囊体分为三种不同的类型,分别是以三维物体x,y,z方向长度为高。
capsule_x:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "capsule_x")
capsule_y:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "capsule_y")
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "capsule")
-- 传入capsule的话就是capsule_y
capsule_z:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "capsule_z")
3.2.5.4 cylinder(圆柱体)
圆柱分为三种不同的类型,分别是以三维物体x,y,z方向长度为高。
cylinder_x:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cylinder_x")
cylinder_y:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cylinder_y")
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cylinder")
-- 传入cylinder默认是cylinder_y
cylinder_z:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cylinder_z")
3.2.5.5 cone(圆锥体)
圆锥体分为三种不同的类型,分别是以三维物体x,y,z方向长度为高。
cone_x:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cone_x")
cone_y:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cone_y")
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cone")
-- 传入cone默认是cone_y
cone_z:
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "cone_z")
3.2.5.6 mesh(网格表面)
创建一个网格表面的碰撞体,可以精确碰撞但是耗费性能!
特别注意:本mesh表面为静态mesh表面,创建mesh碰撞体时务必保证模型没有骨骼动画!!!!
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "mesh")
3.2.5.7 shape_scale (形状缩放系数)
通过图片可以看到有时候物体碰撞体并不会完全把三维模型包住,在一些特殊的形状下,物体模型不能包住三维模型。其实在很多时候,有一点模型外露并不会影响什么。因为碰撞在一瞬间发生,物体在碰撞后马上反弹,甚至无法看出它们会有短暂的相交。但是如果你希望碰撞体将三维物理完全包住,有一个形状缩放系数供你使用。如果你不传入这个值,那么默认是1.0。
DEMO:
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "box", 1.5)
node:create_physics_body(0.0, 0.5, 0.5,"kinematic", "box", 0.5)
形状缩放系数在0-1之间表示缩小,大于1表示放大。 注意:mesh体设置形状缩放系统无效。 还有一点特别注意的是:希望你能够选择一个合适的碰撞体形状来创建碰撞体。如图中的小熊模型,一般情况下,选择box形状与cylinder形状都挺好的。但是选择sphere形状与cone形状则不那么好了。
3.2.6 总结
创建一个碰撞体也许不是一个那么容易的事情,因为对于物理模拟来说,它最终的结果是由多种参数决定的。比如重力,弹性系数,摩擦系数,碰撞形状以及初始位置。如果物理模拟的结果不能满足你的预期,那么你需要不断地调整数值,直到达到你满意的效果。
3.3 获取物理碰撞体
获取物理碰撞体的接口很简单:
get_physics_body()
ARPhysicsBody& get_physics_body()
获取该节点的物理碰撞体
Parameters
- 无
Returns
- ARPhysicsBody& | 返回该节点的物理碰撞体
sample:
node:create_physics_body(10, 0.5, 0.5,"dynamic", "box")
local physics_body = node:get_physics_body()
注意:
调用这个接口时必须保证已经调用了create_physics_body接口, 不然程序会退出!!!
3.4 设置节点物理属性接口
set_property_bool(string key, bool enable)
通过统一的set_property_bool接口设置节点的物理属性。包括以下三个:
Parameters
- key | string :与物理相关的属性包括 physics/enabled、physics/ sync_physics_rotation、physics/sync_physics_position
- enable | bool :true 打开 ,false 关闭
Returns
- void
key | 类型 | 含义 | 备注 |
---|---|---|---|
physics/enabled | bool | 打开或关闭该节点的物理能力 | 当关闭了物理能力后物体碰撞体将被删除,当再一次后开启物理碰撞体会自动创建 |
physics/ sync_physics_rotation | bool | 三维模型的旋转是否受dynamic物理碰撞体影响 | 设置为false后,物理碰撞体仍会因力作用产生位移且旋转,但是三维模型仅移动不旋转 |
physics/sync_physics_position | bool | 三维模型的位置是否受dynamic物理碰撞体影响 | 设置为false后,物理碰撞体仍会因力作用产生位移且旋转,但是三维模型仅旋转不移动 |
sample:
node:set_property_bool("physics/enabled", true)
node:set_property_bool("physics/enabled", false)
-- 备注:当关闭了物理能力后物体碰撞体将被删除,当再一次后开启物理碰撞体会自动创建
sample:
node:set_property_bool("physics/sync_physics_rotation", true)
node:set_property_bool("physics/sync_physics_rotation", false)
-- 备注:设置为false后,物理碰撞体仍会因力作用产生位移且旋转,但是三维模型仅移动不旋转
sample:
node:set_property_bool("physics/sync_physics_position", true)
node:set_property_bool("physics/sync_physics_position", false)
-- 备注:设置为false后,物理碰撞体仍会因力作用产生位移且旋转,但是三维模型仅旋转不移动
四. 物理碰撞体的相关接口
当我们创建好物理碰撞体之后,可以获取到物体碰撞体,然后调用物理碰撞体的接口,就可以对物理碰撞体进行一些操作,比如施加力、力矩等。
4.1 施加力和冲量
根据牛顿第二运动定律 $$ F = ma $$ 对其取时间t的微分 $$ dF/dt = m da/dt = mv $$ 所以对物理碰撞体施加力与冲量,其模拟结果是不同的。 施加力之后,物理碰撞体会沿着施力方向不断加速。如果施加反方向力或者将力设置为0,其速度也不会马上消失,物体不会停下。 施加冲量之后,物理碰撞体会沿着冲量方向有一个突变的速度。物体将沿着这个速度方向移动。 注意:仅对dynamic碰撞体生效。
接口表示如下:
set_property_vec3("force", ARVec3 force)
void set_property_vec3("force", ARVec3 force) 为dynamic碰撞体施加力
Parameters
- force | ARVec3 :力,表示为一个三维向量
Returns
- void
sample:
local physics_node = node:get_physics_body()
local force = ae.ARVec3:new_local(1000, 1000, 1000)
physics_node:set_property_vec3("force", force)
set_property_vec3("impluse",ARVec3 impluse)
void set_property_vec3("impluse",ARVec3 impluse)
为dynamic碰撞体施加冲量
Parameters
- impluse | ARVec3 :冲量,表示为一个三维向量
Returns
- void
sample:
local physics_node = node:get_physics_body()
local impluse = ae.ARVec3:new_local(1000, 1000, 1000)
physics_node:set_property_vec3("impluse",impluse)
4.2 施加扭矩和扭矩冲量
扭矩与扭矩冲量对碰撞体的影响类似于力与冲量对于碰撞体的影响。
施加扭矩则碰撞体会不断地旋转,施加扭矩冲量则碰撞体会有一个突变的角速度。物体旋转可能会慢慢停止。 注意:仅对dynamic碰撞体生效。
扭矩也是用一个三维向量来表示: 根据右手法则:将右手张开,四指并拢,拇指与其成直角方向,然后将四指顺着扭转的方向,将轴握住,此时大拇指的方向就是扭矩的方向。
接口如下:
set_property_vec3("torque",ARVec3 torque)
void set_property_vec3("torque", ARVec3 torque)
为dynamic碰撞体施加扭矩
Parameters
- torque | ARVec3 :扭矩,表示为一个三维向量
Returns
- void
sample:
local physics_node = node:get_physics_body()
local torque = ae.ARVec3:new_local(0, 1000, 0)
physics_node:set_property_vec3("torque", torque)
set_property_vec3("torque_impluse",ARVec3 torque_impluse)
void set_property_vec3("torque_impluse",ARVec3 torque_impluse)
为dynamic碰撞体施加扭矩冲量
Parameters
- torque_impluse | ARVec3 :扭矩冲量,表示为一个三维向量
Returns
- void
sample:
local physics_node = node:get_physics_body()
local torque_impluse = ae.ARVec3:new_local(0, 1000, 0)
physics_node:set_property_vec3("torque_impluse",torque_impluse)
4.3 施加线速度与角速度
施加线速度与角速度后物体会使用该线速度与角速度来移动与旋转。但是施加的力与扭矩仍然是会影响的。如果你只希望你设置的线速度与角速度生效,那请把力与扭矩设置为0。
注意:
1、仅对dynamic碰撞体生效。
2、由于施加了过大的线速度,冲量或者力,导致线速度过大可能会导致无法检测碰撞的情况,请一定注意。
3.与设置力和扭矩不同,不同质量的碰撞体设置相同的线速度与角速度,其表现是相同的。
接口如下:
set_property_vec3("linear_velocity",ARVec3 lin_vel))
void set_property_vec3("linear_velocity",ARVec3 lin_vel)
为dynamic碰撞体施加线性速度
Parameters
- lin_vel | ARVec3 :线性速度,表示为一个三维向量
Returns
- void
sample:
local physics_node = node:get_physics_body()
local linear_velocity = ae.ARVec3:new_local(0, 1000, 0)
physics_node:set_property_vec3("linear_velocity",linear_velocity)
set_property_vec3("angular_velocity",ARVec3 ang_vel)
void set_property_vec3("angular_velocity",ARVec3 ang_vel)
为dynamic碰撞体施加角速度
Parameters
- ang_vel | ARVec3 :角速度,表示为一个三维向量
Returns
- void
sample:
local physics_node = node:get_physics_body()
local angular_velocity = ae.ARVec3:new_local(0, 1000, 0)
physics_node:set_property_vec3("angular_velocity",angular_velocity)
4.4 改变重力
如果你希望一个某些碰撞体受重力影响,而某些碰撞体漂浮在空中,那你可以改变重力,让那些需要漂浮的碰撞体重力变为0。 注意:仅对dynamic碰撞体生效。
接口如下:
set_property_vec3("gravity",ARVec3 gravity)
void set_property_vec3("gravity", ARVec3 gravity) 为dynamic碰撞体设置重力
Parameters
- gravity | ARVec3 :重力,表示为一个三维向量
Returns
- void
sample:
local physics_node = node:get_physics_body()
local gravity = ae.ARVec3:new_local(0, 0, 0)
physics_node:set_property_vec3("gravity",gravity)
4.5 修改线性系数与角度系数
线速度系数与角速度系数影响于碰撞体由于设置力、扭矩与自动碰撞产生的速度、角速度影响。在同样的情况下,由于系数的不同,会导致位移与旋转产生不同的效果。
默认的速度系数是1.0,如果设置为0-1之间,则 碰撞体由于设置力、扭矩与碰撞产生的移动与旋转的效果会减弱。当你只想让一个碰撞体移动不旋转,你可以把角速度系数设置为0;当你只想让一个碰撞体只旋转不移动,你可以把线速度系数设置为0。
注意:
1、仅对dynamic碰撞体生效。
2、本系数只对因设置力、扭矩与碰撞产生的移动与旋转有效。
3、当线性系数或角度系数设置为0,直接设置碰撞体的线速度与角速度仍然会让碰撞体移动或者旋转。
这个系数也是一个三维向量,可以分别对x、y、z三个方向做设定。
接口如下:
set_property_vec3("linear_factor",ARVec3 lin_factor)
void set_property_vec3("linear_factor", ARVec3 lin_factor) 为dynamic碰撞体设置线性阻尼系数
Parameters
- lin_factor | ARVec3 :线性系数,表示为一个三维向量,每个分量都在0-1之间,越小线速度的变化越不明显,当设置为0时物体不会因为碰撞或施加力而移动
Returns
- void
sample:
local physics_node = node:get_physics_body()
local linear_factor = ae.ARVec3:new_local(0.0, 0.0, 0.0)
physics_node:set_property_vec3("linear_factor",linear_factor)
set_property_vec3("angular_factor",ARVec3 ang_factor)
void set_property_vec3("angular_factor",ARVec3 ang_factor) 为dynamic碰撞体设置角度系数
Parameters
- ang_factor | ARVec3 :角度系数,表示为一个三维向量,每个分量都在0-1之间,越小角速度的变化越不明显,当设置为0时物体不会因为碰撞或施加力而旋转
Returns
- void
sample:
local physics_node = node:get_physics_body()
local ang_factor = ae.ARVec3:new_local(0.0, 0.0, 0.0)
physics_node:set_property_vec3("angular_factor",ang_factor)
4.6 获取碰撞体的属性
通过get_property_vec3(string key)的方法来获取碰撞体的向量属性
通过get_property_mat44(string key)的方法来获取碰撞体的矩阵属性
get_property_vec3(string key)
ARVec3 get_property_vec3(string key)
获取碰撞体属性
Parameters
- 无
Returns
- ARVec3 | 碰撞体属性
key | 含义 | 备注 |
---|---|---|
force | 返回力 | 与设置的相同 |
torque | 返回扭矩 | 与设置的相同 |
impluse | 返回冲量 | 与设置的相同 |
torque_impluse | 返回扭矩冲量 | 与设置的相同 |
linear_velocity | 返回线速度 | 与设置的可能不同,碰撞体受到碰撞与力的影响,线速度会变化 |
angular_velocity | 返回角速度 | 与设置的可能不同,碰撞体受到碰撞与扭矩、力影响,角速度会变化 |
linear_factor | 返回线系数 | 与设置的相同 |
angular_factor | 返回角系数 | 与设置的相同 |
gravity | 返回重力 | 与设置的相同 |
sample:
local physics_body = node:get_physics_body()
local force = physics_body:get_property_vec3("force")
local torque = physics_body:get_property_vec3("torque")
local impluse = physics_body:get_property_vec3("impluse")
local torque_impluse = physics_body:get_property_vec3("torque_impluse")
local linear_velocity = physics_body:get_property_vec3("linear_velocity")
local angular_velocity = physics_body:get_property_vec3("angular_velocity")
local linear_factor = physics_body:get_property_vec3("linear_factor")
local angular_factor = physics_body:get_property_vec3("angular_factor")
local gravity = physics_body:get_property_vec3("gravity")
get_property_mat44(string key)
ARMat44 get_property_mat44(string key)
获取碰撞体属性
Parameters
- 无
Returns
- ARMat44 | 碰撞体属性
key | 含义 | 备注 |
---|---|---|
world_transform | 碰撞体的世界变换矩阵 | 无法被直接设置,只能被获取 |
sample:
local physics_node = node:get_physics_body()
local world_mat = physics_node:get_property_mat44("world_transform")
4.7 设置物理碰撞响应函数
当碰撞后,可能需要设置碰撞体的碰撞响应,这里提供了一个设置碰撞响应函数的接口。注意需要先创建碰撞体,并获取它。
注意:只要两个碰撞体接触就会有碰撞响应,推荐对dynamic的碰撞体设置碰撞响应。
接口如下:
set_collision_handler(function(name_a, name_b, pos_a, pos_b, point_num) end)
void set_collision_handler(function)
为碰撞体设置物理碰撞响应函数。
注意:
1.直接定义并传入一个lua函数即可,而不是传入它的函数名,可见sample
2.一次碰撞可能有多个碰撞点,碰撞点的获取下标为0到N-1,N为碰撞点个数
Parameters
- function | lua的一个函数,这个函数包含下面五个参数
- name_a | string :一个碰撞体的名字
- name_b | string: 另一个碰撞体的名字
- pos_a | ARVec3[]: 对于a的碰撞体位置数组,可能同时有多个碰撞点
- pos_b | ARVec3[]: 对于b的碰撞体位置数组,可能同时有多个碰撞点
- point_num | 碰撞体的碰撞点数量
Returns
- void
sample1:
function collision_callback(name_a, name_b, pos_a, pos_b, point_num)
for i=0,point_num-1,1 do
ARLOG(name_a .." "..name_b.." "..pos_a[i]:to_string().." "..pos_b[i]:to_string())
end
end
node:create_physics_body(1, 0.9, 0.1,"dynamic", "sphere")
local body = node:get_physics_body()
body:set_collision_handler(collision_callback)
sample2:
node:create_physics_body(1, 0.9, 0.1,"dynamic", "sphere")
local body = node:get_physics_body()
body:set_collision_handler(function (name_a, name_b, pos_a, pos_b, point_num)
for i=0,point_num-1,1 do
ARLOG(name_a .." "..name_b.." "..pos_a[i]:to_string().." "..pos_b[i]:to_string())
end
end)
4.8 小结
如果你在设置力或者冲量的时候发现物体移动或者转动几乎没有,那需要检查一下物体的质量是否过大,或者可以通过把向量扩大10倍或者100倍。总会有效果的。
如果发现设置的力,冲量或者速度会导致小球无法碰撞的情况,那么请降低这些值的大小。比如当一个很小的球去撞击很薄的平面时,请不要将力,冲量或者速度设置得过大。否则可能会无法成功检测碰撞。
五. 对碰撞体创建约束
约束类型一共有三种,分别是点约束、铰链约束以及滑动约束。注意只能为dynamic碰撞体创建约束。
5.1 点约束
如果使用一个点约束,那么在创建约束的那一刻,这个碰撞体就会被约束在某一个点的周围。
接口如下:
add_point_constraint(ARPhysicsBody& body, ARVec3 pivot)
int add_point_constraint(ARPhysicsBody& body, ARVec3 pivot) 为一个碰撞体添加一个点约束
Parameters
- body | ARPhysicsBody: 被添加约束的碰撞体
- pivot | ARVec3: 约束点的位置(相对于碰撞体的位置)
Returns
- int | 约束的id
sample:
......
local physics_world = scene:get_physics_world() -- 注意physics_world需要创建,此处省略
local pivot = ae.ARVec3:new_local(100,0,0)
local body = node:get_physics_body()
physics_world:add_point_constraint(body, pivot)
add_point_constraint(ARPhysicsBody& body_a, ARPhysicsBody& body_b, ARVec3 pivot_a, ARVec3 pivot_b)
int add_point_constraint(ARPhysicsBody& body_a, ARPhysicsBody& body_b, ARVec3 pivot_a, ARVec3 pivot_b) 为两个碰撞体添加点约束
Parameters
- body_a | ARPhysicsBody: 被添加约束的碰撞体
- body_b | ARPhysicsBody: 被添加约束的碰撞体
- pivot_a | ARVec3: 被添加约束的碰撞体的位置(相对于碰撞体a的位置)
- pivot_b | ARVec3: 被添加约束的碰撞体的位置(相对于碰撞体b的位置)
Returns
- id | 约束id
sample:
......
local physics_world = scene:get_physics_world() -- 注意physics_world需要创建,此处省略
local pivot_a = ae.ARVec3:new_local(100,-100,0)
local pivot_b = ae.ARVec3:new_local(-100,100,0)
local body_a = node_a:get_physics_body()
local body_b = node_b:get_physics_body()
physics_world:add_point_constraint(body_a, body_b, pivot_a, pivot_b)
设置点约束有两种类型的接口,第一种只用传入一个碰撞体与一个三维量,这种用于将一个碰撞体约束在一个点的位置。注意:这个点的位置是一个相对坐标量,也就是说传入的三维量是一个坐标,但是它是相对于当前碰撞体位置的位置。 举个例子:如果传入的位置是(0,0,0),那么物体会在约束在自己的位置,而不是约束在坐标原点。如果传入的位置是(0, 100, 0),那么物体会在自己位置上方100的地方被约束,而不是约束在世界坐标系的(0, 100, 0)位置。
第二种传入两个碰撞体与两个三维量。这种可以将两个物体通过点约束链接在一起。注意,pos仍然是相对于各个碰撞体的相对位置。创建约束后,两个约束量会在数帧内合并在一起。这样两个物体就被链接在一起了。
5.2 铰链约束
铰链约束可以创建一个绕轴旋转的约束类型,其接口如下:
add_hinge_constraint(ARPhysicsBody& body, ARVec3 pivot, ARVec3 axis, ARVec2 angle_range)
int add_hinge_constraint(ARPhysicsBody& body, ARVec3 pivot, ARVec3 axis, ARVec2 angle_range) 为一个碰撞体添加一个铰链约束
Parameters
- body | ARPhysicsBody: 被添加约束的碰撞体
- pivot | ARVec3 :旋转轴的位置(相对于碰撞体的位置)
- axis | ARVec3: 旋转轴的方位(相对于碰撞体的旋转)
- angle_range | ARVec2: 旋转的角度范围
Returns
- int | 约束的id
sample:
......
local physics_world = scene:get_physics_world() -- 注意physics_world需要创建,此处省略
local pivot = ae.ARVec3:new_local(600,0,0)
local axis = ae.ARVec3:new_local(0, 1, 0)
local range = ae.ARVec2:new_local(-3.14159, 3.14159)
local body = node:get_physics_body()
physics_world:add_hinge_constraint(body, pivot, axis, range)
铰链约束传入的参数有4个,除了第一个碰撞体之外,剩下三个分别是旋转轴位置,旋转轴方向以及旋转的范围。
与点约束一样,旋转轴位置与旋转轴方向都是按照局部坐标系来定义的,而不是世界坐标系。旋转范围传入的值是弧度值。
5.3 滑块约束
滑块约束用以创建一个可以滑动的范围,其接口如下:
add_slider_constraint(ARPhysicsBody& body, ARVec3 pivot, ARVec3 axis, ARVec2 linear_range)
int add_slider_constraint(ARPhysicsBody& body, ARVec3 pos, ARVec3 axis, ARVec2 linear_range) 为一个碰撞体添加一个滑块约束
Parameters
- body | ARPhysicsBody: 被添加约束的碰撞体
- pos | ARVec3 :滑动轴的位置(相对于碰撞体的位置)
- axis | ARVec3: 滑动轴的方位(相对于碰撞体的旋转)
- linear_range | ARVec2: 滑动范围 Returns
- int | 约束的id
sample:
......
local physics_world = scene:get_physics_world() -- 注意physics_world需要创建,此处省略
local pos = ae.ARVec3:new_local(0, 300, 0)
local axis = ae.ARVec3:new_local(1, 1, 0)
local range = ae.ARVec2:new_local(-500, 1000)
local body = node:get_physics_body()
physics_world:add_slider_constraint(body, pos, axis, range)
铰链约束传入的参数有4个,除了第一个碰撞体之外,剩下三个分别是滑动轴位置,滑动轴方向以及滑动范围。
与点约束一样,滑动轴位置与滑动轴方向都是按照局部坐标系来定义的,而不是世界坐标系。滑动范围可以有正负范围,正表示从滑动轴位置沿着滑动轴的方向的距离,负表示从滑动轴位置沿着滑动轴反方向的距离。滑块的滑动范围就是在以滑动轴位置为中心,滑动范围内滑动。
5.4 取消约束
通过调用该接口取消约束
remove_constraint(int constranint_id)
void remove_constraint(int constranint_id) 取消一个约束
Parameters
- constranint_id | int: 之前被创建的约束id
Returns
- void
sample:
......
local physics_world = scene:get_physics_world() --注意physics_world需要创建,此处省略
local pos = ae.ARVec3:new_local(100,0,0)
local body = node:get_physics_body()
local id = physics_world:add_point_constraint(body, pos)
physics_world:remove_constraint(id)
用于取消一个约束
六.DebugDraw功能
DebugDraw功能对于开发者来说一个非常重要的功能。如果在没有开启DebugDraw功能的情况下,你会发现即使三维物体已经正确地进行了碰撞,你创建的任何碰撞体都无法看见,你创建的任何约束也无法看见。所以,为了便利地进行开发调试,可以开启DebugDraw功能。
接口如下:
set_debug_draw_mode(int mode)
void set_debug_draw_mode(int mode) 取消一个约束
Parameters
- mode | int: debugdraw模式:PHYSICS.DBG_NoDebug、PHYSICS.DBG_ALL、PHYSICS.DBG_DrawWireframe、PHYSICS.DBG_DrawAabb、PHYSICS.DBG_DrawConstraints、PHYSICS.DBG_DrawConstraintLimits
Returns
- void
mode有很多种:一般来说使用以下几种类型即可。
mode | 含义 | 值 |
---|---|---|
PHYSICS.DBG_NoDebug | 关闭DebugMode,不显示任何碰撞体 | 0 |
PHYSICS.DBG_ALL | 显示所有包围盒,碰撞体线框模型,约束,约束限制 | -1 |
PHYSICS.DBG_DrawWireframe | 仅显示碰撞体线框模型 | 1 |
PHYSICS.DBG_DrawAabb | 仅显示碰撞体包围盒 | 2 |
PHYSICS.DBG_DrawConstraints | 仅显示约束 | 2048 |
PHYSICS.DBG_DrawConstraintLimits | 仅显示约束范围 | 4096 |
sample:
local physics_world = scene:get_physics_world()
physics_world:set_debug_draw_mode(PHYSICS.DBG_NoDebug)
physics_world:set_debug_draw_mode(PHYSICS.DBG_ALL)
physics_world:set_debug_draw_mode(PHYSICS.DBG_DrawWireframe)
physics_world:set_debug_draw_mode(PHYSICS.DBG_DrawAabb)
physics_world:set_debug_draw_mode(PHYSICS.DBG_DrawConstraints)
physics_world:set_debug_draw_mode(PHYSICS.DBG_DrawConstraintLimits)
推荐使用PHYSICS.DBG_ALL。或者直接传入数字即可。
特别注意:
DebugDraw功能仅用于case物理效果调试使用,正式发布时不要调用set_debug_draw_mode!!!