Choose deliberately between simulation, character control, hit detection, and visual-only motion;
they are different jobs. Targets Roblox's rolling platform APIs. Pair with physics-tuning for
engine-neutral stability and feel.
Touched is unreliable/security-sensitive, parts tunnel or jitter, a mechanism breaks
when anchored, or old BodyMover patterns appear.When not to use: ordinary Humanoid lifecycle/control belongs to roblox-characters; remote
validation belongs to roblox-networking; decorative UI/world motion may only need a tween.
| Goal | Mechanism |
|---|---|
| sustained physical interaction | unanchored assembly + modern constraints/forces |
| instantaneous physical change | ApplyImpulse / ApplyAngularImpulse |
| kinematic platform/path | controlled pivot/transform with an explicit passenger policy |
| character locomotion | Humanoid/custom character controller (roblox-characters) |
| authoritative hit test | server raycast/overlap with filters and gameplay validation |
| cosmetic trail/recoil | local visual motion; no gameplay authority |
CanCollide, CanTouch, and CanQuery. These flags are not interchangeable..Touched as a universal hit detector. Use a ray for
a path/line, an overlap query for a volume, and simulation contacts when physical response is
actually required.LinearVelocity, AngularVelocity, VectorForce, AlignPosition, and AlignOrientation
constraints as appropriate; migrate deprecated BodyMovers when changing that system.local Workspace = game:GetService("Workspace")
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {shooterCharacter}
params.IgnoreWater = true
params.CollisionGroup = "WeaponQuery"
local direction = aimDirection.Unit * MAX_RANGE
local result = Workspace:Raycast(muzzlePosition, direction, params)
if result then
local model = result.Instance:FindFirstAncestorOfClass("Model")
local humanoid = model and model:FindFirstChildOfClass("Humanoid")
if humanoid and serverCanDamage(shooter, model, result.Position) then
humanoid:TakeDamage(serverWeaponDamage(shooter))
end
end
The server must validate the origin/direction against server-known character/weapon state; do not accept an arbitrary client origin and treat the raycast itself as validation.
local params = OverlapParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {sourceCharacter}
params.CollisionGroup = "DamageQuery"
params.MaxParts = 64
local seen: {[Model]: boolean} = {}
for _, part in Workspace:GetPartBoundsInBox(hitboxCFrame, hitboxSize, params) do
local model = part:FindFirstAncestorOfClass("Model")
if model and not seen[model] then
seen[model] = true
validateAndApplyHit(model)
end
end
Bounds queries use bounding boxes and can include multiple parts from one target; deduplicate and
perform exact/gameplay checks as needed. For exact geometry use WorldRoot:GetPartsInPart(part, overlapParams)
only when its additional cost is justified. Note OverlapParams.RespectCanCollide decides whether a
query honours CanCollide or CanQuery — set it deliberately, or it silently overrides the flag
policy below. OverlapParams.Tolerance controls contact slop.
AssemblyLinearVelocity is an immediate state change, not a continuous force model.BodyPosition, BodyVelocity, BodyGyro, and
other deprecated BodyMovers when authoring or revising a mechanism.SetNetworkOwner(nil) conservatively for critical objects, then measure responsiveness/server
cost. Visualize network owners in Studio..Touched observations. The server validates
consequential hits, positions, timing, and permissions independently.| Symptom | Likely cause | Remedy |
|---|---|---|
| welded mechanism will not move | one part anchored | inspect full assembly; anchor only intentional world roots |
| force behaves too strongly/weakly | assembly mass ignored | inspect AssemblyMass; tune force/impulse by intended acceleration |
| hit misses fast projectile | discrete touch sampling/tunneling | swept query — WorldRoot:Blockcast(), Spherecast(), or Shapecast() — plus physics-tuning; do not rely only on .Touched |
| ray hits shooter/effects | filters/collision group absent | reuse explicit params and query group |
| same target damaged many times | overlap returned multiple body parts | deduplicate by target model and enforce attack ID/cooldown |
| exploit fires impossible touch | client owns relevant physics | server query/context validation; deliberate ownership |
| invisible trigger blocks or cannot query | three flags conflated | set CanCollide, CanTouch, CanQuery independently |
| mechanism leaks attachments | temporary constraint lifecycle missing | own and destroy constraints, attachments, and connections together |
references/queries-and-ownership.md for collision/query matrices, assembly debugging,
ownership security, migration choices, and the physics verification matrix.physics-tuning — timestep, jitter, tunneling, mass ratios, and stability methodology.roblox-characters — Humanoid/custom movement and respawn lifecycle.roblox-networking — authoritative validation of client-requested physical actions.roblox-studio-workflow — visualization, Output, and multi-client verification.https://create.roblox.com/docs/physics/assemblies
https://create.roblox.com/docs/physics/network-ownership
https://create.roblox.com/docs/workspace/raycasting