Turbulenz 引擎物理约束对象(Constraint)API 详解:五类刚体约束的创建、属性与源码实现 游戏开发图形学【免费下载链接】turbulenz_engineTurbulenz is a modular 3D and 2D game framework for making HTML5 powered games for browsers, desktops and mobile devices.项目地址https://gitcode.com/gh_mirrors/tu/turbulenz_engine点击查看免费下载在 Turbulenz 引擎中Constraint约束对象用于在两个刚体RigidBody之间建立物理连接是构建门、车轮、摇臂、链条乃至 Ragdoll 角色等交互结构的基础设施。本文基于 constraint_api.rst 完整梳理 Constraint 对象的通用属性与 Point2Point、Hinge、ConeTwist、6DOF、Slider 五类约束的全部参数语义并结合 physicsdevice.ts 源码与 physics_constraints.ts 示例讲解从「创建约束」到「加入物理世界求解」的完整链路。读完本文你将掌握每种约束的适用场景、属性取值规则含弧度单位与锁定/自由/受限语义以及默认行为能够直接在自有场景中复现和调优。Constraint 对象是什么连接两个刚体的物理关节官方文档对 Constraint 对象的定义非常精炼一个 Constraint 对象表示两个刚体之间的物理约束。它并不直接参与碰撞检测而是通过限制两个刚体之间的相对自由度平移与旋转把刚体绑在指定的局部坐标系frame of reference上由物理求解器在每次世界更新时计算并施加约束力。在 Turbulenz 中约束对象不能直接new出来必须通过PhysicsDevice上对应的工厂方法构造详见 physicsdevice_api.rst约束类型工厂方法引擎内部 type 值源码确认Point to Point点到点createPoint2PointConstraintPOINT2POINTHinge铰链/转轴createHingeConstraintHINGECone Twist锥形扭转createConeTwistConstraintCONETWIST6 Degrees of Freedom六自由度create6DOFConstraintD6Slider滑轨createSliderConstraintSLIDER从源码看这五个工厂方法统一收敛到WebGLPhysicsConstraint.create(type, params)与WebGLPhysicsPoint2PointConstraint.create(params)两个入口physicsdevice.ts#L10875-L10906其中 Hinge、ConeTwist、6DOF、Slider 以字符串常量标记类型createHingeConstraint(params) : PhysicsHingeConstraint { return PhysicsHingeConstraintany (WebGLPhysicsConstraint.create(HINGE, params)); }对应地turbulenz.d.ts#L955-L1001 中定义了PhysicsConstraint基接口与各类型扩展接口是编写 TypeScript 代码时的权威类型依据。通用属性bodyA、bodyB、transformA、transformB、type所有约束类型共享以下五个属性。bodyA / bodyB只读bodyA约束作用的第一个刚体The first body the constraint applies to。bodyB约束作用的第二个刚体The second body the constraint applies to。两者均为只读属性在创建时通过params.bodyA/params.bodyB指定。源码中的实现值得注意initConstraintProperties使用Object.defineProperty把bodyA/bodyB定义成只读、可枚举属性同时将内部引用写入私有对象physicsdevice.ts#L3729-L3748var pc c._private; pc.bodyA params.bodyA._private; Object.defineProperty(c, bodyA, { value : params.bodyA, enumerable : true }); // bodyB 允许为空pc.bodyB params.bodyB ? params.bodyB._private : null;也就是说bodyB是可省略的。当只传入bodyA时引擎会把世界空间当作另一个物体参与约束——这在官方文档的 Hinge、ConeTwist 创建说明中被明确表述为 If only one body is specified then world space is used for the other transform例如把门铰接到静止的世界框架上见 physicsdevice_api.rst#L524-L552。transformA / transformB可读写仅 Slider 与 D6 可修改transformA以Matrix43对象表示的、约束在 bodyA 上的参考系frame of reference。transformB同理约束在 bodyB 上的参考系。它们的典型用法是读取当前 frame或整体重置为单位矩阵// 获取当前 frame var matrix constraint.transformA; // 设置为原点单位矩阵 constraint.transformA mathDevice.m43BuildIdentity();需要注意两条约束规则transformA/transformB只在 Slider 与 D6 两类约束上可被修改It can only be modified for constraints of type slider or D6Point2Point 约束上不存在这两个属性Not present on the Point2Point constraint——点到点约束只关心两个局部枢轴点pivotA/pivotB不需要显式的参考系矩阵。type只读type约束类型的名称只读。例如POINT2POINT、HINGE、CONETWIST、D6、SLIDER见上表枚举值来自源码。var constraintTypeName constraint.type;Point2Point 约束属性局部枢轴点与软约束参数点到点约束把两个刚体各自的局部点pivot约束到同一世界位置官方文档总结为limits the translation so that the local pivot points of 2 rigidbodies match in worldspace限制平移使两个刚体的局部枢轴点在世界中重合。它可以串联多个刚体形成链条若只指定一个刚体则使用该刚体的质心作为另一个枢轴见 physicsdevice_api.rst#L463-L519。文档口径说明本文所依据的 constraint_api.rst 在 Point2Point Properties 一节将两个枢轴属性写作pointA/pointB而 physicsdevice_api.rst 的创建参数与 turbulenz.d.ts 的类型定义、以及 physicsdevice.ts 的实现字段均为pivotA/pivotB。两者指代同一概念bodyA/bodyB 上的局部枢轴点下文以源码实际名称为准同时保留文档写法说明。pivotA / pivotBpivotAVector3值表示 bodyA 上局部枢轴点local pivot point on bodyA。pivotBVector3值表示 bodyB 上局部枢轴点local pivot point on bodyB。读写示例// 获取当前 pivotA var pivotA constraint.pivotA; // 为 bodyA 设置新的枢轴点 constraint.pivotA mathDevice.v3Build(0, 0, 1);源码实现中pivotA/pivotB分别写入私有数据数组的data[0..2]与data[3..5]getter 通过VMath.v3Build返回副本physicsdevice.ts#L3825-L3876。一个重要的默认行为是当params.pivotB未提供时引擎会用 bodyA 的变换把pivotA变换到世界空间作为 pivotB 的初值从而保证初始位置误差为零if (params.pivotB) { data[3] params.pivotB[0]; ... } else { var pivotB VMath.m43TransformPoint(pc.bodyA.transform, params.pivotA); data[3] pivotB[0]; ... }force最大力对应 Bullet 文档中的 tauforce标量表示约束的最大力maximum force for the constraint文档注明其与 Bullet 物理文档中的tau同义。var force constraint.force; constraint.force (2.0 * force); // 翻倍damping阻尼damping标量表示约束的阻尼damping of the constraint。var damping constraint.damping; constraint.damping (0.5 * damping); // 减半impulseClamp冲量钳制impulseClamp标量表示约束的冲量钳制值0.0表示不钳制no clamp exists。var clamp constraint.impulseClamp; constraint.impulseClamp 2.0;源码确认的默认值physicsdevice.ts#L3878-L3918属性默认值语义force0.3最大约束力taudamping1.0阻尼系数impulseClamp0.00 表示对冲量不做钳制impulseClamp的实际作用体现在求解阶段computeAndApplyImpulses会计算累积冲量的平方长度jlsq若超过clamp * clamp则整体缩放physicsdevice.ts#L4172-L4184var clamp data[32]; if (clamp ! 0) { var jlsq (jAcc0 * jAcc0) (jAcc1 * jAcc1) (jAcc2 * jAcc2); if (jlsq clamp * clamp) { jlsq clamp / Math.sqrt(jlsq); jAcc0 * jlsq; jAcc1 * jlsq; jAcc2 * jlsq; } }在 mouseforces.ts#L95-L130 中可以看到一个非常典型的实战用法鼠标拾取并拖拽刚体时临时创建一个只有bodyApivotA的点到点约束把拾取点约束到世界并传入force、damping: 0.5与impulseClamp: this.clamp来控制拖拽力度与稳定性松开鼠标时dynamicsWorld.removeConstraint(this.pickConstraint)移除该约束。Hinge 约束属性旋转下限与上限Hinge铰链/转动副约束限制额外的两个转动自由度使刚体只能绕单一铰链轴旋转官方文档给出的典型场景是门或绕轴旋转的车轮doors or wheels rotating around one axis且可以指定旋转角度限制见 physicsdevice_api.rst#L524-L575。low / highlow标量表示约束旋转角的下限lower limit单位是弧度radians。high标量表示约束旋转角的上限upper limit单位同样是弧度。// 读取当前下限 var lowerLimit constraint.low; // 设置为 90° constraint.low Math.PI / 2; // 读取当前上限 var upperLimit constraint.high; // 设置为 180° constraint.high Math.PI;类型声明中 Hinge 约束仅有low/high两个扩展属性turbulenz.d.ts#L973-L977。示例场景中门的铰链即设定为low: 0, high: pi * (5 / 6)150 度来模拟门框的开合范围physics_constraints.ts#L359-L369。ConeTwist 约束属性摆动锥与扭转角ConeTwist 约束是构建 Ragdoll 的利器——官方文档明确指出它对上肢upper arm等肢体非常有用To create ragdolls, the cone twist constraint is very useful for limbs like the upper arm。它本质上是带锥形摆动限制与扭转轴限制的特殊点到点约束x 轴即扭转轴见 physicsdevice_api.rst#L580-L640。swingSpan1 / swingSpan2swingSpan1标量弧度用于在一个轴向上形成椭圆/锥形的摆动角度。swingSpan2标量弧度用于在另一个轴向上形成椭圆/锥形的摆动角度。两个值共同决定摆动锥在两个正交方向上的开角大小原文the angle used to form the ellipsis in one axis。var swingSpan1 constraint.swingSpan1; constraint.swingSpan1 Math.PI / 2; var swingSpan2 constraint.swingSpan2; constraint.swingSpan2 Math.PI / 2;twistSpan / twistAngletwistSpan标量弧度表示绕 x 轴旋转的限制limit of the rotation around the x-axis。twistAngle标量弧度表示刚体当前能绕 x 轴转到的角度只读Read Only。// 设置扭转限制 constraint.twistSpan Math.PI / 2; // 读取当前扭转角只读 var twistAngle constraint.twistAngle;类型声明 turbulenz.d.ts#L979-L985 与之完全对应twistAngle无 setter。示例中createConeTwistConstraint传入swingSpan1: (pi / 4) * 0.6、swingSpan2: pi / 4、twistSpan: pi * 0.7、damping: 5.0来约束上臂摆动physics_constraints.ts#L517-L527并预先用m43FromRT构造两个参考系矩阵作为transformA/transformB。6DOF 约束属性六自由度极限向量6DOF六自由度约束是通用型约束官方文档称其可以通过配置 6 个自由度dof来模拟各种标准约束前 3 个是线性轴刚体的平移后 3 个是角运动轴每个轴都可以是锁定、自由或受限的。新建的 6DOF 约束所有轴默认锁定之后可重新配置同时文档警告包含自由/受限角自由度的若干组合是未定义行为见 physicsdevice_api.rst#L645-L707。linearLowerLimit / linearUpperLimitlinearLowerLimitVector3每个分量为对应轴的平移下限。linearUpperLimitVector3每个分量为对应轴的平移上限。var linearLowerLimit constraint.linearLowerLimit; constraint.linearLowerLimit mathDevice.v3Build(1, 1, 1); var linearUpperLimit constraint.linearUpperLimit; constraint.linearUpperLimit mathDevice.v3Build(1, 1, 1);angularLowerLimit / angularUpperLimitangularLowerLimitVector3每个分量表示对应轴的角运动下限弧度。angularUpperLimitVector3每个分量表示对应轴的角运动上限弧度。constraint.angularLowerLimit mathDevice.v3Build(-Math.PI, -Math.PI, -Math.PI); constraint.angularUpperLimit mathDevice.v3Build(Math.PI, Math.PI, Math.PI);每个轴的三种状态判定规则务必记住官方文档给出了每个轴状态的精确判定条件physicsdevice_api.rst#L660-L666对每个轴 Lowerlimit Upperlimit - 轴被锁定locked Lowerlimit Upperlimit - 轴自由free Lowerlimit Upperlimit - 轴在该范围内受限limited示例中 6DOF 约束使用linearLowerLimit: v3Build(-4, -2, -2)、linearUpperLimit: v3Build(4, 2, 2)形成平移盒用angularLowerLimit: v3Build(-pi/2, -0.75, -pi/2)、angularUpperLimit: v3Build(pi/2, 0.75, pi/2)限制角运动范围physics_constraints.ts#L578-L587。Slider 约束属性滑轨的标量极限Slider 约束允许刚体绕 x 轴旋转并沿 x 轴平移allows the body to rotate around the x-axis and translate along this axis见 physicsdevice_api.rst#L712-L756。与 6DOF 不同Slider 的四个极限属性都是标量且全部作用于 x 轴。linearLowerLimit / linearUpperLimitlinearLowerLimit标量表示x 轴的平移下限。linearUpperLimit标量表示x 轴的平移上限。var linearLowerLimit constraint.linearLowerLimit; constraint.linearLowerLimit -3.0; var linearUpperLimit constraint.linearUpperLimit; constraint.linearUpperLimit 3.0;angularLowerLimit / angularUpperLimitangularLowerLimit标量弧度表示x 轴的角运动下限。angularUpperLimit标量弧度表示x 轴的角运动上限。constraint.angularLowerLimit -(Math.PI / 2); constraint.angularUpperLimit Math.PI / 2;类型声明 turbulenz.d.ts#L995-L1001 与此一致。从创建到求解源码视角的完整链路创建与属性注入所有约束构造完成后都要经过initConstraintProperties统一注入userData、bodyA、bodyB以及可运行时切换的active属性physicsdevice.ts#L3729-L3792active默认true约束已加入世界时把active置为true会调用pc.world.wakeConstraint(pc)唤醒约束置为false则从世界活跃约束列表activeConstraints中移除并强制其休眠。这正是示例渲染循环中technique3d.constantColor (body.active) ? color : desaturate(color, 0.5)判断的底层来源physics_constraints.ts#L720-L723。私有数据布局与软约束求解Point2Point 约束的求解数据保存在一个Float32Array(46)中physicsdevice.ts#L3939-L3957[0, 3) pivotA [3, 6) pivotB [6, 9) relA [9, 12) relB [12, 30) skewA/skewB3x3 矩阵 [30, 31) force [31, 32) damping [32, 33) clamp [33, 34) gamma [34, 40) K对称 3x3 有效质量矩阵 [40, 43) jAcc累积冲量 [43, 46) bias位置误差偏置preStepphysicsdevice.ts#L3959-L4094中force与damping被换算为软约束参数omega (2 / timeStep * force * damping) / (1 - force)并进一步计算gamma 1 - gk * ig作为累积冲量的阻尼系数——这与文档中force即 Bullet 的tau、damping即阻尼的说法完全对应也解释了为什么这两个参数会共同影响约束的软硬与收敛速度。computeAndApplyImpulsesphysicsdevice.ts#L4133-L4217则完成有效质量矩阵求逆、位置/速度误差补偿、冲量累积与钳制并把冲量施加到两个刚体的线速度与角速度上。加入与移出物理世界约束创建后必须通过物理世界的接口管理生命周期见 turbulenz.d.ts#L1025-L1047dynamicsWorld.addConstraint(constraint); // 加入求解 dynamicsWorld.removeConstraint(constraint); // 移除对应实现位于 physicsdevice.ts#L7529-L7538最终转发到内部世界的_private.addConstraint / removeConstraint。实战模板一个场景同时演示五种约束仓库中的 physics_constraints.ts对应页面 physics_constraints.html在一个场景中完整演示了全部五种约束是理解本文所有属性最好的可运行样例其布局如下Slider两个立方体沿两点连线方向摆放先通过v3Normalize(v3Sub(...))计算方向向量再用m43FromAxisRotation把 x 轴旋转到该方向构造transformA/transformB后设置linearLowerLimit: 1.2、linearUpperLimit: 8、angularLowerLimit/UpperLimit: 0physics_constraints.ts#L275-L335Hinge用m43FromAxisRotation(yaxis, pi * 5 / 4)旋转门体参考系 160 度模拟门框low: 0、high: 150°physics_constraints.ts#L337-L369Point2Point两个活动立方体通过pivotA/pivotB与固定体两两相连其中pivotInB用m43InverseOrthonormalm43TransformVector把 bodyA 的局部枢轴变换进 bodyB 的物体空间physics_constraints.ts#L371-L463ConeTwist用quatBuildm43FromRT构造两个不同位置的参考系设置swingSpan1/2、twistSpan与dampingphysics_constraints.ts#L465-L5276DOF平移盒±(4, 2, 2)与角范围(-π/2, -0.75, -π/2) ~ (π/2, 0.75, π/2)physics_constraints.ts#L529-L589。场景同时启用了 debugphysics.ts 的DebugConstraint以线框绘制各约束的限制范围drawSliderConstraintLimits、drawHingeConstraintLimits、drawP2PConstraintLimits、drawConeTwistConstraintLimits、draw6DOFConstraintLimits见 physics_constraints.ts#L766-L774非常适合直观验证参数修改后的物理表现配合 mouseforces.ts 的拖拽交互还可以在运行中感受约束力与稳定性。小结选型速查约束核心属性典型场景Point2PointpivotA/pivotB、force、damping、impulseClamp链条、布娃娃骨骼点、鼠标拾取拖拽Hingelow、high门、转轴、车轮ConeTwistswingSpan1、swingSpan2、twistSpan、twistAngle(只读)Ragdoll 上肢等锥形摆动关节6DOF四个Vector3极限锁定/自由/受限需要逐轴定制自由度的通用关节Slider四个 x 轴标量极限滑轨、伸缩机构编写约束代码时请记住三条红线所有角度属性一律使用弧度6DOF 轴状态由「下限上限 / 下限上限 / 下限上限」三规则决定transformA/transformB仅 Slider 与 D6 可写、Point2Point 上不存在。掌握了这些属性语义与源码中的默认行为你就可以在 Turbulenz 的物理世界中稳定地搭出各类机械结构与角色关节。赞分享游戏开发图形学【免费下载链接】turbulenz_engineTurbulenz is a modular 3D and 2D game framework for making HTML5 powered games for browsers, desktops and mobile devices.项目地址https://gitcode.com/gh_mirrors/tu/turbulenz_engine点击查看免费下载相关推荐Turbulenz Engine 3D物理系统详解从刚体到约束的完整教程Turbulenz Engine 3D物理系统详解从刚体到约束的完整教程 想要为你的HTML5游戏创建逼真的物理效果吗Turbulenz Engine的3D游戏开发图形学Turbulenz Physics2DDevice 深度解析2D 物理工厂的 World、刚体、材质与 8 种约束实现Turbulenz Physics2DDevice 深度解析2D 物理工厂的 World、刚体、材质与 8 种约束实现 本文基于 Turbulenz 引擎官方游戏开发图形学Awesome Wayland状态栏和启动器打造个性化Linux桌面的10个技巧Awesome Wayland状态栏和启动器打造个性化Linux桌面的10个技巧 Wayland作为现代Linux桌面的显示协议正在逐步取代传统的X11。本创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考