How to make a button do something when you jump on it(super bomb survival style)!

Dont want to make the button yourself, download this model: Jump Button - Roblox

Introduction:
Hello! I’m kinglol(also known as Biack1st) today i’ll show you how to do super bomb survival styled buttons. Aka which are buttons that do something when you jump on them. This can be useful for puzzle games or really anything! For those of you who haven’t player Super Bomb Survival this is the effect:

As you can see, the spectate button will make you spectate when you jump on the button. This tutorial will teach you how to make something happen when you jump on a button

Modeling
Now, my model wont be a masterpiece for the sake of time. This part will tell you how to make the button model

First, make 2 parts, name the one you want for the button: ButtonPart and the base to be named: BasePart.

Making our hit detection
For this segment, we’ll set up our hit detection(no scripting yet, but soon).

First, insert a part, name it: ButtonDetect. After that make the size of the button detect part the same as the button. But make the Y value bigger. So your detect part should look like this:
image

My detect size is: 8, 12, 7.5

After you do that, make the transparency 1 and set cancollide to false.

Once you finish doing that, group the button by doing ctrl + G.

Scripting
Insert a script into the buttonpart
woo hoo! fun part. This part is complicated, but as long as you follow my directions, you’ll be alright

Variables
First, lets start off with our variables.

-- script inside button
local Button = script.Parent -- our button
local RunService = game:GetService("RunService")
local TrackedObjects = {} -- table for the characters that are in range.
local TrackedHumanoid = {} 
local DetectPart = Button.Parent.ButtonDetect -- detect part

Once we do that, lets set up our detection. Our detection will use region 3. Just follow along and you’ll be all good.

local DetectDimensions = Region3.new(DetectPart.Position - DetectPart.Size / 2, DetectPart.Position + DetectPart.Size / 2) -- gets the dimensions for our detect 
local Blacklist = OverlapParams.new() -- this will be our blacklist. We need a blacklist so that our button wont be detected by our detection
Blacklist.FilterType = Enum.RaycastFilterType.Blacklist -- sets the filter type to blacklist
Blacklist.FilterDescendantsInstances = {Button.Parent} -- blacklists the button model and it's decendants 

Next, lets script our action. This will be done via a function. So just make the function’s content do what you want. For me, I want it to change the button’s color.

local function Action()
	print('jumped on')
Button.BrickColor = BrickColor.Random()
end

The next thing we need to do, is make the detection actually do something with what it detects. So let’s start. Make sure too looks at my comments so you can understand what each part of the script is doing.

local function Track()
	local DetectHitbox = workspace:GetPartBoundsInBox(DetectDimensions.CFrame, DetectDimensions.Size, Blacklist) -- gets the part's in our detection region.
	for i,Object in pairs(DetectHitbox) do -- loops through everything inside of the region
		local Character = Object:FindFirstAncestorWhichIsA("Model") -- checks if the object has a model Ancestor 
		if Character and table.find(workspace:GetPartBoundsInBox(DetectDimensions.CFrame, DetectDimensions.Size, Blacklist), Object) then -- checks if the character exists and checks if the object is still inside our detection, as the object could've left by then.
			local Humanoid = Character:FindFirstChildWhichIsA("Humanoid") -- gets the humanoid object of a character
			if Humanoid then -- checks if the humanoid exists
				local Player = game.Players:GetPlayerFromCharacter(Character) -- get the player object from the character. This is to check if the character model we got was an actual player and not a npc.
				if Player and not table.find(TrackedObjects, Character)  then -- checks if the player is there, also checks if the character is NOT apart of trackedobjects table. 
					table.insert(TrackedObjects, Character) -- adds the character to the tracked objects table.
				end
			end
		end
	end
	for i,v in pairs(TrackedObjects) do -- once we add everything inside the tracked objects, we'll start checking when the humanoid jumps.
		local Humanoid = v:FindFirstChildWhichIsA("Humanoid") -- checks if the character has a humanoid
		if Humanoid and not table.find(TrackedHumanoid, Humanoid) then -- checks if the humanoid is NOT a part of the tracked humanoid table
			table.insert(TrackedHumanoid, Humanoid) -- adds the humanoid to the table
			Humanoid.StateChanged:Connect(function(OldState, NewState) -- this will fire everytime a humanoid's state changes. 
				if OldState == Enum.HumanoidStateType.Freefall or OldState == Enum.HumanoidStateType.Jumping then  -- checks if the humanoid was just in the free fall state  or was just in the jumping state.		
					if table.find(TrackedObjects, v) then -- checks if the tracked object is still in the tracked objects folder. As it could've been removed by this time.
						Action() -- fires our action function
						coroutine.yield() -- this is IMPORTANT, this is so that our statechanged function wont fire again.
				end
					
				end
			end)
		end
	end
end

After that, our button should work. BUT there’s 2 issues, first, we never called our Track function, we’ll do that later. AND it’ll still fire after the character left the detection. Which is a huge issue. We’ll start off by creating a untrack function. To stop tracking objects that are outside of the detection
Make sure to read my comments if you dont understand something.

local IsThere = false -- this will be the value to check if the character is still there. 
local function Untrack()
	local NotThereChar -- this is currently nil, but later, we'll set this to a character model
	local DetectHitbox = workspace:GetPartBoundsInBox(DetectDimensions.CFrame, DetectDimensions.Size, Blacklist) -- creates the detect hitbox again
	if DetectHitbox[1] == nil then -- checks if the detect hitbox is empty
		for i,v in pairs(TrackedObjects) do -- loops through everthing
			table.remove(TrackedObjects, i) -- removes the object
		end
		for i,v in pairs(TrackedHumanoid) do -- loops through every humanoid we tracked
			table.remove(TrackedHumanoid, i) -- removes the humanoid from the table.
		end
	end
	for i, Object in pairs(DetectHitbox) do -- loops through everything that was detected in our hitbox
		for i,Character in pairs(TrackedObjects) do -- goes through our tracked objects
			for i,v in pairs(Character:GetDescendants()) do -- loops through all the items of the character. 
				if v == Object then -- checks if the character's part is the object in the hitbox.
					IsThere = true -- sets isthere to true
					break -- breaks all loops so that the isthere wont be set to false again. As some objects of the character may be inside the hitbox, while others not
				else
					NotThereChar = Character -- sets notThereChar to the character.
					IsThere = false -- sets isthere to false
				end
			end
		end
	end
	if IsThere == false then -- checks if is there is false
		table.remove(TrackedObjects, table.find(TrackedObjects, NotThereChar)) -- removes the character from the tracked objects table
		if NotThereChar ~= nil and NotThereChar:IsA("Model") then -- checks if the nottherechar is nil and if it's a model
			table.remove(TrackedHumanoid, table.find(TrackedHumanoid, NotThereChar:FindFirstChildWhichIsA("Humanoid"))) -- removes the character's humanoid from the tracked humanoids.
		end
		
	end
end

--- connections, this is where we connect our track and untrack functions
RunService.Heartbeat:Connect(Untrack)
RunService.Heartbeat:Connect(Track)

and that’s all!

Final Code
-- script inside button
local Button = script.Parent -- our button
local RunService = game:GetService("RunService")
local TrackedObjects = {} -- table for the characters that are in range.
local TrackedHumanoid = {} 
local DetectPart = Button.Parent.ButtonDetect -- detect part
local DetectDimensions = Region3.new(DetectPart.Position - DetectPart.Size / 2, DetectPart.Position + DetectPart.Size / 2) -- gets the dimensions for our detect 
local Blacklist = OverlapParams.new() -- this will be our blacklist. We need a blacklist so that our button wont be detected by our detection
Blacklist.FilterType = Enum.RaycastFilterType.Blacklist -- sets the filter type to blacklist
Blacklist.FilterDescendantsInstances = {Button.Parent} -- blacklists the button model and it's decendants 
local function Action()
	print('jumped on')
Button.BrickColor = BrickColor.Random()
end
local function Track()
	local DetectHitbox = workspace:GetPartBoundsInBox(DetectDimensions.CFrame, DetectDimensions.Size, Blacklist) -- gets the part's in our detection region.
	for i,Object in pairs(DetectHitbox) do -- loops through everything inside of the region
		local Character = Object:FindFirstAncestorWhichIsA("Model") -- checks if the object has a model Ancestor 
		if Character and table.find(workspace:GetPartBoundsInBox(DetectDimensions.CFrame, DetectDimensions.Size, Blacklist), Object) then -- checks if the character exists and checks if the object is still inside our detection, as the object could've left by then.
			local Humanoid = Character:FindFirstChildWhichIsA("Humanoid") -- gets the humanoid object of a character
			if Humanoid then -- checks if the humanoid exists
				local Player = game.Players:GetPlayerFromCharacter(Character) -- get the player object from the character. This is to check if the character model we got was an actual player and not a npc.
				if Player and not table.find(TrackedObjects, Character)  then -- checks if the player is there, also checks if the character is NOT apart of trackedobjects table. 
					table.insert(TrackedObjects, Character) -- adds the character to the tracked objects table.
				end
			end
		end
	end
	for i,v in pairs(TrackedObjects) do -- once we add everything inside the tracked objects, we'll start checking when the humanoid jumps.
		local Humanoid = v:FindFirstChildWhichIsA("Humanoid") -- checks if the character has a humanoid
		if Humanoid and not table.find(TrackedHumanoid, Humanoid) then -- checks if the humanoid is NOT a part of the tracked humanoid table
			table.insert(TrackedHumanoid, Humanoid) -- adds the humanoid to the table
			Humanoid.StateChanged:Connect(function(OldState, NewState) -- this will fire everytime a humanoid's state changes. 
				if OldState == Enum.HumanoidStateType.Freefall or OldState == Enum.HumanoidStateType.Jumping then  -- checks if the humanoid was just in the free fall state  or was just in the jumping state.		
					if table.find(TrackedObjects, v) then -- checks if the tracked object is still in the tracked objects folder. As it could've been removed by this time.
						Action() -- fires our action function
						coroutine.yield() -- this is IMPORTANT, this is so that our statechanged function wont fire again.
				end
					
				end
			end)
		end
	end
end
local IsThere = false -- this will be the value to check if the character is still there. 
local function Untrack()
	local NotThereChar -- this is currently nil, but later, we'll set this to a character model
	local DetectHitbox = workspace:GetPartBoundsInBox(DetectDimensions.CFrame, DetectDimensions.Size, Blacklist) -- creates the detect hitbox again
	if DetectHitbox[1] == nil then -- checks if the detect hitbox is empty
		for i,v in pairs(TrackedObjects) do -- loops through everthing
			table.remove(TrackedObjects, i) -- removes the object
		end
		for i,v in pairs(TrackedHumanoid) do -- loops through every humanoid we tracked
			table.remove(TrackedHumanoid, i) -- removes the humanoid from the table.
		end
	end
	for i, Object in pairs(DetectHitbox) do -- loops through everything that was detected in our hitbox
		for i,Character in pairs(TrackedObjects) do -- goes through our tracked objects
			for i,v in pairs(Character:GetDescendants()) do -- loops through all the items of the character. 
				if v == Object then -- checks if the character's part is the object in the hitbox.
					IsThere = true -- sets isthere to true
					break -- breaks all loops so that the isthere wont be set to false again. As some objects of the character may be inside the hitbox, while others not
				else
					NotThereChar = Character -- sets notThereChar to the character.
					IsThere = false -- sets isthere to false
				end
			end
		end
	end
	if IsThere == false then -- checks if is there is false
		table.remove(TrackedObjects, table.find(TrackedObjects, NotThereChar)) -- removes the character from the tracked objects table
		if NotThereChar ~= nil and NotThereChar:IsA("Model") then -- checks if the nottherechar is nil and if it's a model
			table.remove(TrackedHumanoid, table.find(TrackedHumanoid, NotThereChar:FindFirstChildWhichIsA("Humanoid"))) -- removes the character's humanoid from the tracked humanoids.
		end
		
	end
end

--- connections, this is where we connect our track and untrack functions
RunService.Heartbeat:Connect(Untrack)
RunService.Heartbeat:Connect(Track)



FINAL RESULT

Polls

As this is my first tutorial, i’d like some feedback via polls. Make sure to answer these!

From 1-10 how useful was this to you?

From 1-10 how useful was this to you
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

0 voters

Did you learn anything new?

Did you learn anything new?
  • Yes
  • No

0 voters

How likely are you to use this?

  • I’ll surely use this.
  • I might use it.
  • No, not really.

0 voters

Outro
I really hope that this could help you in any way! This style of buttons are pretty unique in my opinion, as not many games do this. These types of buttons are especially good for puzzle games. Or if you want to make your game have a unique style of buttons! If you have any questions, feedback, feel like I missed something or anything i could’ve done better then feel free to say it below!

10 Likes

I really like this and thank you for sharing it. Would it be the same to use region3 to detect instead of a hitbox?

Well the region3 is our hitbox. Dont use .touched since it can sometimes not detect stuff

Cool, but isn’t region3 deprecated? Also you should make the hitbox script simpler

It uses region 3 to calculate the dimensions but uses overlap parmas to get the items inside the hitbox. Also, region 3 isn’t deprecated. The old methods like :getitemsinregionwithignorelist are deprecated.

oh yeah, I’m not so experienced in region3, thanks for clarifying

1 Like

Its fine. We all learn from mistakes.

1 Like

tbh non of the videos loaded it glitches

Yeah, the videos seem to be corrupted.

1 Like