Creating a puzzle with Touched and TouchEnded?

so, in my game, i’m trying to remake one of the puzzles from UNDERTALE (yeah, the game everyone’s already tired of hearing for 3 years). here’s basically the concept of what i wanted:

basically, if you step on the union, all of the spikes turn invisible, until you step off. @konlon15 helped out with this after i posted about it on reddit, however, while it got the job done (which im very thankful for), it was riddled with messy bugs, specifically, this one:

after stepping on spikes, sometimes it’s possible to have some still be rendered invisible, even when you step back onto them. this only occurs online, as opposed to single-player test mode in studio.
right now, the script konlon made me is here, however, as he described to me, touchended isn’t always a reliable event.

--made by konlon15 for Superkirbylover
local Players = game:GetService("Players")
local waittime = 0.15

local Ruins = workspace:WaitForChild("spikepath_ruins")
local Spikes = Ruins:WaitForChild("safespikes")

function CheckSpikeTransparency(TouchingChars, Spikes)
	local Count = (function() local c = 0 for i, v in pairs(TouchingChars) do c = 1 end return c end)()
	--the above calls an anonymous function and then counts all touchingchars
	if Count >= 1 then -- 1 or more players stand on the base
		for i, Spike in pairs(Spikes) do	
			Spike.Transparency = 1
		end
	else -- 0 players on the base
		for i, Spike in pairs(Spikes) do
			Spike.Transparency = 0
		end
	end
end

for i, Trap in pairs(Spikes:GetChildren()) do
	--i is the number we're on
	--Trap is the safespiketrap
	local Base = Trap:FindFirstChild("base")
	local TouchingChars = {} -- this is a lua table

	if Base then	
		local Spikes = (function() local t = {} for i, v in pairs(Trap:GetChildren()) do if v.Name == "spike" then table.insert(t, v) end end return t end)()
		Base.Touched:Connect(function(Part)
			print("start", Part, Part.Parent)		
			local Character = Part.Parent
			if Character:FindFirstChild("Humanoid") then
				TouchingChars[Character] = true
				CheckSpikeTransparency(TouchingChars, Spikes)
				
				-- this is the same as doing this:
			--	local Touching_Chars = {
				--	all the other players standing on the base
				--	[game.workspace.konlon15] = true
			--	}
			end
		end)
		Base.TouchEnded:Connect(function(Part)
			print("end", Part, Part.Parent)
			local Character = Part.Parent
			if Character:FindFirstChild("Humanoid") then
				TouchingChars[Character] = nil --this removes the character from the table!
				delay(waittime, function()
					if TouchingChars[Character] == nil then
						CheckSpikeTransparency(TouchingChars, Spikes)
						TouchingChars = {} --this resets the table totally. good? maybe.
					end
				end)
			end
		end)
	end
end

is there ways for me to go about fixing this? do i have to make a whole, new script, or just modify the current one?
(i guess it’s important to mention i have scripting experience, however, it’s not 100% advanced. only slightly.)

konlon is correct, TouchEnded is not always reliable - in fact I’d say in most cases it isn’t. It seems like the ideal solution would be to either have a better detection method, which is properly reliable - or run additional checks.

In terms of a different detection method, I would probably use raycasting - shoot a ray straight down from the player to check which spike-set he is touching, and make it invisible. However; I don’t know that this will provide the outcome you want, seeing how 90% of the time only one spike will be invisible.

If you wanted to add additional checks to fix any spikes that might break - I would use :GetTouchingParts().

With a lack of knowledge to what the outcome is meant to look like it can be hard to provide decent feedback, can you give a link to what this looked like in UNDERTALE? This might allow people to give more accurate responses that more aptly meet your needs.

1 Like

yeah, i had a feeling that was the case. i’m just worried if i have a constantly running script detecting this, it may drag down the game’s speed.

oh, of course! here you are;

You don’t need to run it constantly, only when the player is in the area - and running one raycast will have hardly any effect on your game. I would start with trying the solutions I listed above, and if they don’t work - we’ll go back to the drawing board.

1 Like

sounds good!
one thing, though-- i’ve heard of raycasts, however, i’ve never really known how to effectively use them, or what they even do / are. i’ll probably look up some wiki pages and stuff to fill in some of that stuff. thank you!

If you have questions about raycasting don’t hesitate to PM me here on the DevForum, or on Discord (SummerEquinox#8416) and I’ll explain them to you.

1 Like

well, thank you!! i’ll pm you on discord if i have any questions / problems!!

oof, I had entirely forgot about this. Rays would probably be the best way to do this yeah. I think Roblox should improve TouchEnded reliability. Possible feature request?

1 Like

yeah, that’d be a good idea
and no worries! it’s alright, i honestly don’t mind at all!! rays at the moment for me though are a bit tough to understand but im sure i can figure out how to implement them

you do not need any of that as you can just check their position as well as their height (or raycast down onto the spike) and if the position is within the bricks bounds (you can check by using the local version of their position, obj.CFrame:toObjectSpace(plr.CFrame) and checking X and Z from corners). the other positive is that you can do euclidean or manhattan distance checks to make spikes that are adjacent appear as well

1 Like