.Touched part script breaks when equipping tool inside

Hello,

So in my game, there’s a point where you’re supposed to go into a dark basement to get a screwdriver. I have an invisible .Touched part with CanCollide off in the basement so when you enter it, a flashlight GUI on your screen and a SpotLight on your Torso appear. The thing is that once you find the screwdriver and equip it down there, all of a sudden the scripts breaks for some weird reason.

Clip of what I mean:

How’s this possible? You’re still inside the part as you’re picking it up, there shouldn’t be a reason for the script to bug out.

How things are set up:

The .Touched basement part
image

The code inside “Script”

local zone = script.Parent
local FlashlightOn = game.Workspace.FlashlightOn
local FlashlightOff = game.Workspace.FlashlightOff
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local FlashlightOn = ReplicatedStorage.FlashlightOn
local FlashlightOff = ReplicatedStorage.FlashlightOff


zone.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("Humanoid") then
		local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
		if plr then
			if not plr.PlayerGui:FindFirstChild("GUIClonedfromtouchblock") then
				local clonedgui = script.Parent:FindFirstChildOfClass("ScreenGui"):Clone()
				FlashlightOn:FireClient(plr)
				clonedgui.Name = "GUIClonedfromtouchblock"
				clonedgui.Parent = plr.PlayerGui
				script.Parent.TouchEnded:Connect(function(hit2)
					if hit == hit2 then
						FlashlightOff:FireClient(plr)
						game.Debris:AddItem(clonedgui,0)
					end
				end)
			end
		end
	end
end)

The RemoteEvents in ReplicatedStorage
image

The LocalScripts which make the GUI and SpotLight work
image

The code inside FlashlightOff

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Torso = Character:WaitForChild("Torso")

local Remote = ReplicatedStorage:WaitForChild("FlashlightOff")

local FlashlightOff = workspace.FlashlightOff

Remote.OnClientEvent:Connect(function()
	FlashlightOff:Play()
	task.wait()
	local Light = Torso:FindFirstChild("SpotLight")
	if Light then
		Light:Destroy()
	end
end)

The code inside FlashlightOn

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local Player = Players.LocalPlayer
local Remote = ReplicatedStorage:WaitForChild("FlashlightOn")

local FlashlightOn = workspace.FlashlightOn

Remote.OnClientEvent:Connect(function()
	FlashlightOn:Play()
	light = Instance.new('SpotLight')
	light.Brightness = 2
	light.Parent = game.Players.LocalPlayer.Character.Torso
end)

What’s with things breaking when equipping tools inside the .Touched basement part I’m talking about? I’d appreciate some explanation and help!

Eugh, I severely dislike Touch and TouchEnded because of issues like this. Could I interest you in a completely different approach here? It should resolve your issue and improve how this is handled overall both in terms of performance and code structure.

Since the only real thing you’re doing here is granting a flashlight to the player, you can and should do this all from a single LocalScript, first of all. There’s no important tasks or flags that the server sets so you don’t need to involve the server here. Let’s get that out of the way.

Here’s my suggestion: what you want to do is set up zones like you have now but instead make them flat and underneath the map. Every Heartbeat you will cast a short ray downward and check if the player hits one of those flashlight zones. If they are in the zone then turn on the flashlight, but if they ever leave the zone then turn it off.

I whipped up a repro that does not do obstruction checks and it will only enable up to a character being 6 studs above the flashlight zone (which can be changed).

--- Enable a flashlight if the user enters a flashlight-enabled area
-- @author colbert2677

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")

-- All flashlight areas will be tagged with "FlashlightZone" to help
-- filter the raycast.
local LIGHT_TAG = "FlashlightZone"

local Character = script.Parent
local RootPart = Character:WaitForChild("HumanoidRootPart")

local LocalPlayer = Players.LocalPlayer
local FlashlightOverlay = LocalPlayer.PlayerGui:WaitForChild("FlashlightOverlay")

-- We will reuse our RaycastParams every time we perform a cast that will only
-- check if a FlashlightZone part is hit.
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
raycastParams.IgnoreWater = true
raycastParams.FilterDescendantsInstances = CollectionService:GetTagged(LIGHT_TAG)

-- Create a lighting effect.
local lightEffect = Instance.new("SpotLight")
lightEffect.Brightness = 2
lightEffect.Enabled = false
lightEffect.Parent = RootPart

-- Internal flag so we don't spam-enable the flashlight.
local lightOn = false

local function setLightEnabled(enabled, playSound)
	FlashlightOverlay.Enabled = enabled
	lightEffect.Enabled = enabled
	
	if playSound then
		local lightSound = if enabled then "LightOn" else "LightOff"
		FlashlightOverlay[lightSound]:Play()
	end
end

-- Every frame after physics updates this will run. We will do a short raycast
-- 6 studs downward to check if the player is on a flashlight zone. If so,
-- we enable the flashlight.
local function checkZoneAndToggleFlashlight()
	local castResult = workspace:Raycast(RootPart.Position, Vector3.new(0, -15, 0), raycastParams)
	
	if castResult and not lightOn then
		lightOn = true
		setLightEnabled(lightOn, true)
	elseif not castResult and lightOn then
		lightOn = false
		setLightEnabled(lightOn, true)
	end
end

RunService.Heartbeat:Connect(checkZoneAndToggleFlashlight)

-- Set initial state, since this is a character script.
setLightEnabled(false, false)

Repro file for you to play around with:

FlashlightRepro.rbxl (37.9 KB)

Video demonstration (so long as it stays up on Streamable):

EDIT: OP forwarded me some extra help in a PM and I’ve made a few mistakes in this code that cause some unexpected issues. I’ve updated the sample with fixes and attached a new repro place with lighting settings that allow you to better check out the lights. I’ve also added support for an on and off sound since the original repro only had a toggle sound.

EDIT 2: You probably will want to turn off IgnoreGuiInset for the overlay if you use the same one I did.

3 Likes