agentleFS
EL4CTEOEL4CTEO/roblox-skills/skills/roblox-physics/SKILL.md

roblox-physics

Skill92 starsChanged 47 days agoMITView on GitHub

Physics and spatial queries - Raycast/Blockcast/Spherecast, overlap queries, collision groups, Touched pitfalls, collision summaries (impact point/normal/speed), assemblies and welds, mover constraints replacing BodyMovers, mechanical constraints, network ownership, CFrame math, vehicles/projectiles, and server authority (BindToSimulation, prediction, rollback). Use when moving parts, detecting hits, building vehicles or projectiles, or fixing jitter, flinging, or ownership bugs.

4 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

5 rules. 0 are common, 0 are distinctive, and 5 are very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 0

    Distinctive

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    No summaries for CanCollide = false parts or between two sleeping parts.

    line 94
  2. Very specific:

    Read/set physics via AssemblyLinearVelocity/AssemblyAngularVelocity, part:ApplyImpulse(), ApplyAngularImpulse(), AssemblyMass.

    line 142
  3. Very specific:

    All need an Attachment (Attachment0).

    line 159
  4. Very specific:

    Don't cache AnimationTracks.

    line 188
  5. Very specific:

    Read a player's camera on the server with player:GetCameraState() (CFrame, FieldOfView, ViewportSize).

    line 189

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Roblox physics and spatial queries

Spatial queries (use these, not Touched, for gameplay decisions)

--!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)
  • Reuse params objects; update FilterDescendantsInstances (or params:AddToFilter(instance)) instead of allocating per call.
  • Newer filters: params.ExcludeInstances / IncludeInstances (can be combined; exclude wins) supersede FilterType + FilterDescendantsInstances for new code.
  • RespectCanCollide = true to ignore non-collidable parts; CollisionGroup = "Name" to query as a group.
  • Shapecasts (October 2026): params.Margin inflates/rounds the cast shape (a Raycast with a margin acts as a spherecast — use a thin one where rays slip through seams); params.SizeGradient grows/shrinks it along the cast (0.5 ends 50% larger, -1 shrinks to nothing) for cones and funnels; workspace:Shapecast(part, direction, params, cframeOverride?, sizeOverride?) casts a part from any pose/size without moving it (placement previews). Debug: Studio Visualization Modes → Physics → Shapecasts.
  • GetPartsInPart(part, params) does exact geometry overlap (costlier); GetPartBoundsIn* use bounding boxes.
  • Deprecated: FindPartOnRay*, Ray.new, FindPartsInRegion3* → use the APIs above.
  • Client queries only see streamed-in parts; authoritative checks run on the server.

Touched: know its limits

Touched fires from the physics engine, only for parts that physically contact, is fired by whoever simulates the part (clients can spoof or suppress it for parts they own), and can fire many times per contact. Use it for triggers (pads, pickups) with a debounce and server-side validation; use spatial queries for hit detection and zones.

--!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)

Collision summaries (impact data)

For how parts hit (impact FX, impact audio, damage by impact speed), use BasePart:BindToCollisionSummaries(callback) instead of Touched. Once per frame after the physics step, the callback gets every contact of that part: Part0 (the bound part), Part1, Point, Normal (points away from Part0), RelativeVelocity (before the solver resolves the hit), Area, and Time.

  • Fires only where the part is simulated (its network owner); nothing replicates. Send what others need through your own remote, validated on the server.
  • No summaries for CanCollide = false parts or between two sleeping parts; non-convex parts can produce several summaries per pair. Cost scales with bound parts, not total collisions.
--!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

Collision groups

--!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

Register groups once at server start (max 32, see workspace:GetMaxCollisionGroups()). CanCollide = false removes all collisions; CanQuery = false hides a part from raycasts; CanTouch = false disables Touched (saves CPU); NoCollisionConstraint disables collision between two specific parts.

MeshPart collision shape: CollisionFidelity Box/Hull (cheap), Default, PreciseConvexDecomposition (costly), or Tunable with a CollisionPrecision slider (set in Studio; scripts can't write it) to trade precision for performance per mesh.

Assemblies and joints

  • An assembly = parts rigidly connected (welds, WeldConstraint, Motor6D, rigid constraints). It simulates as one body; the root part (part.AssemblyRootPart) determines network ownership.
  • Anchor static geometry. Unanchored + welded to an anchored part = anchored assembly.
  • Weld at edit time with WeldConstraint (keeps current offset); in code set Part0/Part1 and parent.
  • Move whole models with model:PivotTo(cframe). Moving many parts at once: workspace:BulkMoveTo.
  • Read/set physics via AssemblyLinearVelocity/AssemblyAngularVelocity, part:ApplyImpulse(), ApplyAngularImpulse(), AssemblyMass. Velocity/RotVelocity are deprecated.
  • CustomPhysicalProperties = PhysicalProperties.new(density, friction, elasticity, frictionWeight, elasticityWeight).
  • Units: 1 stud ≈ 0.28 m, workspace.Gravity default 196.2 studs/s².

Mover constraints (replace BodyMovers)

GoalConstraintReplaces
Hold/drive a velocityLinearVelocity (VelocityConstraintMode Vector/Plane/Line)BodyVelocity
Move toward a positionAlignPosition (Mode = OneAttachment + Position, Responsiveness, MaxForce)BodyPosition
Rotate toward an orientationAlignOrientation (CFrame/LookAtPosition)BodyGyro
Constant forceVectorForce (RelativeTo = World)BodyForce, BodyThrust
SpinAngularVelocityBodyAngularVelocity
Pull toward pointLineForceRocketPropulsion
TorqueTorque—

All need an Attachment (Attachment0). Set MaxForce/MaxTorque realistically (not math.huge unless intended) or objects fling. Mechanical constraints: HingeConstraint (Motor/Servo actuators for wheels, doors), PrismaticConstraint (sliders, elevators), SpringConstraint (suspension), RopeConstraint, RodConstraint, BallSocketConstraint, CylindricalConstraint (wheel + steering), UniversalConstraint, RigidConstraint.

Vehicle and projectile recipes, CFrame cookbook: references/recipes.md.

Network ownership

  • The server auto-assigns ownership of unanchored assemblies to a nearby client (smooth for that client, exploitable by it). Characters are owned by their player.
  • part:SetNetworkOwner(nil) = server simulates (secure, laggier for players); SetNetworkOwner(player) for a vehicle driver; SetNetworkOwnershipAuto() to restore. Call on the root part, server-side, after the assembly is in Workspace. Anchored parts have no owner.
  • Ownership changes cause hitches; don't flip it every frame. Visualize with the viewport's Visualization options → Network owners toggle.

Server authority (competitive games)

Workspace AuthorityMode = Server makes the server simulate everything while clients predict a few frames ahead and roll back/resimulate on mispredictions. Requirements: NextGenerationReplication, PlayerScriptsUseInputActionSystem, SignalBehavior = Deferred, UseFixedSimulation, StreamingEnabled (setting AuthorityMode sets these). Core rules:

  • Game logic lives in functions bound with RunService:BindToSimulation(fn) inside a ModuleScript required by both server and client.
  • Clients affect the game only through Input Actions (InputContexts parented under the Player), not UserInputService events.
  • Sync custom state through attributes on predicted instances, written only inside simulation callbacks.
  • Don't cache AnimationTracks; query Animator:GetTrackByAnimationId() each step.
  • Read a player's camera on the server with player:GetCameraState() (CFrame, FieldOfView, ViewportSize); the old camera InputAction replication no longer syncs.

Fully released (opt-in) since July 2026. Details and patterns: references/server-authority.md.

Stability tips

  • Jitter: conflicting ownership, constraints fighting, huge mass ratios (> 1:100), or setting CFrame on unanchored parts every frame (use constraints instead).
  • Flinging: MaxForce = math.huge on light parts, overlapping collisions at spawn, NaN/huge velocities.
  • Sleeping assemblies stop simulating until disturbed — wake with a small impulse if needed.
  • Adaptive timestepping (Workspace.PhysicsSteppingMethod = Adaptive) saves CPU; use fixed for precise mechanisms.

Related skills

roblox-security (ownership exploits), roblox-characters-animation, roblox-networking, roblox-performance, roblox-input.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.