What is the best way to precisely detect a player on top of a model?

What is my goal?
I’m starting to create maps for my game, and I want the player weigh down on these jets when they run around on them. In order to do this, I just want to tween the ship down by a stud if a player is on top of it, and return it back up when they hop off. I also want this system to be efficient because there will likely be multiple instances of the jet in my maps.

The Issue
The problem right now is that the touched events trigger every time a different limb of the player goes inside of the hitboxes or leaves them, plus I’m pretty sure they are retriggering for every instance of the hitbox.

In the output I’m printing the instance that triggers the touched events.


at the end of the video, it actually starts acting as I want it to. I guess because its only the legs entering a single box at nearly the same time.

The pink blocks are some hitboxes I constructed which my script iterates and inserts the touched script inside of.

My Code
My script is placed inside a folder also containing all of the hitbox parts.

--[[ Services ]]

local TweenService = game:GetService("TweenService")

--[[ Constants ]]

local detectoBoxes = script.Parent:GetChildren() -- grabs all the hitboxes
local ship = script.Parent.Parent -- model
local primaryPart = ship.primarypart

--[[ Main ]]

local anchorPos = primaryPart.Position
local goalPosDown = anchorPos + Vector3.new(0, -1, 0)
local goalPosUp = anchorPos + Vector3.new(0, 1, 0)

local tweenInfo = TweenInfo.new(
	1,
	Enum.EasingStyle.Quint,
	Enum.EasingDirection.Out,
	0,
	false,
	0
)

local shipTweenDown = TweenService:Create(primaryPart, tweenInfo, {Position = goalPosDown})
local shipTweenUp = TweenService:Create(primaryPart, tweenInfo, {Position = goalPosUp})

local done = false
for it,ver in next, detectoBoxes do

	if ver:IsA("BasePart") then
		ver.Touched:Connect(function(hit) print(hit)
			if done == false then
				shipTweenDown:Play()
				done = true
			end
		end)
		ver.TouchEnded:Connect(function()
			shipTweenUp:Play()
			done = false
		end)

	end

end

What have I tried?
I know Modules like ZonePlus exist but I’m worried that because I will be making multiple detection boxes as well, I will come across similar issues when it comes to the player moving from one hitbox to another separate hitbox, possibly re-firing the function.
I also know its possible that I could mess around with having a collision group only for a single part of the player, and then for all of the hitboxes for the ship, but I still would likely come across the re-firing issue.

Any advice would be a huge help, thanks!!! :happy1:

1 Like

You can create a table referring a player’s character to the number of parts touching the hitbox. Then, create a variable to store the number of characters touching the hitbox. Add one to this variable every time a character enters, and subtract one when all of their touching parts leave. Once this reaches 0, tween the ship down, like this:


--[[ Services ]]

local TweenService = game:GetService("TweenService")

--[[ Constants ]]

local detectoBoxes = script.Parent:GetChildren() -- grabs all the hitboxes
local ship = script.Parent.Parent -- model
local primaryPart = ship.primarypart

--[[ Manipulated ]]
local characterPartsTouching = {}
local charactersTouching = 0
local isShipDown = false

--[[ Main ]]

local anchorPos = primaryPart.Position
local goalPosDown = anchorPos + Vector3.new(0, -1, 0)
local goalPosUp = anchorPos + Vector3.new(0, 1, 0)

local tweenInfo = TweenInfo.new(
	1,
	Enum.EasingStyle.Quint,
	Enum.EasingDirection.Out,
	0,
	false,
	0
)

local shipTweenDown = TweenService:Create(primaryPart, tweenInfo, {Position = goalPosDown})
local shipTweenUp = TweenService:Create(primaryPart, tweenInfo, {Position = goalPosUp})

for it,ver in next, detectoBoxes do

	if ver:IsA("BasePart") then
		ver.Touched:Connect(function(hit)
			--Get character from hit
			local character = hit:FindFirstAncestorOfClass("Model")
			local humanoid = character and character:FindFirstChildOfClass("Humanoid")
			if humanoid then
				--Update parts touching
				if not characterPartsTouching[character] then
					characterPartsTouching[character] = 0
				end
				--Update number of characters touching
				if characterPartsTouching[character] == 0 then
					charactersTouching += 1
				end
				characterPartsTouching[character] += 1
				--Move ship down if not already moved
				if not isShipDown then
					isShipDown = true
					shipTweenDown:Play()
				end
			end
		end)
		ver.TouchEnded:Connect(function(hit)
			--Get character from hit
			local character = hit:FindFirstAncestorOfClass("Model")
			local humanoid = character and character:FindFirstChildOfClass("Humanoid")
			if humanoid and characterPartsTouching[character] then
				--Update parts touching
				characterPartsTouching[character] -= 1
				--Check if all parts have stopped touching the ship
				if characterPartsTouching[character] == 0 then
					charactersTouching -= 1
					--Check if there are no characters touching the ship, and ship is down
					if charactersTouching == 0 and isShipDown then
						isShipDown = false
						shipTweenUp:Play()
					end
				end
			end
			shipTweenUp:Play()
		end)

	end

end

1 Like

This works great! I really need to get some practice with tables and their applications. This serves as a good example. Thanks for the info and explanation! :happy3:

1 Like

Tables are super useful! No problem and let me know if you have any questions

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