roblox-physics

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Roblox Physics & Constraints

Roblox 物理与约束

Use this skill when building physics-driven gameplay: vehicles, ragdolls, projectiles, mechanical contraptions, elevators, swinging platforms, or anything using constraints and forces.
当构建由物理驱动的玩法时使用本技能:载具、布娃娃系统、投射物、机械装置、电梯、摆动平台,或任何使用约束与力的内容。

Constraint Types

约束类型

Mechanical Constraints

机械约束

ConstraintWhat it doesUse for
HingeConstraint
Rotation around one axisDoors, wheels, pendulums, flaps
PrismaticConstraint
Slide along one axisElevators, pistons, sliding doors
CylindricalConstraint
Rotate + slide on one axisTelescoping arms, drill bits
BallSocketConstraint
Free rotation (3 DOF)Ragdoll joints, chains, wrecking balls
UniversalConstraint
2-axis rotation (no twist)Steering columns, gimbal joints
WeldConstraint
Rigid connectionAttach parts permanently
RigidConstraint
Rigid (like Weld but with offset)Precise attachment with maintained offset
约束类型功能适用场景
HingeConstraint
绕单轴旋转门、车轮、钟摆、挡板
PrismaticConstraint
沿单轴滑动电梯、活塞、滑动门
CylindricalConstraint
单轴旋转+滑动伸缩臂、钻头
BallSocketConstraint
自由旋转(3自由度)布娃娃关节、链条、破坏球
UniversalConstraint
双轴旋转(无扭转)转向柱、万向节
WeldConstraint
刚性连接永久附着部件
RigidConstraint
刚性连接(类似Weld但支持偏移)保持偏移的精确附着

Motion Constraints

运动约束

ConstraintWhat it doesUse for
AlignPosition
Move toward target positionFloating platforms, magnetic attraction
AlignOrientation
Rotate toward target orientationAuto-leveling, look-at behavior
LinearVelocity
Constant velocity in directionConveyor belts, moving platforms
AngularVelocity
Constant rotation speedSpinning obstacles, fans
VectorForce
Apply constant forceGravity modification, thrust
Torque
Apply constant torqueSpinning objects
约束类型功能适用场景
AlignPosition
向目标位置移动悬浮平台、磁力吸引
AlignOrientation
向目标方向旋转自动调平、朝向行为
LinearVelocity
沿方向保持恒定速度传送带、移动平台
AngularVelocity
保持恒定旋转速度旋转障碍物、风扇
VectorForce
施加恒定力重力修改、推力
Torque
施加恒定扭矩旋转物体

Spring/Rope

弹簧/绳索

ConstraintWhat it doesUse for
SpringConstraint
Bouncy connectionSuspension, trampolines, bouncy bridges
RopeConstraint
Max distance (slack allowed)Grappling hooks, hanging objects
RodConstraint
Fixed distance (rigid)Rigid linkages, pendulum arms
约束类型功能适用场景
SpringConstraint
弹性连接悬挂系统、蹦床、弹性桥梁
RopeConstraint
最大距离(允许松弛)抓钩、悬挂物体
RodConstraint
固定距离(刚性)刚性连杆、钟摆臂

Attachment Pattern

附着点模式

All constraints connect via Attachments, not Parts directly:
luau
local function connectHinge(part0: BasePart, part1: BasePart, pivotOffset: Vector3)
    local att0 = Instance.new("Attachment")
    att0.Position = pivotOffset
    att0.Parent = part0

    local att1 = Instance.new("Attachment")
    att1.Position = Vector3.new(0, 0, 0) -- at part1's origin
    att1.Parent = part1

    local hinge = Instance.new("HingeConstraint")
    hinge.Attachment0 = att0
    hinge.Attachment1 = att1
    hinge.ActuatorType = Enum.ActuatorType.Motor -- or None, Servo
    hinge.MotorMaxTorque = 1000
    hinge.AngularVelocity = 5 -- rad/s
    hinge.Parent = part0

    return hinge
end
所有约束通过Attachment而非直接连接Part:
luau
local function connectHinge(part0: BasePart, part1: BasePart, pivotOffset: Vector3)
    local att0 = Instance.new("Attachment")
    att0.Position = pivotOffset
    att0.Parent = part0

    local att1 = Instance.new("Attachment")
    att1.Position = Vector3.new(0, 0, 0) -- at part1's origin
    att1.Parent = part1

    local hinge = Instance.new("HingeConstraint")
    hinge.Attachment0 = att0
    hinge.Attachment1 = att1
    hinge.ActuatorType = Enum.ActuatorType.Motor -- or None, Servo
    hinge.MotorMaxTorque = 1000
    hinge.AngularVelocity = 5 -- rad/s
    hinge.Parent = part0

    return hinge
end

Vehicles

载具

Basic Car (4 wheels + body)

基础汽车(4轮+车身)

luau
local function createWheel(chassis: BasePart, offset: Vector3, steer: boolean): HingeConstraint
    local wheel = Instance.new("Part")
    wheel.Shape = Enum.PartType.Cylinder
    wheel.Size = Vector3.new(1, 3, 3) -- width, diameter, diameter
    wheel.CFrame = chassis.CFrame * CFrame.new(offset) * CFrame.Angles(0, 0, math.pi/2)
    wheel.CustomPhysicalProperties = PhysicalProperties.new(1, 0.5, 0, 1, 1)
    wheel.Parent = chassis.Parent

    -- Suspension (spring between chassis and wheel)
    local springAtt0 = Instance.new("Attachment")
    springAtt0.Position = offset + Vector3.new(0, 1, 0)
    springAtt0.Parent = chassis

    local springAtt1 = Instance.new("Attachment")
    springAtt1.Parent = wheel

    local spring = Instance.new("SpringConstraint")
    spring.Attachment0 = springAtt0
    spring.Attachment1 = springAtt1
    spring.FreeLength = 2
    spring.Stiffness = 5000
    spring.Damping = 200
    spring.Parent = chassis

    -- Axle (hinge for rotation)
    local axleAtt0 = Instance.new("Attachment")
    axleAtt0.Position = offset
    axleAtt0.Parent = chassis

    local axleAtt1 = Instance.new("Attachment")
    axleAtt1.Parent = wheel

    local hinge = Instance.new("HingeConstraint")
    hinge.Attachment0 = axleAtt0
    hinge.Attachment1 = axleAtt1
    hinge.ActuatorType = Enum.ActuatorType.Motor
    hinge.MotorMaxTorque = 500
    hinge.AngularVelocity = 0 -- controlled by input
    hinge.Parent = chassis

    return hinge
end
luau
local function createWheel(chassis: BasePart, offset: Vector3, steer: boolean): HingeConstraint
    local wheel = Instance.new("Part")
    wheel.Shape = Enum.PartType.Cylinder
    wheel.Size = Vector3.new(1, 3, 3) -- width, diameter, diameter
    wheel.CFrame = chassis.CFrame * CFrame.new(offset) * CFrame.Angles(0, 0, math.pi/2)
    wheel.CustomPhysicalProperties = PhysicalProperties.new(1, 0.5, 0, 1, 1)
    wheel.Parent = chassis.Parent

    -- Suspension (spring between chassis and wheel)
    local springAtt0 = Instance.new("Attachment")
    springAtt0.Position = offset + Vector3.new(0, 1, 0)
    springAtt0.Parent = chassis

    local springAtt1 = Instance.new("Attachment")
    springAtt1.Parent = wheel

    local spring = Instance.new("SpringConstraint")
    spring.Attachment0 = springAtt0
    spring.Attachment1 = springAtt1
    spring.FreeLength = 2
    spring.Stiffness = 5000
    spring.Damping = 200
    spring.Parent = chassis

    -- Axle (hinge for rotation)
    local axleAtt0 = Instance.new("Attachment")
    axleAtt0.Position = offset
    axleAtt0.Parent = chassis

    local axleAtt1 = Instance.new("Attachment")
    axleAtt1.Parent = wheel

    local hinge = Instance.new("HingeConstraint")
    hinge.Attachment0 = axleAtt0
    hinge.Attachment1 = axleAtt1
    hinge.ActuatorType = Enum.ActuatorType.Motor
    hinge.MotorMaxTorque = 500
    hinge.AngularVelocity = 0 -- controlled by input
    hinge.Parent = chassis

    return hinge
end

Vehicle Input (server-authoritative)

载具输入(服务器权威)

luau
-- Server: receive input, apply to constraints
local DriveRemote = Instance.new("RemoteEvent")
DriveRemote.Name = "Drive"
DriveRemote.Parent = ReplicatedStorage

DriveRemote.OnServerEvent:Connect(function(player, throttle: number, steer: number)
    -- Validate
    throttle = math.clamp(throttle, -1, 1)
    steer = math.clamp(steer, -1, 1)

    local vehicle = getPlayerVehicle(player)
    if not vehicle then return end

    -- Apply throttle to rear wheels
    for _, hinge in vehicle.rearWheels do
        hinge.AngularVelocity = throttle * MAX_SPEED
    end

    -- Apply steering to front wheels
    for _, servo in vehicle.frontSteering do
        servo.TargetAngle = steer * MAX_STEER_ANGLE
    end
end)
luau
-- Server: receive input, apply to constraints
local DriveRemote = Instance.new("RemoteEvent")
DriveRemote.Name = "Drive"
DriveRemote.Parent = ReplicatedStorage

DriveRemote.OnServerEvent:Connect(function(player, throttle: number, steer: number)
    -- Validate
    throttle = math.clamp(throttle, -1, 1)
    steer = math.clamp(steer, -1, 1)

    local vehicle = getPlayerVehicle(player)
    if not vehicle then return end

    -- Apply throttle to rear wheels
    for _, hinge in vehicle.rearWheels do
        hinge.AngularVelocity = throttle * MAX_SPEED
    end

    -- Apply steering to front wheels
    for _, servo in vehicle.frontSteering do
        servo.TargetAngle = steer * MAX_STEER_ANGLE
    end
end)

Ragdoll

布娃娃系统

Activate Ragdoll (replace Motor6Ds with BallSockets)

激活布娃娃系统(用BallSocket替换Motor6D)

luau
local function enableRagdoll(character: Model)
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    humanoid:ChangeState(Enum.HumanoidStateType.Physics)

    for _, motor in character:GetDescendants() do
        if motor:IsA("Motor6D") and motor.Name ~= "Root" then -- keep Root for HRP
            local att0 = Instance.new("Attachment")
            att0.CFrame = motor.C0
            att0.Parent = motor.Part0

            local att1 = Instance.new("Attachment")
            att1.CFrame = motor.C1
            att1.Parent = motor.Part1

            local socket = Instance.new("BallSocketConstraint")
            socket.Attachment0 = att0
            socket.Attachment1 = att1
            socket.LimitsEnabled = true
            socket.UpperAngle = 45 -- prevent unnatural bending
            socket.Parent = motor.Part0

            motor.Enabled = false
        end
    end
end

local function disableRagdoll(character: Model)
    local humanoid = character:FindFirstChildOfClass("Humanoid")

    -- Remove sockets, re-enable motors
    for _, obj in character:GetDescendants() do
        if obj:IsA("BallSocketConstraint") then
            obj:Destroy()
        elseif obj:IsA("Motor6D") then
            obj.Enabled = true
        end
    end

    humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
end
luau
local function enableRagdoll(character: Model)
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    humanoid:ChangeState(Enum.HumanoidStateType.Physics)

    for _, motor in character:GetDescendants() do
        if motor:IsA("Motor6D") and motor.Name ~= "Root" then -- keep Root for HRP
            local att0 = Instance.new("Attachment")
            att0.CFrame = motor.C0
            att0.Parent = motor.Part0

            local att1 = Instance.new("Attachment")
            att1.CFrame = motor.C1
            att1.Parent = motor.Part1

            local socket = Instance.new("BallSocketConstraint")
            socket.Attachment0 = att0
            socket.Attachment1 = att1
            socket.LimitsEnabled = true
            socket.UpperAngle = 45 -- prevent unnatural bending
            socket.Parent = motor.Part0

            motor.Enabled = false
        end
    end
end

local function disableRagdoll(character: Model)
    local humanoid = character:FindFirstChildOfClass("Humanoid")

    -- Remove sockets, re-enable motors
    for _, obj in character:GetDescendants() do
        if obj:IsA("BallSocketConstraint") then
            obj:Destroy()
        elseif obj:IsA("Motor6D") then
            obj.Enabled = true
        end
    end

    humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
end

Projectiles

投射物

Server-Authoritative Raycast Projectile (hitscan)

服务器权威型射线投射物(命中扫描)

luau
local function fireProjectile(origin: Vector3, direction: Vector3, damage: number, ignore: {Instance})
    local params = RaycastParams.new()
    params.FilterDescendantsInstances = ignore
    params.FilterType = Enum.RaycastFilterType.Exclude

    local result = workspace:Raycast(origin, direction * 300, params)
    if result then
        local hit = result.Instance
        local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
            or hit.Parent.Parent:FindFirstChildOfClass("Humanoid")
        if humanoid then
            humanoid:TakeDamage(damage)
        end
        return result.Position
    end
    return origin + direction * 300
end
luau
local function fireProjectile(origin: Vector3, direction: Vector3, damage: number, ignore: {Instance})
    local params = RaycastParams.new()
    params.FilterDescendantsInstances = ignore
    params.FilterType = Enum.RaycastFilterType.Exclude

    local result = workspace:Raycast(origin, direction * 300, params)
    if result then
        local hit = result.Instance
        local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
            or hit.Parent.Parent:FindFirstChildOfClass("Humanoid")
        if humanoid then
            humanoid:TakeDamage(damage)
        end
        return result.Position
    end
    return origin + direction * 300
end

Physics Projectile (arcing, grenade-style)

物理投射物(弧线运动,手榴弹样式)

luau
local function launchProjectile(origin: CFrame, velocity: Vector3, lifetime: number)
    local projectile = Instance.new("Part")
    projectile.Size = Vector3.new(0.5, 0.5, 0.5)
    projectile.Shape = Enum.PartType.Ball
    projectile.CFrame = origin
    projectile.Anchored = false
    projectile.CanCollide = true
    projectile.Parent = workspace

    -- Apply initial velocity
    projectile.AssemblyLinearVelocity = velocity

    -- Cleanup after lifetime
    task.delay(lifetime, function()
        if projectile.Parent then
            -- Explode or just destroy
            projectile:Destroy()
        end
    end)

    -- Detect hits
    projectile.Touched:Connect(function(hit)
        if hit.Parent:FindFirstChildOfClass("Humanoid") then
            -- Deal damage, create explosion, etc.
            projectile:Destroy()
        end
    end)

    return projectile
end
luau
local function launchProjectile(origin: CFrame, velocity: Vector3, lifetime: number)
    local projectile = Instance.new("Part")
    projectile.Size = Vector3.new(0.5, 0.5, 0.5)
    projectile.Shape = Enum.PartType.Ball
    projectile.CFrame = origin
    projectile.Anchored = false
    projectile.CanCollide = true
    projectile.Parent = workspace

    -- Apply initial velocity
    projectile.AssemblyLinearVelocity = velocity

    -- Cleanup after lifetime
    task.delay(lifetime, function()
        if projectile.Parent then
            -- Explode or just destroy
            projectile:Destroy()
        end
    end)

    -- Detect hits
    projectile.Touched:Connect(function(hit)
        if hit.Parent:FindFirstChildOfClass("Humanoid") then
            -- Deal damage, create explosion, etc.
            projectile:Destroy()
        end
    end)

    return projectile
end

Common Patterns

常见模式

Elevator / Moving Platform

电梯/移动平台

luau
local function createElevator(platform: BasePart, bottomY: number, topY: number, speed: number)
    local att = Instance.new("Attachment")
    att.Parent = platform

    local prismatic = Instance.new("PrismaticConstraint")
    prismatic.Attachment0 = att
    -- Attachment1 on a fixed anchor
    local anchor = Instance.new("Part")
    anchor.Anchored = true
    anchor.CanCollide = false
    anchor.Transparency = 1
    anchor.Position = platform.Position
    anchor.Parent = workspace

    local anchorAtt = Instance.new("Attachment")
    anchorAtt.Parent = anchor

    prismatic.Attachment1 = anchorAtt
    prismatic.ActuatorType = Enum.ActuatorType.Servo
    prismatic.Speed = speed
    prismatic.ServoMaxForce = 100000
    prismatic.LowerLimit = 0
    prismatic.UpperLimit = topY - bottomY
    prismatic.Parent = platform

    platform.Anchored = false

    return prismatic -- set .TargetPosition to move
end
luau
local function createElevator(platform: BasePart, bottomY: number, topY: number, speed: number)
    local att = Instance.new("Attachment")
    att.Parent = platform

    local prismatic = Instance.new("PrismaticConstraint")
    prismatic.Attachment0 = att
    -- Attachment1 on a fixed anchor
    local anchor = Instance.new("Part")
    anchor.Anchored = true
    anchor.CanCollide = false
    anchor.Transparency = 1
    anchor.Position = platform.Position
    anchor.Parent = workspace

    local anchorAtt = Instance.new("Attachment")
    anchorAtt.Parent = anchor

    prismatic.Attachment1 = anchorAtt
    prismatic.ActuatorType = Enum.ActuatorType.Servo
    prismatic.Speed = speed
    prismatic.ServoMaxForce = 100000
    prismatic.LowerLimit = 0
    prismatic.UpperLimit = topY - bottomY
    prismatic.Parent = platform

    platform.Anchored = false

    return prismatic -- set .TargetPosition to move
end

Swinging Platform

摆动平台

luau
local function createSwing(platform: BasePart, pivot: Vector3, maxAngle: number)
    platform.Anchored = false

    local pivotAtt = Instance.new("Attachment")
    pivotAtt.WorldPosition = pivot
    pivotAtt.Parent = workspace.Terrain -- fixed world point

    local platformAtt = Instance.new("Attachment")
    platformAtt.Position = platform.CFrame:PointToObjectSpace(pivot)
    platformAtt.Parent = platform

    local hinge = Instance.new("HingeConstraint")
    hinge.Attachment0 = pivotAtt
    hinge.Attachment1 = platformAtt
    hinge.LimitsEnabled = true
    hinge.LowerAngle = -maxAngle
    hinge.UpperAngle = maxAngle
    hinge.Parent = platform

    return hinge
end
luau
local function createSwing(platform: BasePart, pivot: Vector3, maxAngle: number)
    platform.Anchored = false

    local pivotAtt = Instance.new("Attachment")
    pivotAtt.WorldPosition = pivot
    pivotAtt.Parent = workspace.Terrain -- fixed world point

    local platformAtt = Instance.new("Attachment")
    platformAtt.Position = platform.CFrame:PointToObjectSpace(pivot)
    platformAtt.Parent = platform

    local hinge = Instance.new("HingeConstraint")
    hinge.Attachment0 = pivotAtt
    hinge.Attachment1 = platformAtt
    hinge.LimitsEnabled = true
    hinge.LowerAngle = -maxAngle
    hinge.UpperAngle = maxAngle
    hinge.Parent = platform

    return hinge
end

Network Ownership

网络所有权

Critical for physics objects: By default, the nearest player "owns" the physics simulation of unanchored parts. This means exploiters can fling your physics objects.
luau
-- Keep physics server-authoritative
local function setServerOwnership(model: Model)
    for _, part in model:GetDescendants() do
        if part:IsA("BasePart") and not part.Anchored then
            part:SetNetworkOwner(nil) -- server owns
        end
    end
end

-- Give ownership to driver (for responsive vehicles)
local function setDriverOwnership(vehicle: Model, player: Player)
    for _, part in vehicle:GetDescendants() do
        if part:IsA("BasePart") and not part.Anchored then
            part:SetNetworkOwner(player)
        end
    end
end
Trade-off: Server ownership = secure but laggy (server tick rate). Player ownership = responsive but exploitable. For vehicles, give ownership to the driver. For NPCs and world objects, keep server-owned.
物理对象的关键注意事项:默认情况下,最近的玩家“拥有”未锚定部件的物理模拟权。这意味着 exploit 者可以随意操纵你的物理对象。
luau
-- Keep physics server-authoritative
local function setServerOwnership(model: Model)
    for _, part in model:GetDescendants() do
        if part:IsA("BasePart") and not part.Anchored then
            part:SetNetworkOwner(nil) -- server owns
        end
    end
end

-- Give ownership to driver (for responsive vehicles)
local function setDriverOwnership(vehicle: Model, player: Player)
    for _, part in vehicle:GetDescendants() do
        if part:IsA("BasePart") and not part.Anchored then
            part:SetNetworkOwner(player)
        end
    end
end
权衡:服务器所有权 = 安全但延迟高(受服务器 tick 速率限制)。玩家所有权 = 响应迅速但存在被 exploit 的风险。对于载具,将所有权赋予驾驶员;对于 NPC 和世界对象,保持服务器所有权。

Common Mistakes

常见错误

  • Forgetting Anchored = false: Constraints do nothing on anchored parts.
  • Missing Attachments: Constraints need Attachment0 AND Attachment1. Missing one = silent failure.
  • No network ownership control: Physics objects get owned by nearest player. Exploiters fling them.
  • Over-constraining: Too many constraints on one assembly = physics solver instability (jitter).
  • No mass tuning: Default density makes small parts too light. Use CustomPhysicalProperties.
  • Touched for projectiles: Touched fires for every contact. Use Raycast for hitscan, Touched only for slow physics projectiles.
  • No lifetime on projectiles: Forgotten projectiles accumulate and kill server performance.
  • 忘记设置Anchored = false:约束对锚定部件无效。
  • 缺少Attachment:约束需要同时设置Attachment0和Attachment1。缺少任意一个都会导致静默失败。
  • 未控制网络所有权:物理对象会被最近的玩家接管所有权,exploit者可随意操纵它们。
  • 过度约束:单个组件上添加过多约束会导致物理求解器不稳定(抖动)。
  • 未调整质量:默认密度会使小型部件过轻。使用CustomPhysicalProperties调整。
  • 用Touched处理投射物:Touched会在每次接触时触发。命中扫描投射物使用Raycast,仅对低速物理投射物使用Touched。
  • 未设置投射物生命周期:未清理的投射物会不断累积,导致服务器性能下降。