mcpField
R
roblox-physics

roblox-physics

github.com/el4cteo开发61574 次安装2 周前更新
代码仓库

关于 roblox-physics

面向 Roblox 游戏开发者的物理技能,涵盖空间查询、碰撞处理和网络所有权。内容介绍当前 API、服务器权威模拟模式,以及稳定性和性能注意事项。

主要功能

  1. 01射线投射与空间重叠查询
  2. 02碰撞摘要与碰撞组
  3. 03装配体、关节与运动约束
  4. 04网络所有权控制
  5. 05服务器权威模拟模式
  6. 06物理稳定性指南

适用场景

  1. 01实现命中检测和游戏区域
  2. 02制作载具或弹射物运动
  3. 03处理碰撞冲击与特效
  4. 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
旋转AngularVelocityBodyAngularVelocity
拉向某个点LineForceRocketPropulsion
扭矩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。

评论

支持 Markdown