What does this module do?
I made a module couple weeks ago and I thought I would share it with the community. This is an easy to use module that provides the functionality of adding a “bubble click” effect to any GUI object(demonstrations will be shown later in the post). The module works by calling it’s method “BindObjs” which takes as argument a table containing the GUI object(s) and then a connection is created to detect when the user clicks or taps any of the binded GUI object(s)
Methods of the module:
function module:BindObjs(objs:{[Instance]: boolean|{Color:Color3?, Gpe:boolean?, Tinfo:TweenInfo?, nilIdentifier:any?, ImageTransparency:number?, Image:string?, Callback:(GuiObject)->()?}}, SecondaryFilterClasses:{string}?, blacklist:boolean?)
:BindObjs()
This method is very flexible which makes it easy to use. This method allows to bind GUI object(s) in one of of 6 ways which includes the ability to add optional arguments to specific GUI objects!
Current optional arguments available:
Keep in mind that the default behavior can be edited in the module itself
“Color”: a color3 object the click effect image will be for the specific GUI object (The color of the click effect image is black by default)
“Gpe” aka the GameProcessedEvent argument of UserInputService.InputBegan: a boolean indicating what the GameProcessedEvent argument needs to be in order for the click effect to be triggered for that specific GUI object (It doesn’t take into account the GameProcessedEvent argument of UserInputService.InputBegan by default)
“Callback”: a callback function that is called when the click effect is done tweening on the GUI object (there is no callback by default)
“nilIdentifier”: The purpose of this is to act as a substitute for nil since it can’t be stored in a normal table so you can use specified nilIdentifier or the default when you want to set a custom setting as nil ("nil" is the default identifier)
“ImageTransparency”: This indicates what the transparency of the image used for the click effect should start as. (0 is the default)
“Image”: The indicates what the “Image” property of the click effect image should be (the possibilities of the effects you can create are basically endless!). (“rbxassetid://6193144138” is the default (basically a white circle))
- Binding a single GUI object:
local object = *GUI object*
module:BindObjs(object)
- Binding GUI objects in a simple array like form:
local object1 = *GUI object*
local object2 = *GUI object*
local object3 = *GUI object*
module:BindObjs({object1,object2,object3,object...})
- Binding the children/descendants of a GUI and letting the module do the filtering for you! (This can be used with the second argument “SecondaryFilterClasses” and third argument “blacklist” to further filter out GUI object(s) you don’t/do want to apply the effect to!) (an example is a ScreenGui)
local ScreenGui = *GUI*
module:BindObjs(ScreenGui:GetChildren(), {"TextButton", "ImageButton"}) --Only gui objects that are TextButtons and ImageButtons will have the effect applied!
*OR*
local ScreenGui = *GUI*
module:BindObjs(ScreenGui:GetChildren(), {"TextButton", "ImageButton"}, true) --All GUI objects but TextButtons and ImageButtons will have the effect applied!
- Binding GUI objects in a dictionary like form:
local object1 = *GUI object*
local object2 = *GUI object*
local object3 = *GUI object*
module:BindObjs({
[object1] = true,
[object2]= true,
[object3] = true,
[object...] = true,
})
- Binding GUI objects in dictionary like form with custom settings!
local object1 = *GUI object*
local object2 = *GUI object*
local function OnClickEffectTweened(object:GuiObject)
print(object.Name.. " finished it's click effect!")
end
module:BindObjs({
[object1] = {Color = Color3.new(1,1,1), Tinfo = TweenInfo.new(0.1, Enum.EasingStyle.Sine), Gpe = true, Callback = OnClickEffectTweened},
[object2] = {Color = Color3.new(1,0,1), Tinfo = TweenInfo.new(0.5, Enum.EasingStyle.Quart), Gpe = false, Callback = OnClickEffectTweened},
object... = ...,
})
- Simply updating the custom settings for a specific GUI object! (keep in mind that previous settings of a GUI object will remain the same if you don’t specify when updating it’s custom settings)
local object1 = *GUI object*
local object2 = *GUI object*
local function OnClickEffectTweened(object:GuiObject)
print(object.Name.. " finished it's click effect!")
end
module:BindObjs({
[object1] = {Color = Color3.new(1,1,1), Tinfo = TweenInfo.new(0.1, Enum.EasingStyle.Sine), Gpe = true, Callback = OnClickEffectTweened},
[object2] = {Color = Color3.new(1,0,1), Tinfo = TweenInfo.new(0.5, Enum.EasingStyle.Quart), Gpe = false, Callback = OnClickEffectTweened},
[object...] = ...,
})
*later on in script*
module:BindObjs({
[object1] = {Color = Color3.new(.5,.2,.9), Tinfo = TweenInfo.new(0.75, Enum.EasingStyle.Bounce), Gpe = "nil", Callback = "nil"},
[object...] = ...,
})
function module:UnbindObjs(objs:{GuiObject} | {[GuiObject]: any} | GuiObject, SecondaryFilterClasses:{string}?, blacklist:boolean?)
:UnbindObjs()
This method is used is unbind GUI object(s) from the effect temporarily so you can call :BindObjs() on them later on in your script. You can unbind GUI object(s) in one of 4 ways.
- Unbinding a single GUI object (Might as well use :RemoveObjs() since the main purpose of this method is to temporarily disable the effect while still saving it’s custom setting and you can’t do that with single objects without using a table)
local object = *GUI object*
module:BindObjs(object)
*later in the script*
module:Unbind(object)
- Unbinding an array of GUI objects
local object1 = *GUI object*
local object2 = *GUI object*
local object3 = *GUI object*
module:BindObjs({
[object1] = ...,
[object2] = ...,
[object3] = ...,
[object...] = ...,
})
*later on in the script*
module:UnbindObjs({object1,object2,object3,object...})
- Unbinding the children/descendants of a GUI and letting the module do the filtering for you! (This can also be used with the second argument “SecondaryFilterClasses” and third argument “blacklist” to further filter out GUI object(s) you don’t/do want to unbind!) (an example is a ScreenGui)
local ScreenGui = *GUI*
module:BindObjs(ScreenGui:GetChildren())
Later in the script
module:UnbindObjs(ScreenGui:GetChildren(), {"TextButton", "ImageButton"}, true) --All GUI objects but TextButtons and ImageButtons will be unbinded!
*OR*
module:UnbindObjs(ScreenGui:GetChildren(), {"TextButton", "ImageButton"}) --Only GUI objects that are TextButtons and ImageButtons will be unbinded!
- Unbinding a dictionary of GUI objects
local object1 = *GUI object*
local object2 = *GUI object*
local object3 = *GUI object*
module:BindObjs({
[object1] = ...,
[object2] = ...,
[object3] = ...,
[object...] = ...,
})
*later on in the script*
module:UnbindObjs({
[object1] = true,
[object2]= true,
[object3] = true,
[object...] = true,
})
function BubbleClick:RemoveObjs(objs:{GuiObject}|{[GuiObject]: boolean}|GuiObject, SecondaryFilterClasses:{string}?, blacklist:boolean?)
:RemoveObjs()
The same as “:UnbindObjs” except it’s rather permanent than temporary (useful when you don’t longer need the effect on a GUI object)
function module:SetSettings(UpdatedSettings:{DefaultTinfo:TweenInfo?, DefaultColor:Color3?, DefaultGpeBehavior:boolean?, DefaultCallback:(obj:GuiObject)->()?, DefaultnilIdentifier:any?, DefaultImageTransparency:number?, DefaultImage:string?})
:SetSettings()
This method is used when you want to update specific default settings (this is useful when you want different default behaviors for different scripts)
function module:Simulate(v:GuiObject, Pos:Vector2, ObjSettings:{Color:Color3?, Tinfo:TweenInfo?, Callback:()->()?, ImageTransparency:number?, Image:string?}?)
:Simulate()
This is a method you won’t really interact with directly often but is there incase you wanted to manually trigger the effect on a GUI object without the need of user interaction.
Explaining the argument
“v”: The GUI object you want to apply the effect on
“Pos”: A Vector2 object containing the X and Y scale where the effect should originate from relative the object’s position
“ObjSettings”: An optional argument for custom settings for the effect
function module:InBound(obj:GuiObject,x:number,y:number):Vector2?
:InBound()
This function returns a Vector2 object containing the X and Y scale relative to “obj” if the x and y pixel coordinates are within bound of “obj”
Code used to achieve the above (num1, num1B):
local gui = script.Parent
local rep = game:GetService("ReplicatedStorage")
local module = require(rep:WaitForChild("ClickEffectModule"))
repeat task.wait() until #gui:GetChildren() == #game:GetService("StarterGui").ScreenGui:GetChildren()
--
local objs = {}
local index = 0
local cols = {
Color3.fromRGB(0,0,0),
Color3.fromRGB(255,0,0),
Color3.fromRGB(0,255,0),
Color3.fromRGB(0,0,255),
Color3.fromRGB(255,255,0),
Color3.fromRGB(0,255,255),
Color3.fromRGB(255,0,255),
Color3.fromRGB(255, 85, 0),
Color3.fromRGB(85, 0, 0),
Color3.fromRGB(170, 85, 255),
}
for _,v in pairs(gui:GetChildren()) do
if v:IsA("GuiObject") then
local col = cols[(index%#cols)+1]
index += 1
objs[v] = {Color = col}
end
end
module:BindObjs(objs)
Code used to achieve the above(num2):
local gui = script.Parent
local rep = game:GetService("ReplicatedStorage")
local module = require(rep:WaitForChild("ClickEffectModule"))
repeat task.wait() until #gui:GetChildren() == #game:GetService("StarterGui").ScreenGui:GetChildren()
--
while true do
task.wait(.5)
module:Simulate(gui.TextButton, Vector2.new(.5,.5))
end
Code used to achieve the above(num3):
local gui = script.Parent
local rep = game:GetService("ReplicatedStorage")
local module = require(rep:WaitForChild("ClickEffectModule"))
repeat task.wait() until #gui:GetChildren() == #game:GetService("StarterGui").ScreenGui:GetChildren()
--
while true do
task.wait(.5)
for _,v in pairs(gui:GetChildren()) do
if v:IsA("GuiObject") then
module:Simulate(v, Vector2.new(.5,.5))
end
end
end
Code used to achieve the above(num4):
local gui = script.Parent
local rep = game:GetService("ReplicatedStorage")
local module = require(rep:WaitForChild("ClickEffectModule"))
repeat task.wait() until #gui:GetChildren() == #game:GetService("StarterGui").ScreenGui:GetChildren()
--
while true do
task.wait(.5)
for _,v in pairs(gui:GetChildren()) do
if v:IsA("GuiObject") then
module:Simulate(v, Vector2.new(.5,.5), {Color = Color3.fromRGB(math.random(0,255), math.random(0,255), math.random(0,255))})
end
end
end
Code used to achieve the above(num5):
local gui = script.Parent
local rep = game:GetService("ReplicatedStorage")
local module = require(rep:WaitForChild("ClickEffectModule"))
repeat task.wait() until #gui:GetChildren() == #game:GetService("StarterGui").ScreenGui:GetChildren()
--
module:SetSettings({DefaultImage = "rbxassetid://378834872"})
while true do
task.wait(.5)
for _,v in pairs(gui:GetChildren()) do
if v:IsA("GuiObject") then
module:Simulate(v, Vector2.new(.5,.5), {Color = Color3.fromRGB(math.random(0,255), math.random(0,255), math.random(0,255))})
end
end
end
Code used to achieve the above(num6):
module:SetSettings({DefaultTinfo = TweenInfo.new(1.5, Enum.EasingStyle.Bounce), DefaultImage = "rbxassetid://378834872"})
while true do
task.wait(.5)
for _,v in pairs(gui:GetChildren()) do
if v:IsA("GuiObject") then
module:Simulate(v, Vector2.new(.5,.5), {Color = Color3.fromRGB(math.random(0,255), math.random(0,255), math.random(0,255))})
end
end
end
Code used to achieve the above(num7):
module:SetSettings({DefaultImage = "rbxassetid://1249690853"})
while true do
task.wait(.5)
for _,v in pairs(gui:GetChildren()) do
if v:IsA("GuiObject") then
module:Simulate(v, Vector2.new(.5,.5), {Color = Color3.fromRGB(math.random(0,255), math.random(0,255), math.random(0,255))})
end
end
end
Link to get the module: BubbleClickEffectModule - Roblox
Module Source Code!
local BubbleClick = {}
local Binded = {}
--Services
local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local Tservice = game:GetService("TweenService")
--Player related stuff
local plr = Players.LocalPlayer
local Mouse = plr:GetMouse()
--Preset
local BubbleEffect = script:WaitForChild("BubbleEffectPreset")
--local function
local function Coroutine(func:()->(),...)
--coroutine.resume(coroutine.create(func), ...)
coroutine.resume(coroutine.create(function(...)
local suc,err = pcall(func,...)
if not suc then warn("[Bubble Click Effect] please report:", err) end
end),...)
--[[You can also do:
task.spawn(func, ...)
--]]
end
--Settings ref
local Settings = {
DefaultTinfo = TweenInfo.new(1,Enum.EasingStyle.Linear, Enum.EasingDirection.Out),
DefaultColor = Color3.new(),
DefaultGpeBehavior = nil,
DefaultCallback = nil,
DefaultnilIdentifier = "nil",
DefaultImageTransparency = 0,
DefaultImage = "rbxassetid://6193144138",
}
local acceptedClasses = {
Frame = true;
ImageButton = true;
ImageLabel = true;
TextButton = true;
TextLabel = true;
ScrollingFrame = true;
ViewportFrame = true;
}
local emptyTab = {}
local connection:RBXScriptConnection = nil
local function Disconnect()
if connection and connection.Connected then
connection:Disconnect()
connection = nil
end
end
local function CreateConnection()
connection = UIS.InputBegan:Connect(function(input, gpe)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
local x,y = Mouse.X,Mouse.Y
for obj,v in pairs(Binded) do
if v.Paused then continue end
local objGpeBehavior = v.Gpe
if (objGpeBehavior and gpe ~= objGpeBehavior) or (Settings.DefaultGpeBehavior ~= nil and objGpeBehavior == nil and gpe ~= Settings.DefaultGpeBehavior) then continue end
if not obj.Visible then continue end
local Pos = BubbleClick:InBound(obj,x,y)
if Pos then
BubbleClick:Simulate(obj, Pos, v)
end
end
end
end)
end
function BubbleClick:InBound(obj:GuiObject,x:number,y:number):Vector2?
local AbsPos = obj.AbsolutePosition
local AbsSize = obj.AbsoluteSize
return (x>AbsPos.X and x<AbsPos.X+AbsSize.X and y>AbsPos.Y and y<AbsPos.Y+AbsSize.Y and Vector2.new((x-AbsPos.X)/AbsSize.X,(y-AbsPos.Y)/AbsSize.Y)) or nil
end
function BubbleClick:Simulate(v:GuiObject, Pos:Vector2, ObjSettings:{Color:Color3?, Tinfo:TweenInfo?, Callback:()->()?, ImageTransparency:number?, Image:string?}?)
Coroutine(function()
ObjSettings = ObjSettings or {}
local AbsSize = v.AbsoluteSize
local BubbleEffectClone = BubbleEffect:Clone()
BubbleEffectClone.Position = UDim2.new(0,0,0,0)
BubbleEffectClone.Size = UDim2.new(1,0,1,0)
BubbleEffectClone.Image.Image = ObjSettings.Image or Settings.DefaultImage
BubbleEffectClone.Image.ImageTransparency = ObjSettings.ImageTransparency or Settings.DefaultImageTransparency
BubbleEffectClone.Image.ImageColor3 = ObjSettings.Color or Settings.DefaultColor
BubbleEffectClone.Image.AnchorPoint = Vector2.new(0.5,0.5)
BubbleEffectClone.Image.Position = UDim2.new(Pos.X, 0, Pos.Y, 0)
BubbleEffectClone.Parent = v
local Magnitude = (Vector2.one*0.5 - Pos).Magnitude
local SizeRatio = math.max(AbsSize.X,AbsSize.Y) / math.min(AbsSize.X,AbsSize.Y)
local Size = UDim2.fromScale(2 * (1+Magnitude) * SizeRatio, 2 * (1+Magnitude) * SizeRatio)
local Tween = Tservice:Create(BubbleEffectClone.Image, ObjSettings.Tinfo or Settings.DefaultTinfo, {ImageTransparency = 1, Size = Size})
Tween:Play()
Tween.Completed:Wait()
BubbleEffectClone:Destroy()
if type(ObjSettings.Callback) == "function" then
ObjSettings.Callback(v)
elseif type(Settings.DefaultCallback) == "function" then
Settings.DefaultCallback(v)
end
end)
end
function BubbleClick:BindObjs(objs:{[Instance]: boolean|{Color:Color3?, Gpe:boolean?, Tinfo:TweenInfo?, nilIdentifier:any?, ImageTransparency:number?, Image:string?, Callback:(GuiObject)->()?}}, SecondaryFilterClasses:{string}?, blacklist:boolean?)
objs = (typeof(objs) == "Instance" and {objs=true}) or (typeof(objs) == "table" and objs) or nil
if not objs then return end
for i,v in pairs(objs) do
local indexType = type(i)
local obj = (indexType == "number" or indexType == "string") and v or i
local val = v
if typeof(obj) == "Instance" and acceptedClasses[obj.ClassName] then
if SecondaryFilterClasses then
local found = table.find(SecondaryFilterClasses, obj.ClassName)
if ((not blacklist and not found) or (blacklist and found)) then continue end
end
local objSettings = Binded[obj] or {}
local UpdatedObjSettings = type(v) == "table" and v or emptyTab
for j,k in pairs(UpdatedObjSettings) do
if j ~= "nilIdentifier" then
if k == objSettings.nilIdentifier or k == Settings.DefaultnilIdentifier then
objSettings[j] = nil
end
end
end
local objFinalSettings = {
Color = objSettings.Color,
Callback = objSettings.Callback,
Gpe = objSettings.Gpe,
Tinfo = objSettings.Tinfo,
ImageTransparency = objSettings.ImageTransparency,
Image = objSettings.Image,
Paused = nil,
}
Binded[obj] = objFinalSettings
end
end
if not connection then
CreateConnection()
end
end
function BubbleClick:UnbindObjs(objs:{GuiObject} | {[GuiObject]: any} | GuiObject, SecondaryFilterClasses:{string}?, blacklist:boolean?)
objs = (typeof(objs) == "Instance" and {objs}) or (typeof(objs) == "table" and objs) or nil
if not objs then return end
for i,v in pairs(objs) do
local indexType = type(i)
local obj = (indexType == "number" or indexType == "string") and v or i
if SecondaryFilterClasses then
local found = table.find(SecondaryFilterClasses, obj.ClassName)
if ((not blacklist and not found) or (blacklist and found)) then continue end
end
if Binded[obj] then
Binded[obj].Paused = true
end
end
for _,_ in pairs(Binded) do return end
Disconnect()
end
function BubbleClick:RemoveObjs(objs:{GuiObject}|{[GuiObject]: boolean}|GuiObject, SecondaryFilterClasses:{string}?, blacklist:boolean?)
objs = (typeof(objs) == "Instance" and {objs}) or (typeof(objs) == "table" and objs) or nil
if not objs then return end
for i,v in pairs(objs) do
local indexType = type(i)
local obj = (indexType == "number" or indexType == "string") and v or i
if SecondaryFilterClasses then
local found = table.find(SecondaryFilterClasses, obj.ClassName)
if ((not blacklist and not found) or (blacklist and found)) then continue end
end
Binded[obj] = nil
end
for _,_ in pairs(Binded) do return end
Disconnect()
end
function BubbleClick:SetSettings(UpdatedSettings:{DefaultTinfo:TweenInfo?, DefaultColor:Color3?, DefaultGpeBehavior:boolean?, DefaultCallback:(obj:GuiObject)->()?, DefaultnilIdentifier:any?, DefaultImageTransparency:number?, DefaultImage:string?})
Settings.DefaultnilIdentifier = UpdatedSettings.DefaultnilIdentifier or Settings.DefaultnilIdentifier
local nilIdentifier = Settings.DefaultnilIdentifier
for i,v in pairs(UpdatedSettings) do
if i ~= "DefaultnilIdentifier" and Settings[i] then
local val = v ~= nilIdentifier and v or nil
Settings[i] = val
end
end
end
return BubbleClick
Sorry if the video doesn’t load, not much I can do about it
I am sorry for not documenting my code. This is because I spent a lot of time explaining and giving examples in this post (I might update the module to add some though). Also, this only supports Pc and mobile/tablet as I do not own an Xbox which makes making codes Xbox compatible hard! If requested though, I will look into it!
Poll Time!
How likely are you to use this module?
- Very likely
- Likely
- I will consider
- maybe?
- Not likely
- Other (comment your reason please)
0 voters
If you have any question/concern/feedback/request, please feel free to reply to this post!