Stage Selector Acting Weird

Hello.

I’m scripting an upcoming obby, and I’ve run into some issues whilst scripting the stage selector. As seen in the video attached below, there are a few things that aren’t working correctly. I can’t seem to put my finger on any of the root causes, so I’ve listed what can be observed.

  • The stage selector buffers when I try to move from stage 16 to stage 15. Additionally, my player somehow takes damage. I’m aware that there are parts that damage the player on stage 15, but my script doesn’t take the player’s character anywhere near them.

  • It works normally when I change stages at a normal rate; however, when I start spamming my clicks, the output goes crazy. The stage selector starts buffering as well. I’m pretty sure I need to add some sort of delay so that the server finishes responding to the client, but I feel like I’m doing it wrong.

Here’s the video. As a side note, everything takes 3 seconds to load at the beginning because I added a delay.

I have code on both the server and client for this system. I’m using ProfileStore to save data, so I’ve attached a relevant part of one of my data modules as well.

Server
local dataManager = require(game.ServerScriptService:WaitForChild("Core"):WaitForChild("DataManager"))
local remoteEvents = game.ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("Events")

game.Players.PlayerAdded:Connect(function(player)
		
	while not dataManager.Profiles[player] do
		task.wait()
	end
	
	local profile = dataManager.Profiles[player]
	
	if not profile then
		return
	end
	
	task.wait(3)
	
	remoteEvents.SendStageData:FireClient(player, profile.Data.Stage)
	
end)

remoteEvents.StageSelectorRequest.OnServerEvent:Connect(function(player, target)
	
	local profile = dataManager.Profiles[player]
	
	if not profile then
		return
	end
	
	local maxStage = profile.Data.Stage
	
	if target < 1 or target > maxStage then
		return
	end
	
	player.leaderstats.Stage.Value = target
	remoteEvents.StageSelectorConfirm:FireClient(player, target, workspace.Checkpoints:FindFirstChild(tostring(target)).Checkpoint.CFrame)
		
end)
Client
local remoteEvents = game.ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("Events")
local checkpoints = workspace:WaitForChild("Checkpoints")
local player = game.Players.LocalPlayer
local stage = player:WaitForChild("leaderstats"):WaitForChild("Stage")
local playerGui = player:WaitForChild("PlayerGui")
local stageSelector = playerGui:WaitForChild("UI"):WaitForChild("Top"):WaitForChild("Frame"):WaitForChild("StageSelector")
local left1 = stageSelector:WaitForChild("Left1")
local left10 = stageSelector:WaitForChild("Left10")
local right1 = stageSelector:WaitForChild("Right1")
local right10 = stageSelector:WaitForChild("Right10")
local text = stageSelector:WaitForChild("StageNumber")
local debounce = false
local stageData
local currentStage

local function updateVisibility()
	left1.Visible = currentStage > 1
	left10.Visible = currentStage > 10
	right1.Visible = currentStage < stageData
	right10.Visible = currentStage <= stageData - 10
end

remoteEvents.SendStageData.OnClientEvent:Connect(function(maxStage)
	
	stageData = maxStage
	
	repeat task.wait() until stage.Value ~= nil
	
	currentStage = stage.Value
	text.Text = "STAGE " .. currentStage
	updateVisibility()
	
	print(currentStage)
	
end)

remoteEvents.StageSelectorConfirm.OnClientEvent:Connect(function(newStage, position)
	
	currentStage = newStage
	
	if player.Character and player.Character.PrimaryPart then
		player.Character:PivotTo(position + Vector3.new(0, 2, 0))
	end
	
	text.Text = "STAGE " .. currentStage
	updateVisibility()
	debounce = false
	
end)

remoteEvents.BiomeTravelConfirm.OnClientEvent:Connect(function(newStage, position)
	currentStage = newStage
	text.Text = "STAGE " .. currentStage
	updateVisibility()
	debounce = false
end)

left1.MouseButton1Click:Connect(function()
	
	print(currentStage)
	
	if debounce == true or currentStage <= 1 then
		return
	end
	
	debounce = true
	remoteEvents.StageSelectorRequest:FireServer(currentStage - 1)
	
end)

left10.MouseButton1Click:Connect(function()
	
	if debounce == true or currentStage <= 10 then
		return
	end
	
	debounce = true
	remoteEvents.StageSelectorRequest:FireServer(currentStage - 10)
	
end)

right1.MouseButton1Click:Connect(function()
	
	if debounce == true or currentStage >= stageData then
		return
	end
	
	debounce = true
	remoteEvents.StageSelectorRequest:FireServer(currentStage + 1)
	
end)

right10.MouseButton1Click:Connect(function()
	
	if debounce == true or currentStage > stageData - 10 then
		return
	end
	
	debounce = true
	remoteEvents.StageSelectorRequest:FireServer(currentStage + 10)
	
end)
Data Module
function dataManager:UpdateStage(player, newStage)

	local profile = dataManager.Profiles[player]
	
	if not profile then
		return
	end
	
	player.leaderstats.Stage.Value = newStage
	
	if newStage > profile.Data.Stage then
		profile.Data.Stage = newStage
	end
	
	remoteEvents.UpdateStage:FireClient(player, newStage)
	remoteEvents.SendStageData:FireClient(player, profile.Data.Stage)	
		
end

If anyone could explain why these issues are happening and how I can fix it, I’d greatly appreciate it.

Thank you!

I think it would be helpful to print the parts that are touching the player’s name. It would give you an idea of how to solve the bug. :happy1:

I added a snippet of code to do that, and the statement printed a few times.

I don’t know how it’s possible for the player’s character to touch those parts, though. I’m simply moving it to the corresponding stage. Do you know why this might be happening?

I managed to fix it. I added a cooldown of 0.2 seconds between clicks and moved the CFrame logic to the server. I’m not sure if this is the best solution, but it’s what works.

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