
KeylessUniversal

--[[
ButtonTourTeleport (LocalScript)
Place in StarterGui or StarterPlayerScripts.
Creates a small GUI with a "Start Tour" button. When pressed, it
teleports the player's character to parts named _Button1, _Button2,
..., _Button20 (waiting 1 second between each), then finally
teleports to a part named ExitRegion.
Parts are searched for in workspace. Adjust FindPart() below if your
button parts live in a specific folder/model instead of directly in
workspace.
]]
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
----------------------------------------------------------------
-- CONFIG
----------------------------------------------------------------
local NUM_BUTTONS = 20
local WAIT_TIME = 1 -- seconds between each teleport
local EXIT_PART_NAME = "ExitRegion"
----------------------------------------------------------------
-- GUI CREATION
----------------------------------------------------------------
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "ButtonTourGui"
screenGui.ResetOnSpawn = false
screenGui.Parent = playerGui
local frame = Instance.new("Frame")
frame.Name = "MainFrame"
frame.Size = UDim2.new(0, 220, 0, 130)
frame.Position = UDim2.new(0, 20, 0.5, -65)
frame.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
frame.BorderSizePixel = 0
frame.Parent = screenGui
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 8)
corner.Parent = frame
local statusLabel = Instance.new("TextLabel")
statusLabel.Name = "StatusLabel"
statusLabel.Size = UDim2.new(1, -20, 0, 24)
statusLabel.Position = UDim2.new(0, 10, 0, 6)
statusLabel.BackgroundTransparency = 1
statusLabel.Text = "Ready"
statusLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
statusLabel.Font = Enum.Font.GothamBold
statusLabel.TextSize = 16
statusLabel.Parent = frame
local startButton = Instance.new("TextButton")
startButton.Name = "StartButton"
startButton.Size = UDim2.new(1, -20, 0, 40)
startButton.Position = UDim2.new(0, 10, 0, 36)
startButton.BackgroundColor3 = Color3.fromRGB(0, 140, 255)
startButton.Text = "Start Tour"
startButton.TextColor3 = Color3.fromRGB(255, 255, 255)
startButton.Font = Enum.Font.GothamBold
startButton.TextSize = 18
startButton.AutoButtonColor = true
startButton.Parent = frame
local buttonCorner = Instance.new("UICorner")
buttonCorner.CornerRadius = UDim.new(0, 6)
buttonCorner.Parent = startButton
local restartButton = Instance.new("TextButton")
restartButton.Name = "RestartButton"
restartButton.Size = UDim2.new(1, -20, 0, 40)
restartButton.Position = UDim2.new(0, 10, 0, 82)
restartButton.BackgroundColor3 = Color3.fromRGB(255, 90, 40)
restartButton.Text = "Restart"
restartButton.TextColor3 = Color3.fromRGB(255, 255, 255)
restartButton.Font = Enum.Font.GothamBold
restartButton.TextSize = 18
restartButton.AutoButtonColor = true
restartButton.Parent = frame
local restartButtonCorner = Instance.new("UICorner")
restartButtonCorner.CornerRadius = UDim.new(0, 6)
restartButtonCorner.Parent = restartButton
----------------------------------------------------------------
-- HELPERS
----------------------------------------------------------------
local currentRunId = 0 -- incremented each time a (re)start happens; used to cancel stale runs
local function findPart(name)
-- Searches the whole workspace for a part/model with this name.
local inst = Workspace:FindFirstChild(name, true)
return inst
end
local function findHitbox(buttonInst)
-- Given a _ButtonN instance (likely a Model), find its "Hitbox" child part.
-- Falls back to the instance itself if it has no Hitbox child.
if not buttonInst then
return nil
end
local hitbox = buttonInst:FindFirstChild("Hitbox", true)
if hitbox and hitbox:IsA("BasePart") then
return hitbox
end
return buttonInst
end
local function teleportCharacterTo(inst)
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:FindFirstChild("HumanoidRootPart")
if not hrp then
return false
end
if not inst or not inst:IsA("BasePart") then
return false
end
-- Position on the part's top face, then nudge 0.5 studs down into it
-- (in the part's own local space, so this works even if it's rotated).
local topOffset = (inst.Size.Y / 2) - 0.5
local targetCFrame = inst.CFrame * CFrame.new(0, topOffset, 0)
hrp.CFrame = targetCFrame
return true
end
----------------------------------------------------------------
-- MAIN TOUR LOGIC
----------------------------------------------------------------
local function runTour()
currentRunId = currentRunId + 1
local myRunId = currentRunId
startButton.Active = false
startButton.AutoButtonColor = false
startButton.BackgroundColor3 = Color3.fromRGB(90, 90, 90)
for i = 1, NUM_BUTTONS do
if myRunId ~= currentRunId then
return -- a newer run (restart) has taken over; stop this one
end
local partName = "_Button" .. i
statusLabel.Text = "Going to " .. partName
local part = findPart(partName)
if part then
local hitbox = findHitbox(part)
teleportCharacterTo(hitbox)
else
warn("[ButtonTourTeleport] Could not find part named: " .. partName)
statusLabel.Text = "Missing: " .. partName
end
task.wait(WAIT_TIME)
end
if myRunId ~= currentRunId then
return
end
statusLabel.Text = "Going to " .. EXIT_PART_NAME
local exitPart = findPart(EXIT_PART_NAME)
if exitPart then
local exitTarget = exitPart
if not exitPart:IsA("BasePart") then
-- ExitRegion might be a Model; grab its PrimaryPart or first BasePart
exitTarget = exitPart.PrimaryPart or exitPart:FindFirstChildWhichIsA("BasePart", true)
end
teleportCharacterTo(exitTarget)
else
warn("[ButtonTourTeleport] Could not find part named: " .. EXIT_PART_NAME)
statusLabel.Text = "Missing: " .. EXIT_PART_NAME
end
if myRunId ~= currentRunId then
return
end
statusLabel.Text = "Done!"
task.wait(1)
if myRunId == currentRunId then
statusLabel.Text = "Ready"
startButton.Active = true
startButton.AutoButtonColor = true
startButton.BackgroundColor3 = Color3.fromRGB(0, 140, 255)
end
end
startButton.MouseButton1Click:Connect(function()
task.spawn(runTour)
end)
restartButton.MouseButton1Click:Connect(function()
-- Bumping currentRunId cancels any in-progress run on its next check,
-- then we immediately kick off a fresh one.
task.spawn(runTour)
end)No comments yet for this script.



