关于 roblox-physics
面向 Roblox 游戏开发者的物理技能,涵盖空间查询、碰撞处理和网络所有权。内容介绍当前 API、服务器权威模拟模式,以及稳定性和性能注意事项。
主要功能
- 01射线投射与空间重叠查询
- 02碰撞摘要与碰撞组
- 03装配体、关节与运动约束
- 04网络所有权控制
- 05服务器权威模拟模式
- 06物理稳定性指南
适用场景
- 01实现命中检测和游戏区域
- 02制作载具或弹射物运动
- 03处理碰撞冲击与特效
- 04设计由服务器模拟的竞技游戏
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ el4cteo/ roblox-skills --skill roblox-physics安装后在 Claude Code 中输入/roblox-physics确认已加载。
附带文件references/recipes.mdreferences/server-authority.md
详细介绍
Roblox 物理和空间查询
空间查询(游戏逻辑判断请使用这些 API,而不是 Touched)
--!strict
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {} -- e.g. { character } of the shooter
params.IgnoreWater = true
local function castRay(origin: Vector3, direction: Vector3): RaycastResult?
-- direction's length is the max distance
return workspace:Raycast(origin, direction, params)
end
local result = castRay(Vector3.new(0, 50, 0), Vector3.new(0, -100, 0))
if result then
print(result.Instance, result.Position, result.Normal, result.Material, result.Distance)
end
-- Swept volumes: thick bullets, melee arcs, ground checks with width.
local sphereHit = workspace:Spherecast(Vector3.new(0, 10, 0), 2, Vector3.new(0, 0, 50), params)
local boxHit = workspace:Blockcast(CFrame.new(0, 10, 0), Vector3.new(4, 4, 4), Vector3.new(0, 0, 50), params)
-- Volume overlap: explosions, hitboxes, zone checks.
local overlap = OverlapParams.new()
overlap.FilterType = Enum.RaycastFilterType.Exclude
overlap.MaxParts = 50
local partsInBox = workspace:GetPartBoundsInBox(CFrame.new(0, 5, 0), Vector3.new(10, 10, 10), overlap)
local partsInSphere = workspace:GetPartBoundsInRadius(Vector3.zero, 15, overlap)
print(sphereHit, boxHit, #partsInBox, #partsInSphere)
- 重复使用参数对象;更新
FilterDescendantsInstances(或params:AddToFilter(instance)), 而不是每次调用都重新创建对象。 - 设置
RespectCanCollide = true可忽略不可碰撞部件;设置CollisionGroup = "Name"可按组进行查询。 GetPartsInPart(part, params)执行精确几何重叠查询(开销较高);GetPartBoundsIn*使用边界框。- 已弃用:
FindPartOnRay*、Ray.new、FindPartsInRegion3*→ 请改用上述 API。 - 客户端查询只能看到已流式加载的部件;权威检查应在服务端运行。
Touched:了解其局限
Touched 由物理引擎触发,仅针对发生物理接触的部件;由模拟该部件的一方触发(客户端可以伪造或抑制其拥有的部件的事件),且每次接触可能触发多次。可将其用于触发器(踏板、拾取物),并添加防抖和服务端验证;命中检测和区域检测请使用空间查询。
--!strict
local Players = game:GetService("Players")
local pad = Instance.new("Part")
pad.Anchored = true
pad.Parent = workspace
local recently: { [Player]: boolean } = {}
pad.Touched:Connect(function(hit)
local character = hit:FindFirstAncestorOfClass("Model")
local player = character and Players:GetPlayerFromCharacter(character)
if not player or recently[player] then
return
end
recently[player] = true
print(`{player.Name} stepped on the pad`)
task.delay(1, function()
recently[player] = nil
end)
end)
碰撞摘要(冲击数据)
若要了解部件如何发生碰撞(冲击特效、冲击音效、根据冲击速度造成伤害),请使用
BasePart:BindToCollisionSummaries(callback),而不是 Touched。每个物理步结束后,每帧调用一次回调,并传入该部件的每次接触信息:Part0(绑定的部件)、Part1、Point、Normal(指向远离 Part0 的方向)、RelativeVelocity(求解器处理碰撞前的速度)、Area 和 Time。
- 仅在模拟该部件的位置触发(即该部件的网络所有者);不会复制任何内容。需要让其他客户端收到的数据,请通过自定义远程对象发送,并在服务端进行验证。
CanCollide = false的部件之间,或两个处于休眠状态的部件之间,不会生成摘要;非凸部件的每对碰撞可能生成多条摘要。开销取决于绑定的部件数量,而非碰撞总数。
--!strict
local function bindImpacts(part: BasePart, onImpact: (point: Vector3, speed: number) -> ()): RBXScriptConnection
return part:BindToCollisionSummaries(function(summaries: { any })
for _, summary in summaries do
local speed = math.abs(summary.RelativeVelocity:Dot(summary.Normal)) -- head-on impact speed
if speed > 10 then
onImpact(summary.Point, speed)
end
end
end)
end
return bindImpacts
碰撞组
--!strict
-- Collision group APIs now live on WorldRoot (workspace); the PhysicsService versions are superseded.
workspace:RegisterCollisionGroup("Players")
workspace:RegisterCollisionGroup("Ghosts")
workspace:CollisionGroupSetCollidable("Players", "Ghosts", false)
local ghost = Instance.new("Part")
ghost.CollisionGroup = "Ghosts" -- assign by name (CollisionGroupId/SetPartCollisionGroup are deprecated)
ghost.Parent = workspace
在服务端启动时注册各组(最多 32 个,参见 workspace:GetMaxCollisionGroups())。CanCollide = false 会禁用所有碰撞;
CanQuery = false 会使部件不参与射线检测;CanTouch = false 会禁用 Touched(节省 CPU);
NoCollisionConstraint 会禁用两个指定部件之间的碰撞。
MeshPart 碰撞形状:CollisionFidelity 可设为 Box/Hull(开销低)、Default、
PreciseConvexDecomposition(开销高),或使用带有 CollisionPrecision 滑块的 Tunable(在 Studio 中设置;脚本无法写入),以便针对每个网格调整精度与性能的权衡。
装配体和关节
- 装配体 = 刚性连接的部件(焊接、
WeldConstraint、Motor6D、刚性约束)。它会作为一个刚体进行模拟;根部件(part.AssemblyRootPart)决定网络所有权。 - 静态几何体应锚定。未锚定但通过焊接连接到已锚定部件的部件会组成已锚定的装配体。
- 在编辑时使用
WeldConstraint进行焊接(保留当前偏移);在代码中设置Part0/Part1并设置父级。 - 使用
model:PivotTo(cframe)移动整个模型。一次移动多个部件时,使用workspace:BulkMoveTo。 - 通过
AssemblyLinearVelocity/AssemblyAngularVelocity、part:ApplyImpulse()、ApplyAngularImpulse()、AssemblyMass读取或设置物理属性。Velocity/RotVelocity已弃用。 CustomPhysicalProperties = PhysicalProperties.new(density, friction, elasticity, frictionWeight, elasticityWeight)。- 单位:1 stud ≈ 0.28 m,
workspace.Gravity默认值为 196.2 stud/s²。
Mover 约束(替代 BodyMovers)
| 目标 | 约束 | 替代项 |
|---|---|---|
| 保持/驱动速度 | LinearVelocity(VelocityConstraintMode 为 Vector/Plane/Line) | BodyVelocity |
| 向某个位置移动 | AlignPosition(Mode = OneAttachment + Position、Responsiveness、MaxForce) | BodyPosition |
| 朝某个方向旋转 | AlignOrientation(CFrame/LookAtPosition) | BodyGyro |
| 恒定力 | VectorForce(RelativeTo = World) | BodyForce、BodyThrust |
| 旋转 | AngularVelocity | BodyAngularVelocity |
| 拉向某个点 | LineForce | RocketPropulsion |
| 扭矩 | Torque | — |
所有约束都需要一个 Attachment(Attachment0)。请合理设置 MaxForce/MaxTorque(除非确有需要,否则不要使用 math.huge),否则物体会被甩飞。机械约束:HingeConstraint(用于车轮、门的 Motor/Servo 执行器)、PrismaticConstraint(滑块、电梯)、SpringConstraint(悬挂)、RopeConstraint、RodConstraint、BallSocketConstraint、CylindricalConstraint(车轮 + 转向)、UniversalConstraint、RigidConstraint。
载具和投射物配方、CFrame 指南:references/recipes.md。
网络所有权
- 服务器会自动将未锚定组件的所有权分配给附近的客户端(该客户端的体验更流畅,但也能利用此机制进行作弊)。角色由其玩家拥有。
part:SetNetworkOwner(nil)= 由服务器模拟(更安全,但玩家体验会更卡);对载具驾驶员使用SetNetworkOwner(player);使用SetNetworkOwnershipAuto()恢复自动分配。在组件位于Workspace后,于服务器端对根部件调用。锚定部件没有所有者。- 所有权变更会造成卡顿;不要每帧切换。使用视口中的 Visualization options → Network owners 开关进行可视化。
服务器权威(竞技游戏)
Workspace 的 AuthorityMode = Server 会让服务器模拟所有内容,同时客户端会提前预测几帧,并在预测错误时回滚并重新模拟。要求:NextGenerationReplication、PlayerScriptsUseInputActionSystem、SignalBehavior = Deferred、UseFixedSimulation、StreamingEnabled(设置 AuthorityMode 会启用这些设置)。核心规则:
- 游戏逻辑应放在通过
RunService:BindToSimulation(fn)绑定的函数中,并置于服务器和客户端均会 require 的 ModuleScript 内。 - 客户端只能通过输入操作(以
Player为父级的 InputContexts)影响游戏,不能使用UserInputService事件。 - 通过预测实例上的属性同步自定义状态,且只能在模拟回调中写入。
- 不要缓存
AnimationTrack;每个步骤都要调用Animator:GetTrackByAnimationId()查询。 - 在服务器上使用
player:GetCameraState()(CFrame、FieldOfView、ViewportSize)读取玩家摄像机;旧版摄像机InputAction复制机制不再同步。
自 2026 年 7 月起已全面发布(可选启用)。详情和实践模式:references/server-authority.md。
稳定性提示
- 抖动:所有权冲突、约束相互作用、质量比过大(> 1:100),或每帧为未锚定部件设置 CFrame(应改用约束)。
- 甩飞:对轻质部件使用
MaxForce = math.huge、生成时碰撞体重叠、速度为 NaN/过大。 - 休眠组件会停止模拟,直到受到扰动;如有需要,可施加一个小冲量将其唤醒。
- 自适应时间步进(
Workspace.PhysicsSteppingMethod = Adaptive)可节省 CPU;精密机械应使用固定时间步进。
相关技能
roblox-security(所有权漏洞)、roblox-characters-animation、roblox-networking、roblox-performance、roblox-input。