【iOS ARKit】RealityKit 中的物理组件

在 RealityKit 中,对虚拟物体进行物理模拟时需要在该物体实体对象上挂载物理组件,物理引擎会忽略所有未挂载物理组件的物体,RealityKit 包含两个跟物理相关的组件:PhysicsBodyComponent 和PhysicsMotionComponent,ModelEntity 实体类默认带有这两个组件,即使用 ModelEntity 类创建的实体都会参与物理模拟。

PhysicsBodyComponent 组件

PhysicsBodyComponent 组件以纯物理的方式处理力、质量、材质属性,根据牛顿力学模拟物体的运动。它是 RealityKit 物理模拟最主要的组件,利用该组件可以设置物体质量、物理质、物理类型、受力等所有与物理相关的属性。需要注意的是,物理模拟要起作用,物体还必须要有CollisionComponent 组件。

PhysicsBodyComponent 组件属性从功能上可以分为3大类:物理属性类、力学属性类和辅助类。物理属性类主要包括表9-1所示物理属性。

表9-1 物理属性类

|---------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| 属性名称 | 描述 |
| mode | PhysicsBodyMode 属性,定义物体受力影响的类型 |
| massProperties | PhysicsMassProperties 属性,定义物体质量相关属性 |
| material | PhysicsMaterialResource 属性,定义物体的物理材质 |
| is TranslationLocked | 移动限制,定义物体是否在某个或几个轴方向上锁定移动,默认不限制。限制移动可以限制物体在指定范围内,如限制桌子在Y轴上的移动,不允许桌子悬空 |
| isRotationLocked | 旋转限制,定义物体是否在某个或几个轴方向上锁定旋转,默认不限制。限制旋转可以限制物体的旋转范围,如限制车轮在Z轴上的旋转,不允许车轮翻转 |
| isContinuousCollisionDetectionEnabled | 连续碰撞检测,常称为 CCD,用于快速运动物体的碰撞检测,性能开销比较大,默认 false。如子弹射击时,由于子弹速度快,可能会在两次物理模拟循环之间穿过某个物体,导致漏检,利用CCD可以检测子弹等快速移动物体与其他物体的碰撞 |

从表9-1可以看出,物理属性类定义了物理模拟所需要的各类参数,这些参数设置会直接影响物理模拟后物体的运动表现。力学属性类主要处理与力相关的动态工作,如表9-2所示。

表9-2 力学属性类

|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| 方法名称 | 描述 |
| addForce (SIMD3 < Float >, relative To:Entity?) | 在物体的质心处施加一个力,这个力相对于 relativeTo实体的坐标空间,nil为世界空间。施加的力在下一个模拟周期中执行,且在一个完整的模拟周期执行完后被移除 |
| addForce (SIMD3 < Float >, at: SIMD3< Float>, relativeTo: Entity?) | 在物体的指定位置施加一个力,这个力相对于 relativeTo实体的坐标空间,nil为世界空间。施加的力在下一个模拟周期中执行,且在一个完整的模拟周期执行完后被移除 |
| add Torque (SIMD3 < Float>, relative To:Entity?) | 在物体的质心处施加一个力矩,这个力矩相对于 relativeTo实体的坐标空间,nil为世界空间。施加的力矩在下一个模拟周期中执行,且在一个完整的模拟周期执行完后被移除 |
| clearForcesAndTorques () | 清除施加到物体的所有力与力矩 |
| applyLinearImpulse (SIMD3 < Float >, relative To: Entity?) | 在物体的质心处施加一个线性冲量,这个冲量相对于 relativeTo实体的坐标空间,nil为世界空间 |
| applyAngularlmpulse(SIMD3 < Float >, relative To: Entity?) | 在物体的质心出施加一个线性冲量,这个冲量相对于 relativeTo实体的坐标空间,nil 为世界空间 |
| applyImpulse (SIMD3 < Float >,at:SIMD3 < Float >,relativeTo: Entity?) | 在物体的指定位置施加一个线性冲量,这个冲量相对于 relativeTo实体的坐标空间,nil 为世界空间 |
| reset Physics Transform (recursive: Bool) | 重置模拟物理体的位置、方向和速度 |

使用表9-2中的方法,可以按照牛顿力学的方式对物体施加力、力矩、冲量,如果对不活跃的物体(Entity.isActive=false)施加力、力矩、冲量,物理引擎不会进行模拟计算,也不会有任何效果。addForce和addlmpulse 是两种施加力的方式,Impulse称为冲量,是一个与时间有关的物理量,单位是kg•m/s, 而力是与时间无关的物理量,单位是牛顿。

提示:冲量描述了动量(momentum)的改变量,Impulse=m(W」一w2),而F=ma,因此F=m(V1一V2)/t,所以Impulse=Ft,即冲量是力与该力作用时间的乘积。物理引擎中的材质称为物理材质,物理材质定义了物体的物理特性,如静摩擦力、动摩擦力、弹性系数、恢复系数等

辅助类用于辅助进行初始化或者物理模拟,主要包括表9-3所示功能。

表9-3 辅助类

|--------------------|----|-----------------------------------------------------------|
| 名称 | 类型 | 描述 |
| init | 方法 | 初始化方法,共有4个重载,用于以指定质量、物理材质、物理类型初始化 PhysicsBodyComponent 组件 |
| physics Body | 属性 | 指向该实体的 PhysicsBodyComponent 组件 |
| registerComponent) | 方法 | 类方法,用于注册各类组件 |

在实际的开发过程中,我们一般会通过实体的 physicsBody 属性获取该实体上挂载的PhysicsBodyComponent 组件,然后利用该组件设置物体的物理属性、施加作用力进行物理模拟。利用PhysicsBodyComponent 组件进行物理模拟是完全按照物理力学规律进行仿真计算,因此模拟结果与真实物体行力表现相一致。

PhysicsBodyComponent 组件的mode 属性描述物体的物理类型,该值为 PhysicsBodyMode 枚举值,PhysicsBodyMode 枚举定义了物体受力影响的类型,该枚举有3个枚举值:static、kinematic、dynamic。

  1. static(静态体) 静态体参与碰撞,但不参与物理模拟,也不会移动位置。静态体存在的重要意义在于其能与动态体产生相互作用,当动态体与静态体发生碰撞时,静态体本身不会受到任何力与碰撞的影响,但动态体会遵循物理规律,受力并发生变化。如场景中的墙壁与巨大的石头,因为它们不会移动位置,在使用中,通常会用作环境物体,应当设置为静态体,不应当使用任何方式移动静态体。当一个动态体小球与墙壁或者石头发生碰撞时,墙壁和石头不会受到力的影响,也不会产生位置移动,但小球会发生受力变化,产生反弹或者改变运动方向。

  2. kinematic(动力学体)动力学体与静态体一样,也不参与物理模拟,但动力学体可以运动,其运动由开发人员自行处理,不必遵循物理学规律。动力学体也会与动态体发生碰撞并影响动态体的运动。例如电梯或者大门,这类物体可以运动,但通常不需要使用物理引擎模拟,一般采用手动的方式控制其运动,如当一个机器人接近大门时,大门可以自动开启,而无须机器人对大门施加作用力。该类物体运动时也会影响与其相关的动态体,如电梯会使电梯内的动态体升高或者降低,门开关时会推动门后的动态体。

  3. dynamic(动态体)动态体的运动受到物理引擎的控制,如给子弹施加一个力,子弹的运动就会受到物理引擎的驱动,呈现与真实世界一致的运动轨迹。动态体可以与静态体、动力学体、动态体发生碰撞反应,并遵循物理规律。

PhysicsBodyComponent 组件的 massProperties 属性力 PhysicsMassProperties 类型,该属性定义了物体的质量属性。PhysicsMassProperties 结构体的主要属性如表9-4所示。

表 9-4 PhysicsMassProperties 结构体

|--------------|----|-------------------------------------|
| 属性名称 | 类型 | 描述 |
| init( ) | 方法 | 初始化方法,共有4个重载,用于以指定质量、惯性、质心、包围盒进行初始化 |
| mass | 属性 | 物体质量,单位为千克(kg) |
| inertia | 属性 | 转动贯量,单位为千克平方米(kg•m") |
| centerOfMass | 属性 | 质心的位置和主轴的方向 |

该结构体用于描述物体的质量属性,其中 inertia 称转动惯量,转动惯量用于描述改变物体转动状态的难易程度,转动惯量与物体质量的分布情况和转动轴位置有非常大的关系,同一物体如果质量分布情况不一样或者转动轴位置与方向不一样时转动惯量差异可能会非常大。就像质量对物体非旋转运动状态改变的阻碍一样,转动惯量对物体旋转运动状态改变也产生同样的阻碍,即转动惯量在旋转动力学中的角色相当于线性动力学中的质量,因此转动惯量可以形象地理解为一个物体对旋转运动的惯性,用于建立角动量、角速度、力矩和角加速度等数个物理量之间的关系。centerOfMass 用于描述质心的位置与主轴方向,这个参数影响物体的转动。

PhysicsBodyComponent 组件的 material 属性 PhysicsMaterialResource 类型,该属性定义了物体的物理材质属性。PhysicsMaterialResource 类包含两个方法和一个 default 属性,如表9-5所示。

表9-5 PhysicsMaterialResource 类

|---------------------------------------------------------------------------------------------------------|----|----------------------------|
| 名称 | 类型 | 描述 |
| default | 属性 | 使用默认的物理材质 |
| generate(friction: Float, restitution: Float) -> PhysicsMaterialResource | 方法 | 以指定的摩擦系数与恢复系数生成物理材质 |
| generate staticFriction: Float, dynamicFriction: Float, restitution: Float) -> PhysicsMaterialResource | 方法 | 以指定的静摩擦系数、动摩擦系数、恢复系数生成物理材质 |

PhysicsMaterialResource 类用于描述物体的物理材质属性,其中 friction 为摩擦系数(staticFriction 为静摩擦系数,dynamicFriction 为动摩擦数),取值范围为[0,∞),默认为 0.8;restitution 为恢复系数,取值范围为[0,1],默认为0.8,该属性描述的是物体反弹的程度,如一个球从1m 高的位置自由下落,恢复系数描述的是这个球能反弹的高度,默认将反弹到0.8m 的高度。

PhysicsMotionComponent 组件

PhysicsBodyComponent 组件完全按照物理学规律处理物体的运动,通过设置真实的物理参数、施加作用力进行物理模拟,由于是完全依照真实物理世界规律处理物体运动,因此不能直接设置物体的位置与速度参数。

PhysicsMotionComponent 组件则不完全拘泥于真实物理规律,可以通过设置物体的速度、角速度驱动物体运动,虽然可以直接设置非真实物理参数,但在设置完参数后,物体的行为仍然受物理引擎模拟,受物体 PhysicsBodyMode 属性影响。

当物体为静态体时,对该物体设置速度、角速度无效,物理引擎会忽略所有属性设置值,静态体也不参与物理模拟。当物体为动力学体时,物理引擎会读取开发人员设置的速度值,并相应地处理物体的运动。当物体为动态体时,物理引擎会实时地更新物体的运动状态,但直接设置速度值不会产生运动效果,直到 resetPhysicsTransform(_:recursive:)方法被调用。换言之,动态体不能通过直接设置速度的方法产生运动效果。

PhysicsMotionComponent 组件由于可以直接设置更容易理解和产生效果的速度、角速度等非基本物理参数,在某些情况下更容易达到预定效果,更方便使用。PhysicsMotionComponent 组件主要包括两个属性:linearVelocity 和 angularVelocity。 linearVelocity即促使物体直线运动的速度,而angularVelocity 是促使物体转动的角速度。

需要注意的是,在使用 PhysicsMotionComponent组件进行物理模拟时,所有模拟相对于 physiesOrigin进行(速度其实是个相对量,描述两个物体之间距离的变化程度,因此需要一个参考体),獸认 physicsOrigin为场景的原点,即世界坐标系原点,在实际使用时可以将 physicsOrigin 设置到特定的实体上。

CollisionComponent 组件

CollisionComponent 组件不属于 RealityKit 中的物理组件,但物理引擎会忽略没有挂载该组件的物体,CollisionComponent 组件直译为碰撞组件,顾名思义,碰撞组件负责处理碰撞相关事项,不带碰撞组件的实体不能参与碰撞,也不能参与物理模拟。碰撞组件实现两个关键功能,一个是定义碰撞体形状(Shape),主要目的是提高碰撞检测性能,另一个是检测并处理碰撞。CollisionComponent 组件包含3 个属性和1个初始化方法,如表9-6所示。

表9-6 CollisionComponent 组件

|-----------------------------------------------------------|----|------------------------------------------------------------------------|
| 名称 | 类型 | 描述 |
| init(shapes: [ShapeResource], mode: CollisionComponent. | 方法 | 初始化方法,设置实体碰撞属性 |
| Mode, filter: CollisionFilter) mode | 属性 | CollisionComponent. Mode 类型,用于设置碰撞器的类型,分为普通碰撞器(default)和触发器(trigger)两种 |
| filter | 属性 | CollisionFilter类型,用于设置碰撞检测过滤类型 |
| shapes | 属性 | shapeResource数组类型,用于设置碰撞器外形 |

CollisionComponent. Mode 是一个枚举,该枚举有两个枚举值(default、trigger),用于设定碰撞器为普通碰撞类型还是触发器类型。CollisionFilter 是一个用于设置碰撞检测过滤属性的结构体,该结构体主要包括4个属性,如表9-7所示。

表 9-7 CollisionFilter 结构体

|---------|------|--------------------------------------------|
| 名称 | 类型 | 描述 |
| default | 类属性 | 默认常规碰撞器 |
| sensor | 类属性 | 传感器,设置该值实体会与所有对象发生碰撞,通常用于射线检测(Raycast)和触发器 |
| group | 实例属性 | CollisionGroup 类型,用于设置碰撞器所属的分组 |
| mask | 实例属性 | CollisionGroup 类型,用于设置碰撞器所使用的掩码 |

通过设置 CollisionFilter 值,将碰撞器设置成不同分组和使用不同掩码,可以对碰撞物体进行分层处理,实现复杂大场景、多物体间的不同碰撞效果,并能提升性能。如在AR 游戏中,在主角通过发射火球攻击柽物场景,可以通过设置火球与圣物的分组或者掩码将其分割在相同的层中,在进行碰撞检测时火球就只会与怪物发生碰撞反应,而不会与队友发生碰撞。这样做,一方面在进行碰撞检测时可以只检测特定的分组,提高碰撞检测效率;另一方面,通过分组分层可以简化处理逻辑。

通过 CollisionFilter 的group 值和 mask 值设置,可以实现非常灵活、复杂的碰撞检测功能,并能有效地提升碰撞检测效率。ShapeResource 是一个用于定义碰撞器形状的类,碰撞器形状定义了物体发生碰撞时的外观。为提高检测效率,碰撞并不是以模型的几何网格为边界进行检查,而是依据碰撞器形状进行检测,模型几何网格可能会非常复杂,通过定义简单的能满足需要的碰撞器形状,可以大幅提高碰撞检测效率。在 RealityKit 中,ShapeResource 类可以生成如表9-8所示形状。

表9-8 ShapeResource 类可生成的形状

|-------------------|----------------------------------|
| 方法 | 描述 |
| offsetBy() | 3个重载,平移或者旋转物体的碰撞器形状,使其更符合物体的几何外观 |
| generateBox) | 2个重载,根据指定的尺寸生成立方体或长方体形状 |
| generateSphere () | 根据指定的半径生成球体形状 |
| generateCapsule() | 根据指定的高与半径生成胶囊体形状 |
| generateConvex() | 根据指定的点或者网格生成形状 |

相关推荐
missmisslulu1 天前
电容笔值得买吗?2024精选盘点推荐五大惊艳平替电容笔!
学习·ios·电脑·平板
GEEKVIP1 天前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
GEEKVIP1 天前
如何在 Windows 10 上恢复未保存/删除的 Word 文档
macos·ios·智能手机·电脑·word·笔记本电脑·iphone
奇客软件1 天前
iPhone使用技巧:如何恢复变砖的 iPhone 或 iPad
数码相机·macos·ios·电脑·笔记本电脑·iphone·ipad
奇客软件2 天前
如何从相机的记忆棒(存储卡)中恢复丢失照片
深度学习·数码相机·ios·智能手机·电脑·笔记本电脑·iphone
GEEKVIP2 天前
如何修复变砖的手机并恢复丢失的数据
macos·ios·智能手机·word·手机·笔记本电脑·iphone
一丝晨光2 天前
继承、Lambda、Objective-C和Swift
开发语言·macos·ios·objective-c·swift·继承·lambda
GEEKVIP3 天前
iPhone/iPad技巧:如何解锁锁定的 iPhone 或 iPad
windows·macos·ios·智能手机·笔记本电脑·iphone·ipad
KWMax3 天前
RxSwift系列(二)操作符
ios·swift·rxswift
Mamong3 天前
Swift并发笔记
开发语言·ios·swift