How do I make an impact detection system?

Hi, I’m pretty new to scripting and I’ve been stuck on this for a while. I’m trying to figure out how to make some kind of impact detection system like in Broken Bones. This is the only thing keeping me from finishing my game, so I’m kind of desperate at this point.

1. What do you want to achieve?

I want to detect when a player hits the ground or another object with force, and be able to measure how big the impact was.

2. What is the issue?

The main problem is reliability and accuracy.

.Touched does register collisions, but it doesn’t really separate small bumps from strong impacts.
Raycasting gave me results sometimes, but they didn’t feel accurate for actual collisions.

Because of this, I can’t get consistent or accurate results with the systems I’ve tried.

3. What solutions have you tried so far?

Using `.Touched’.
Raycasting from the player downward.
Searching Creator Hub and DevForum, but I didn’t find anything that covered this issue properly.

Extra details

What I’d like is a way to reliably measure:

How strong the impact is when a character collides with something.

i think you can use Velocity somehow idk

yeah, but how do i know if the player is going to make an impact?

maybe you could use Region3 or the ZonePlus module, if you don’t want to use Touched

This approach using workspace:Blockcast() will pretty reliably detect impacts in my testing.

--[[ Variables ]]--
-- Character --
local character = script.Parent
local rootPart = character:WaitForChild("HumanoidRootPart")

--[[ Functions ]]--
local function detectImpact(dt : number) : RaycastResult?
	local castParams = RaycastParams.new()
	castParams.FilterDescendantsInstances = {character}
	castParams.RespectCanCollide = true
	
	local shapeCFrame = rootPart:GetPivot()
	local shapeSize = Vector3.new(2, 5, 2)
	local castDirection = rootPart.AssemblyLinearVelocity * dt
	return workspace:Blockcast(shapeCFrame, shapeSize, castDirection, castParams)
end

local function init() : ()
	while true do
		local dt = task.wait()
		local impactResult = detectImpact(dt)
		if impactResult then
			print(`Impact at {impactResult.Position} at {rootPart.AssemblyLinearVelocity.Magnitude} studs/s`)
		end
	end
end
init()

It won’t be 100% up to date since it is independent of the actual physics engine, but because it predicts a collision by a frame duration ahead, it should be pretty reliable.

3 Likes

In Roblox a Humanoid has a RootPart (usually HumanoidRootPart) that you can monitor!

You can (for example):

  1. Continuously track the player’s velocity
  2. Detect when velocity suddenly changes (which indicates an impact)
  3. Measure the magnitude of the change to quantify the force

Heres a really really simple LocalScript (or ServerScript) that calculates impact:

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local root = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")

local lastVelocity = Vector3.new(0,0,0)
local impactThreshold = 30 -- tweak this to control what counts as a strong hit!

--loop to check velocity every frame
game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
    local currentVelocity = root.Velocity
    local velocityChange = (currentVelocity - lastVelocity).Magnitude

    if velocityChange > impactThreshold then
        print("Impact detected! Force magnitude:", velocityChange)
        -- you can call functions here to deal damage or play effects or whatever you use this for
    end

    lastVelocity = currentVelocity
end)

how This Works

  1. root.Velocity gives the current speed and direction of the character
  2. (currentVelocity - lastVelocity).Magnitude calculates how much the velocity changed since the last frame
  3. If the change is above your impactThreshold it counts as a strong hit
  4. You can tweak impactThreshold depending on how sensitive you want it!
2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.