Currently I’m using this tutorial as a reference for implementing ui sliders in a project of mine.
The problem is, I don’t find the code used in the tutorial to be very efficient.
Is there a better way of implementing dragging? As in my current code, I would have to go through a list of conditional statements to check if any of the button down variables are true (therefore held down) and then call a method from a module script from there.
Current code
HeightBar.MouseButton1Down:Connect(function()
local BarSlider = SliderManager.new(HeightBar:FindFirstChildWhichIsA("Frame")) -- Construct slider class
BarSlider:ClickToSetBar()
SliderHeldDown = true
end)
UserInputService.InputChanged:Connect(function(input, GameProcess)
if not GameProcess then
if input.UserInputType == Enum.UserInputType.MouseMovement and SliderHeldDown then -- Button held down and mouse is moving position
print("a") -- Call sliding method here
end
end
end)
There are posts that already implemented this and should help, I recommend these and I’m assuming these are more efficient than that implementation you’re following.
(Also next time try searching before posting so it’ll be faster for you to answer next time)
Alright, I’m back from implementing dragging. How I would implement this is by running a loop, preventing it from sliding from the x-axis, above and below the bar, and calculating the percentage by the slider’s y position offset, dividing with bar’s y size’s position offset subtracting the slider y size’s offset.
LocalScript (Note that my method uses offset so If you want it by scale, you need to rescript it)
local maxPercent = 100
local percent = 0
local uis = game:GetService("UserInputService")
local run = game:GetService("RunService")
local slider = script.Parent
local bar = slider.Parent
local x = {slider.Position.X.Scale, slider.Position.X.Offset}
local y = {slider.Position.Y.Scale, slider.Position.Y.Offset}
run.RenderStepped:Connect(function()
slider.Position = UDim2.new(x[1], x[2],slider.Position.Y.Scale, slider.Position.Y.Offset)
-- prevents it from sliding the x axis
if slider.Position.Y.Offset >= bar.Size.Y.Offset - slider.Size.Y.Offset then
-- only runs if the button is below the bar
slider.Position = UDim2.new(x[1], x[2] , y[1], y[2])
elseif slider.Position.Y.Offset <= 0 then
-- only runs if the button's position is above the bar
slider.Position = UDim2.new(x[1], x[2],slider.Position.Y.Scale, 0)
end
local value = math.floor(
(slider.Position.Y.Offset / (bar.Size.Y.Offset - slider.Size.Y.Offset))
* maxPercent
)
-- Apparently the value is in reverse so we're reverse it back
value = maxPercent-value
percent = value
bar.PercentText.Text = percent
end)
rbxm file if u want to see the preview: slider.rbxm (7.0 KB)
I think I still prefer my method using OOP. It allows for only one module script which can be required by a single local script which controls the UI sliders, it’s a lot more efficient, readable and cleaner.
I’m still gonna have to slog through a bunch of conditional statements to check if a button is being held down however. https://gyazo.com/ba2e49c4e9fb39f2fd2aff22904ff3b3
local script
HeightBar.MouseButton1Down:Connect(function() -- Player pressed button
HeightSlider_Down = true
end)
HeightBar.MouseButton1Up:Connect(function() -- Player released button
HeightSlider_Down = false
end)
UserInputService.InputChanged:Connect(function(input, GameProcess)
if input.UserInputType == Enum.UserInputType.MouseMovement then -- Mouse moving
if HeightSlider_Down then -- Height slider dragging
local BarSlider = SliderManager.new(HeightBar:FindFirstChildWhichIsA("Frame")) -- Construct slider class
local size = BarSlider:DragBar() -- Call dragging method
print(size)
end
end
end)
Module Class
local UserInputService = game:GetService("UserInputService")
local SliderManager = {}
SliderManager.__index = SliderManager
function SliderManager.new(BarFrame) -- Construct slider class
local BarSlider = {}
setmetatable(BarSlider, SliderManager)
BarSlider.Bar = BarFrame -- The frame which is the actual sliding bar
BarSlider.BarButton = BarFrame.Parent -- The text button which the frame is parented to
return BarSlider
end
function SliderManager:DragBar() -- Drags slider and returns slider value(s)
-- Dragging bar visual effect
local absolutePos = Vector2.new(self.BarButton.AbsolutePosition.X, self.BarButton.AbsolutePosition.Y)
local mouseLoc = UserInputService:GetMouseLocation()
local max = self.BarButton.AbsoluteSize.X
self.Bar.Size = UDim2.new(0, math.clamp(mouseLoc.X - absolutePos.X, 0, max), 1, 0) -- bar size changes position to mouse x position, clamp it to the size of the bar
-- Calculate bar value out of 100
local MaxSize = Vector2.new(self.BarButton.AbsoluteSize.X, self.BarButton.AbsoluteSize.Y)
local size = self.Bar.AbsoluteSize
local value = math.floor(100 * (size.X / MaxSize.X) + 0.5) -- "+0.5" , any value above 0.5 rounds up
return value
end
return SliderManager
You can certainly use ContextActionService to bind and unbind actions whenever you want, it would lead to cleaner code for sure.
local function dragSlider(_, _, input)
-- this doesn't need any conditionals!
BarSlider:DragBar(input.Position) -- passing in the mouse location
end
HeightBar.MouseButton1Down:Connect(function()
ContextActionService:BindAction(
"DragSlider", -- action name
dragSlider, -- function to bind
false, -- whether this should create a mobile button
Enum.UserInputType.MouseMovement -- inputs to bind to
)
end)
HeightBar.MouseButton1Up:Connect(function()
ContextActionService:UnbindAction("DragSlider")
end)
Well it’s just how I would implement, you could’ve try my implementation (excluding the loop) since I find it more efficient, clean, and readable and more simple than that imo.
(If you did try, I’m assuming that didn’t work because I tried it without a loop and with functions and it doesn’t work somehow)
This doesn’t seem to work whenever the mouse is actually over the button gui. I’m assuming this is because of game processed event being true when the mouse is over the button? https://gyazo.com/afd86da86409e35f5fb4baf31a572d13
That’s because the button’s mouse events only fire when the mouse is hovering over the button, so that’s my mistake.
You can add logic for when to unbind the action in the dragSlider function.
local function isDragging(input)
return input.UserInputType == Enum.UserInputType.MouseMovement
end
local function isReleasing(input)
return input.UserInputType == Enum.UserInputType.MouseButton1
and input.UserInputState == Enum.UserInputState.End
end
local function dragSlider(_, _, input)
if isDragging(input) then
-- dragging code
BarSlider:DragBar(input.Position)
elseif isReleasing(input) then
ContextActionService:UnbindAction("DragSlider")
end
end
HeightBar.MouseButton1Down:Connect(function()
ContextActionService:BindAction(
"DragSlider", dragSlider, false,
Enum.UserInputType.MouseMovement,
Enum.UserInputType.MouseButton1
)
end
You could also try binding two separate actions.
local function dragSlider(_, _, input)
-- dragging code
BarSlider:DragBar(input.Position)
end
local function releaseSlider(_, state)
if state == Enum.UserInputState.End then
ContextActionService:UnbindAction("DragSlider")
ContextActionService:UnbindAction("ReleaseSlider")
end
end
HeightBar.MouseButton1Down:Connect(function()
ContextActionService:BindAction(
"DragSlider", dragSlider, false,
Enum.UserInputType.MouseMovement
)
ContextActionService:BindAction(
"ReleaseSlider", releaseSlider, false,
Enum.UserInputType.MouseButton1
)
end)
It seems that TextButtons always sink certain inputs even with the .Active property set to false. Two solutions:
With.Active = false, you can connect to the button’s MouseButton1Up event and unbind the action there, and you should be good.
-- the rest of your code
HeightBar.MouseButton1Up:Connect(function()
ContextActionService:UnbindAction("DragSlider")
end)
Using a TextLabel or a Frame allows the context action to do its job, but you would have to connect to its InputBegan event instead.
HeightBar.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
ContextActionService:BindAction(
"DragSlider", dragSlider, false,
Enum.UserInputType.MouseMovement,
Enum.UserInputType.MouseButton1
)
end
end)
This time it’s tested, so it can’t be wrong unless it is