Badge position problem

So i’ve been making this gui that when a player clicks another player it open a gui that shows who is this gui and what badges he have.
The only problem is that the badge aren’t in the orders when a player get them, but follow the order of the badge ID, if anyone have an idea feel free to ask or try to solve this problem!
I’m really close the finish this project but there is this problem!

Uploading: RobloxScreenShot20240618_181755005.png…
Uploading: robloxapp-20240615-1603539.wmv…

-- Initialize Services
local Players = game:GetService("Players")
local BadgeService = game:GetService("BadgeService")

-- Function to position badges dynamically
local function positionBadges(scrollingFrame, badgeIds, player)
	local badgeSize = UDim2.new(0.20, 0, 0.20, 0)
	local badgePaddingX = 30
	local badgePaddingY = 35
	local maxBadgesPerRow = 4

	scrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)

	local function updateBadgePositions()
		local numBadges = #badgeIds
		for i = 1, numBadges do
			local badgeId = badgeIds[i]
			local success, hasBadge = pcall(function()
				return BadgeService:UserHasBadgeAsync(player.UserId, badgeId)
			end)

			if success then
				if hasBadge then
					local successBadgeInfo, badgeInfo = pcall(function()
						return BadgeService:GetBadgeInfoAsync(badgeId)
					end)

					if successBadgeInfo and badgeInfo then
						local rowIndex = math.floor((i - 1) / maxBadgesPerRow)
						local colIndex = (i - 1) % maxBadgesPerRow

						local badgeImage = scrollingFrame:FindFirstChild("Badge_" .. badgeId)
						if not badgeImage then
							badgeImage = Instance.new("ImageButton")
							badgeImage.Name = "Badge_" .. badgeId
							badgeImage.Parent = scrollingFrame
							badgeImage.Size = badgeSize
							badgeImage.BackgroundTransparency = 1 -- Hide background
							badgeImage.BorderSizePixel = 0 -- Remove border

							local timestampLabel = Instance.new("TextLabel")
							timestampLabel.Name = "TimestampLabel"
							timestampLabel.TextColor3 = Color3.new(1, 1, 1)
							timestampLabel.BackgroundTransparency = 1 -- Hide background
							timestampLabel.Size = UDim2.new(0, badgeSize.X.Offset, 0, 20)
							timestampLabel.Position = UDim2.new(0, 0, 1, 5)
							timestampLabel.Parent = badgeImage
						end

						badgeImage.Position = UDim2.new(
							0,
							colIndex * (badgeSize.X.Offset + badgePaddingX),
							0,
							rowIndex * (badgeSize.Y.Offset + badgePaddingY)
						)
						badgeImage.Image = "rbxassetid://" .. badgeInfo.IconImageId
						badgeImage.Visible = true

						local timestampLabel = badgeImage:FindFirstChild("TimestampLabel")
						if timestampLabel then
							local awardedDate = os.date("*t", badgeInfo.AwardedTimestamp)
							if awardedDate.month == 6 and awardedDate.day == 21 and awardedDate.year == 2023 then
								timestampLabel.Visible = false -- Hide label for specific date
							else
								timestampLabel.Visible = false
								timestampLabel.Text = "Awarded: " .. os.date("%c", badgeInfo.AwardedTimestamp)
							end
						end

						print("Player " .. player.Name .. " has badge: " .. badgeInfo.Name)
					else
						warn("Failed to retrieve badge info for badge ID: " .. badgeId)
					end
				else
					-- Badge no longer exists, remove from badgeIds
					table.remove(badgeIds, i)
					updateBadgePositions() -- Update positions after removal
					break -- Exit loop since badge was removed
				end
			else
				warn("Error checking badge for player " .. player.Name .. ": " .. tostring(hasBadge))
			end
		end

		-- Calculate total height for scrolling frame
		local totalHeight = math.ceil(#badgeIds / maxBadgesPerRow) * (badgeSize.Y.Offset + badgePaddingY) - badgePaddingY
		scrollingFrame.CanvasSize = UDim2.new(0, 0, 0, totalHeight)
	end

	-- Initially position badges
	updateBadgePositions()
end

-- Function to show player info GUI
local function showPlayerInfoGui(player)
	local playerGui = player:WaitForChild("PlayerGui", 10)
	if not playerGui then
		warn("PlayerGui not found.")
		return
	end

	local playerInfoGui = playerGui:FindFirstChild("PlayerInfoGui")
	if not playerInfoGui then
		warn("PlayerInfoGui not found.")
		return
	end

	local frame = playerInfoGui:FindFirstChild("PlayerInfoFrame")
	if not frame then
		warn("PlayerInfoFrame not found.")
		return
	end

	frame.Visible = true

	local textLabel = frame:FindFirstChild("PlayerUsername")
	if textLabel then
		textLabel.Text = player.Name
	end

	local scrollingFrame = frame:FindFirstChild("ScrollingFrameBadges")
	local badgeTemplate = scrollingFrame and scrollingFrame:FindFirstChild("BadgeTemplate")
	if not scrollingFrame or not badgeTemplate then
		warn("ScrollingFrameBadges or BadgeTemplate not found.")
		return
	end

	-- Clear old badges
	for _, child in ipairs(scrollingFrame:GetChildren()) do
		if child:IsA("ImageButton") and child ~= badgeTemplate then
			child:Destroy()
		end
	end

	-- Retrieve badgeIds for the player (example IDs)
	local badgeIds = {413205495208321, 3409622418214160, 133056055004766, 4029032162190803, 934944237747911, 2896030095702880, 2612730504555096}

	-- Position badges dynamically
	positionBadges(scrollingFrame, badgeIds, player)
end

-- Function to handle player click
local function onPlayerClicked(clickedCharacter)
	local clickedPlayer = Players:GetPlayerFromCharacter(clickedCharacter)
	if clickedPlayer then
		showPlayerInfoGui(clickedPlayer)
	end
end

-- Function to update positions of custom parts (if needed)
local customParts = {}

local function updateAllParts()
	for player, part in pairs(customParts) do
		if player.Character and player.Character:IsDescendantOf(game) then
			part.CFrame = player.Character:GetPrimaryPartCFrame()
		end
	end
end

-- Function to create a custom part and attach click detector (if needed)
local function createCustomPart()
	local part = Instance.new("Part")
	part.Size = Vector3.new(5, 5, 5)
	part.Transparency = 1
	part.Anchored = true
	part.CanCollide = false
	part.Parent = game.Workspace

	local clickDetector = Instance.new("ClickDetector")
	clickDetector.Parent = part
	return part, clickDetector
end

-- Function to add custom part to player's character (if needed)
local function addCustomPartToCharacter(player, character)
	local part, clickDetector = createCustomPart()
	customParts[player] = part

	clickDetector.MouseClick:Connect(function()
		onPlayerClicked(character)
	end)
end

-- Function called when player is added
local function onPlayerAdded(player)
	player.CharacterAdded:Connect(function(character)
		addCustomPartToCharacter(player, character)
	end)

	if player.Character then
		addCustomPartToCharacter(player, player.Character)
	end
end

-- Loop through existing players and add custom parts
for _, player in ipairs(Players:GetPlayers()) do
	onPlayerAdded(player)
end

-- Connect event for new players added
Players.PlayerAdded:Connect(onPlayerAdded)

-- Connect event to update all parts positions (if needed)
game:GetService("RunService").Stepped:Connect(updateAllParts)

The media you attempted to attach to the post didn’t get uploaded fully before you hit post, so we can’t see them :frowning:


Looking at the code, it seems that you are attempting to manually set the position of each badge. I’ve also noticed that you name the badge image according to the ID of the badge.

I suspect that you may have a UIListLayout or UIGridLayout which is clashing with your code, causing the unexpected behaviour. Can you please double check that you don’t have either of the layouts in the scrolling frame?

If it still doesn’t behave correctly, can you please test what happens if you change the badgeImage name from Badge_BADGEID, to Badge_i (iteration in loop)

2 Likes

Here this is the example, the badges aren’t positioned in the order earned by the player!
But I’ll try to do what you said!
Thank you for helping