跳到主要内容

物理实体

PhysicsBody是用于实现物理体的抽象基类。所有 *Body 类型都继承自它。根据不同的使用场景,PhysicsBody派生出多个不同类型的派生类。比如用于环境中不运动物体的StaticBody,使用引擎的物理逻辑的RigidBody,使用自定义物理逻辑的KinematicBody,以及用于模型骨骼的物理骨骼节点。

基本属性

该类相对与其父类CollisionObject,主要增加了对例外对象的处理。PhysicsBody增加了获取,设置以及移除例外对象的方法,作用是在进行物理检测计算时不去计算添加到exception数组中的对象。相关的方法是:

# 添加exception数组
void add_collision_exception_with(Node body)
# 获取exception数组
Array get_collision_exceptions()
# 移除exception数组
void remove_collision_exception_with(Node body)

不同的物理实体

根据使用场景的不同,主要分成的了如下几个派生类。

StaticBody

用于3D物理的静态物体。静态物体是不应移动的简单物体。与RigidBody相比,它们只要不动就不会消耗任何CPU资源。

此外,可以为静态物体设置恒定的线速度或角速度,因此即使它不移动,也会像移动一样影响其他物体(这对于模拟传送带或传送轮很有用)。

IdeaXR为一个模型生成对应的StaticBody节点提供了快捷操作方式,如下: create_area

RigidBody

刚体(RigidBody)是直接由物理引擎控制以模拟物理对象行为的物体。这意味着你是不直接控制刚体。为了定义物体的形状, 必须分配一个或多个Shape对象。注意,设置这些形状的位置将影响物体的重心。

刚体有 4 种行为 mode:刚体、静态、角色和运动。

  • MODE_RIGID --- 刚体模式。这是一个刚体的 "自然 "状态。它受到力的影响,可以移动、旋转,并受到用户代码的影响。

  • MODE_STATIC --- 静止模式。实体的行为就像一个StaticBody,只能通过用户代码移动。

  • MODE_CHARACTER --- 角色模式。这与刚体的行为类似,但不能旋转。

  • MODE_KINEMATIC --- 运动体模式。这个实体的行为就像一个KinematicBody,只能通过用户代码来移动。

刚体的行为可以通过设置其属性来改变,比如质量和重量。需要给刚体添加一个物理材质来调整它的摩擦力和反弹力,并设置它是否具有吸收性、粗糙度。这些属性可以在检查器中或通过代码来设置。参见RigidBodyPhysicsMaterial获取完整的属性列表和它们的效果。

有几种方法可以控制刚体的运动, 这取决于您的应用程序。

如果你只需要放置一次刚体, 例如设置它的初始位置, 你可以使用 Spatial 节点提供的方法, 例如set_global_transform()look_at()。但是, 这些方法不能每一帧都被调用, 否则物理引擎将无法正确地模拟物体的状态. 举个例子, 考虑一个刚体, 你想旋转它, 使它指向另一个对象。在实现这种行为时, 一个常见的错误是每一帧都使用look_at(),这样会破坏物理模拟。下面。

你不能使用set_global_transform()look_at()方法并不意味着你不能完全控制一个刚体。相反, 你可以通过使用 _integrate_forces()回调来控制它。在这个方法中, 你可以添加, 应用冲量, 或者设置速度, 以实现你想要的任何运动。下面的代码在物理回调中被调用,实现每帧指向一个对象的功能。

extends RigidBody

func look_follow(state, current_transform, target_position):
var up_dir = Vector3(0, 1, 0)
var cur_dir = current_transform.basis.xform(Vector3(0, 0, 1))
var target_dir = (target_position - current_transform.origin).normalized()
var rotation_angle = acos(cur_dir.x) - acos(target_dir.x)

state.set_angular_velocity(up_dir * (rotation_angle / state.get_step()))

func _integrate_forces(state):
var target_position = $my_target_spatial_node.get_global_transform().origin
look_follow(state, get_global_transform(), target_position)

IdeaXR为RigidBody的创建提供了快捷方式:点击网格类型节点,在3D视窗顶部的网格菜单中选择一键转化为刚体即可。

VehicleBody

由RigidBody派生的,用于模拟汽车行为的物理体。

此节点实现了模拟汽车所需的所有物理逻辑。它是基于物理引擎中常见的射线投射的车辆系统。你需要为车的主体添加一个CollisionShape,并为车轮添加VehicleWheel节点。还应该为汽车的三维模型添加一个MeshInstance节点,但这个模型不应该包括车轮的网格。你应该通过使用brake、engine_force和steering 属性来控制车辆,而不是直接改变这个节点的位置或方向。可以参考新建项目模板里的车辆驾驶项目。

note

VehicleBody的原点将决定你的车辆的重心,所以最好保持低位,并将 CollisionShape 和 MeshInstance 往上移。

KinematicBody

KinematicBody用于实现通过代码控制的物体。运动体在移动时可以检测到与其他物体的碰撞, 但不受引擎物理属性的影响, 比如重力或摩擦。虽然这意味着你必须编写一些代码来创建它们的行为, 但这也意味着你可以更精确地控制它们如何移动和反应。

note

KinematicBody可以受到重力和其他力的影响, 但您必须在代码中计算它的运动. 物理引擎不会移动KinematicBody。

当移动一个KinematicBody2D时, 你不应该直接设置它的position属性, 而是使用move_and_collide()move_and_slide()方法. 这些方法沿着给定的向量移动物体, 如果检测到与另一个体发生碰撞, 则立即停止. 在KinematicBody2D发生碰撞后, 任何碰撞响应必须手动编码。

caution

你应该只在_physics_process()回调中做Kinematic物体运动。

move_and_collide()move_and_slide()方法使用的场景会不一样,主要有两点区别,一是move_and_collide在碰撞后会停止,而move_and_slide会滑动,二是move_and_collide的速度需要速度向量乘以帧时间步长,而move_and_slide需要以秒为单位速度向量。整体而言,move_and_slide()方法使用更简单,move_and_collide()控制更精确。两个方法都有很多参数,可以参考API文档设置不同的参数达到不同的效果,比如爬坡。

检测碰撞同样通过上面的两个函数来实现,不过获取碰撞到的对象会有所不同。当使用move_and_collide()时, 函数直接返回一个KinematicCollision, 你可以在代码中使用这个对象。当使用move_and_slide()时, 有可能发生多次碰撞, 因为滑动响应是计算出来的。要处理这些碰撞, 使用get_slide_count()get_slide_collision()

此外,该类型还有一些使用的方法,比如is_on_floor()判断是否在地板上。

PhysicalBone

物理骨骼主要使用场景是布娃娃系统。布娃娃系统依靠物理模拟来创建逼真的程序动画。比如角色死亡时倒下的随重力倒下的动画,比如布娃娃手臂甩动的效果。通过下面的方式可以创建物理骨骼节点。 create_area 创建完节点后,回生成与骨骼对应的PhysicalBone以及碰撞形状。根据需要删除不需要的节点,调整不合适的参数设置即可。 启用布娃娃模拟是在Skeleton节点上调用physical_bones_start_simulation方法,结束模拟调用physical_bones_stop_simulation方法。