How to make a 'scrolling' UI

Title might be confusing. I’m not talking about ScrollingFrames, but using a Previous and Next button, to basically scroll through items.

The basic problem I am facing is that the current way I am going about this is going to end up convoluted when I add in more items over time.

previousPage.Activated:Connect(function()
	if var.selected ~= 'Knight' then
		var.selected = 'Knight'
		class.Text = var.selected
		previousPage.ImageColor3 = Color3.fromRGB(25, 25, 25)
		nextPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
	end
end)

nextPage.Activated:Connect(function()
	if var.selected ~= 'Archer' then
		var.selected = 'Archer'
		class.Text = var.selected
		nextPage.ImageColor3 = Color3.fromRGB(25, 25, 25)
		previousPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
	end
end)

Since I currently only have 2 classes, it isn’t that convaluted, but when I move onto say 10, it’d start to become really clustered. For example, let’s say next class is a Scout:

previousPage.Activated:Connect(function()
	if var.selected == 'Archer' then
		var.selected = 'Knight'
		class.Text = var.selected
		previousPage.ImageColor3 = Color3.fromRGB(25, 25, 25)
		nextPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
	elseif var.selected == 'Scout' then
		var.selected = 'Archer'
		class.Text = var.selected
		previousPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
		nextPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
	end
end)

nextPage.Activated:Connect(function()
	if var.selected == 'Knight' then
		var.selected = 'Archer'
		class.Text = var.selected
		nextPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
		previousPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
	elseif var.selected == 'Archer' then
		var.selected = 'Scout'
		class.Text = var.selected
		nextPage.ImageColor3 = Color3.fromRGB(85, 170, 255)
		previousPage.ImageColor3 = Color3.fromRGB(25, 25, 25)
	end
end)	

And basically every class added would add an extra 10 or so lines to the script. I’m guessing somehow adding a for loop that loops through the classes from a table might be able to minimise the code, but not entirely sure how to go about that and how to have it recognise what colors the next and previous buttons would have to be (Knight being first in the list, so the Next button is Blue, Previous is grey, and Scout being the last in the list, so Next being Grey and Previous being Blue)

2 Likes

A post was merged into an existing topic: Off-topic and bump posts

Essentially you want to generalise. This means take out all specific information from your basic next/previous code, and then create some variables (that do what they say – store a variable value). Make it so that you don’t need to change one bit of code (aside from specific variables) to add or remove pages.

In this case, in the following sample code I have done two main things. I generalised the setup code, and I put all the actual page changing into its own generalised function. Hopefully you’ll be able to understand better by reading it.

local SELECTED_COLOR = Color3.new()
local DESELECTED_COLOR = Color3.new()

local Pages = {Gui.KnightPage, Gui.ArcherPage, Gui.WizardPage}
local SelectedPageIndex = 1

local function SelectPage(Index)
    local NewPage = Pages[Index]
    if not NewPage then return end -- If there is no next/previous page

    if SelectedPageIndex then
        Pages[SelectedPageIndex].ImageColor3 = DESELECTED_COLOR
    end
    NwePage.ImageColor3 = SELECTED_COLOR

    SelectedPageIndex = Index
end

NextPage.Activated:Connect(function()
    SelectPage(SelectedPageIndex + 1)
end)

PreviousPage.Activated:Connect(function()
    SelectPage(SelectedPageIndex - 1)
end)```
1 Like

Thank you for your response :smiley:

With the pages table, there isn’t a different UI per say for each class. What I did was whenever the page was changed, I fire this function (forgot to put it into the code I pasted on here!! :grimacing:)

function changed()
	getModel:Class()
	if var.selected ~= var.equipped then
		selector.Label.Text = 'Equip'
	else
		selector.Label.Text = 'Equipped'
	end
	-- Health Stats --
	health.Total.Text = classStats.Stats[var.selected].Health
	health.BarSpace.Bar:TweenSize(UDim2.new(classStats.Stats[var.selected].Health/200, 0, 1, 0), 'Out', 'Quad', 0.25, true)
	-- Speed Stats --
	speed.Total.Text = classStats.Stats[var.selected].Speed
	speed.BarSpace.Bar:TweenSize(UDim2.new(classStats.Stats[var.selected].Speed/32, 0, 1, 0), 'Out', 'Quad', 0.25, true)
	-- Damage Stats --
	damage.Total.Text = classStats.Stats[var.selected].Damage
	damage.BarSpace.Bar:TweenSize(UDim2.new(classStats.Stats[var.selected].Damage/50, 0, 1, 0), 'Out', 'Quad', 0.25, true)
end  

But this is basically fired whenever the page changes. The getModel:Class() is a module, that gets a model of the classes character (from RepStorage) and puts a clone into CurrentCamera (so it’s basically like a 3D shop, not using Viewports though. The sets what the button would say, and then gets the classes stats based on a module script which contains all their data

@ZylanchewYT

OP doesn’t want that though. You can see this explicitly mentioned within the first statement to clarify.


@OP

Scrolling != Previous & Next menu. Your title did confuse me a bit.

Your current setup isn’t necessarily convoluted, it’s just that you’re hard coding everything to the point where you’ll end up with nested if statements, which is not ideal. You want to make your system scale so that once you add a class, your code will already be able to support new classes. Don’t be specific when it comes to such code: make sure that it is able to support external additions.

In addition to what EmeraldSlash contributed, there is an object called UIPageLayout, which essentially handles the bulk of page work for you and provides appropriate methods for your use case. You would still, of course, be responsible for fetching classes and creating pages for them, while this object handles the rest for you. I don’t know if this is what you’re searching for, but it’s worth a look at.

As for the code you posted above me, a small pet peeve. I don’t know whether this affects performance or whether it’s just dumb premature optimisation, however I notice that you constantly re-enter the table to get values under all the stats tabs. Instead of doing this, you can index it once as a local variable and then continue on to set the rest. You can also do a quick “magic trick” with your equip statement.

I’ve rewritten your code so you can understand what I’m talking about. I’ve kept the variable in camelCase to be consistent with your naming conventions. It reflects the feedback above.

function changed()
	getModel:Class()
	selector.Label.Text = (var.selected == var.equipped and "Equipped" or "Equip")
	local selectedClass = classStats.Stats[var.selected]
	-- Health Stats --
	health.Total.Text = selectedClass.Health
	health.BarSpace.Bar:TweenSize(UDim2.new(selectedClass.Health/200, 0, 1, 0), 'Out', 'Quad', 0.25, true)
	-- Speed Stats --
	speed.Total.Text = selectedClass.Speed
	speed.BarSpace.Bar:TweenSize(UDim2.new(selectedClass.Speed/32, 0, 1, 0), 'Out', 'Quad', 0.25, true)
	-- Damage Stats --
	damage.Total.Text = selectedClass.Damage
	damage.BarSpace.Bar:TweenSize(UDim2.new(selectedClass.Damage/50, 0, 1, 0), 'Out', 'Quad', 0.25, true)
end  

Difference log below (you will not see this well on dark theme):

- if var.selected ~= var.equipped then
-		selector.Label.Text = 'Equip'
-	else
-		selector.Label.Text = 'Equipped'
-	end
+	selector.Label.Text = (var.selected == var.equipped and "Equipped" or "Equip")
+ 	local selectedClass = classStats.Stats[var.selected]
+ Anything with "classStats.Stats[var.Selected]" replaced with "selectedClass"

Furthermore, you could probably create a function of some kind to better handle stat setting up here, like ApplyStatVisual(Type) and then pass Health, Speed or Damage. You never know, you may end up adding additional bars. Again, not anything major, so don’t worry about it too much.

1 Like