Checking if a part is touching another part every .1 seconds?

I need to find a way to check if a ball is touching a brick, and check every .1 seconds if it is still touching the brick.

I cannot use :GetTouchingParts() because it only gives me parts that are intersecting the brick. The ball is only touching the surface. How can I achieve this?

You could cast a ray from the center of your ball towards the brick with a distance of the radius of the ball (maybe +.01), if you hit the brick with that ray, then the ball is touching, or within .01 away from touching the brick.

local ball, brick = -- your_ball, your_brick

local start = ball.CFrame.p
local direction = (start-your_brick.CFrame.p).Unit

local extra_distance = 0.01
local distance = (ball.Size.X/2) + extra_distance -- you could probably do ball.Size.Magnitude/2 too, i think.

local ray = Ray.new(start, direction * distance)

local brick_touched = workspace:FindPartOnRayWithWhitelist(ray, {brick})

if brick_touched then -- this will be your_brick, if we were able to hit it.
    --your ball is close enough to your brick to be considered touching.
end

untested so may be typos but that should do it.

2 Likes

What is “.Unit”?

A normalized copy of the vector - one which has the same direction as the original but a magnitude of 1

It is useful for determining which direction one Vector3 is from another Vector3.

In this case we are saying local direction is the direction the brick’s position is from the ball’s position. That way we can cast our ray in that direction.

2 Likes

Not exactly. A unit vector has a distance of 1 if you imagine a straight line from the origin (0,0,0). So (1,0,0) is a unit vector but (1,1,1) is not.

2 Likes

My mistake, thank you

I don’t think that works for all cases. See the counter-example in my image above.

EDIT: The red ray is pointing from the center of the ball to the center of the part and goes as far as the radius.
Despite the ball touching the part, the red ray does not intersect anywhere on the Part. Even if you add on a small distance (in reference to your extra_distance variable), the ray still doesn’t reach any surfaces of the Part.

1 Like

Less you send two rays from either side of the ball.

However if you’re using the ball’s velocity to determine the pointing direction, you may want to average it, as velocity can spike very harshly especially when FPS drops.

You can use BasePart.Touched and BasePart.TouchEnded to track whether two parts are touching another.
Depending on your use case, it might even be more performant to just listen to those events rather than to have a loop check every 1/10th of a second.

2 Likes

I’ve tested two ways of doing it and found both to be reliable. The first is using my GJK distance and intersection module. If you are not familiar with the Gilbert-Johnson-Kerthi algorithm, basically you make a “support” function for each of the primitives to test (the part and the sphere in this case). The support function is called with a direction and should return the point on the primitive furthest in that direction. For a part, it will always be one of its corners. For a sphere, it will be a point on its surface. GJK uses these support functions to look for a plane of separation (whose normal is the direction passed into the support functions).

local GJK = require(script.GJK)
local min = math.min
local up = Vector3.new(0, 1, 0)

local function newPartSupport(part)
	local pos = part.Position
	local s = part.Size * 0.5
	local cf = part.CFrame - pos
	local i = cf.RightVector * s.X
	local j = cf.UpVector * s.Y
	local k = cf.LookVector * s.Z

	return function(dir)
		return pos
			+ (i:Dot(dir) > 0 and i or -i)
			+ (j:Dot(dir) > 0 and j or -j)
			+ (k:Dot(dir) > 0 and k or -k)
	end
end

local function newSphereSupport(sphere)
	local pos = sphere.Position
	local s = sphere.Size * 0.5
	local radius = math.min(s.X, s.Y, s.Z)

	return function(dir)
		return pos + dir.Unit * radius
	end
end

local function isTouching(part, sphere)
	return GJK.intersection(
		newPartSupport(part),
		newSphereSupport(sphere),
		up
	)
end

local part = workspace.Part
local sphere = workspace.Sphere
while wait() do
	print(isTouching(part, sphere))
end

The other method was described by @Flubberlutsch and works well. Here is the script I used to test it. I ran round and hit the ball and made it bounce. Seems reliable:

local part = workspace.Part
local sphere = workspace.Sphere

local isTouching

local function onTouched(other)
	if other == sphere then
		print 'Touch'
		isTouching = true
	end
end

local function onTouchEnded(other)
	if other == sphere then
		print 'Ended'
		isTouching = false
	end
end

part.Touched:Connect(onTouched)
part.TouchEnded:Connect(onTouchEnded)

while wait() do
	print(isTouching)
end

As for a performance comparison, I’d expect the event based one to be more performant. While running both, I noticed a FPS drop on the GJK one from 54.5 to 54.4, which doesn’t seem significant to me. The GJK algorithm calls each support function 6 times while touching and once when not touching and separated on the y axis.

Yes, absolutely. Definitely used the event-based one. Using Touched and TouchEnded should definitely be the solution for this problem.

This basically gets to the difference between events and polling. Events are almost always preferred.

1 Like

You can use, BasePart:GetTouchingParts() .
It will return a table of the touching parts.
https://developer.roblox.com/api-reference/function/BasePart/GetTouchingParts
for example,

local part = game.Workspace.Part
local targetPart = game.Workspace.target

local isTouching = false
for i,v in next, part:GetTouchingParts() do
     isTouching = (v==targetPart and true) or isTouching
end

If the parts cant collide, you can do this

local part = game.Workspace.Part
local targetPart = game.Workspace.target

local isTouching = false

targetPart.CanCollide = true
part.CanCollide = true

for i,v in next, part:GetTouchingParts() do
     isTouching = (v==targetPart and true) or isTouching
end

targetPart.CanCollide = false
part.CanCollide = false
1 Like

This is correct according to the wiki, so the name of GetTouchingParts can be quite deceptive

1 Like