How to loop through a varying table in a specific order?

Hello,

In the game I’m working on, I’ve come across an issue which I have identified, but cannot think of a viable way to fix with my limited Roblox scripting experience.

The issue emerges from the randomness aspect of my game. I have coded a for-loop to attach each meshpart to its appropriate location. Ideally, once the script is run, the player will have a fully assembled sword. What currently happens is something like this:![image|423x352]image

This happens because when attaching the objects, the guard of the sword is attached to the blade first, but is then updated by the grip’s attachment, resulting in the blade being left where it was in the workspace initially. Here’s the code that is causing the issue:

for _, part in ipairs(weapon:GetChildren()) do -- the meshpart object
		print(part.Name)
		for _, attachment in ipairs(part:GetChildren()) do -- the meshpart's attachment
			if not attachment:IsA("Attachment") or attachment.Name == "Done" then
				continue
			end

			print(attachment.Name)
			for _, otherAttachment in ipairs(weapon:GetDescendants()) do -- loops through all attachments in the weapon

				if not otherAttachment:IsA("Attachment") then
					continue
				end

				if otherAttachment.Parent ~= attachment.Parent and otherAttachment.Name ~= "Done" then -- checks that otherAttachment is not the same object as attachment
					
					if attachment.Name == otherAttachment.Name then -- Script by RoBoPoJu
						-- attach the parts, mark as done when complete
						local part = attachment.Parent
						local other = otherAttachment.Parent

						local part1, part2 = part, other
						local attach1, attach2 = attachment, otherAttachment

						local part2PpCf = part2.CFrame

						local newPos = attach1.WorldPosition-attach2.Position
						local newPart2PpCf = part2PpCf-part2PpCf.Position+newPos

						part2.CFrame = newPart2PpCf
						
						-- weld the parts so they move together
						local weld = Instance.new("WeldConstraint")
						weld.Parent = attachment.Parent
						weld.Part0 = attachment.Parent
						weld.Part1 = otherAttachment.Parent
						
						print(attachment.Name .. " " .. otherAttachment.Name)
						attachment.Name = "Done"
						otherAttachment.Name = "Done"
						
						break
					end
				end
			end
		end
		print("\n")
	end

I’ve tried a lot to remedy this, but nothing is getting the result I want.


In my latest attempt at debugging this, I isolated the script into its own game. Since I was using weld constraints, I thought that welding the parts together on each iteration would move everything in relation to eachother, but by the end, the weapon remained malformed. I think the only way to fix this bug is to either choose a completely new method or get to control the order of the loop somehow. Here is a screenshot as well as the code that causes it (I know it’s messy. I was trying to work through it and I’m pasting it as-is).

image

for _, part in ipairs(folder:GetChildren()) do -- the meshpart object

	print(part.Name)
	
	for _, attachment in ipairs(part:GetChildren()) do -- the meshpart's attachment
		if not attachment:IsA("Attachment") or attachment.Name == "Done" then
			continue
		end

		print(attachment.Name)
		for _, otherAttachment in ipairs(folder:GetDescendants()) do -- loops through all attachments in the weapon

			if not otherAttachment:IsA("Attachment") then
				continue
			end

			if otherAttachment.Parent ~= attachment.Parent and otherAttachment.Name ~= "Done" then -- checks that otherAttachment is not the same object as attachment
				wait(2)
				if attachment.Name == otherAttachment.Name then -- Script by RoBoPoJu
					-- attach the parts, mark as done when complete
					local part = attachment.Parent
					local other = otherAttachment.Parent

					local part1, part2 = part, other
					local attach1, attach2 = attachment, otherAttachment

					local part2PpCf = part2.CFrame

					local newPos = attach1.WorldPosition-attach2.Position
					local newPart2PpCf = part2PpCf-part2PpCf.Position+newPos

					part2.CFrame = newPart2PpCf

					-- weld the parts so they move together
					local weld = Instance.new("WeldConstraint")
					weld.Parent = attachment.Parent.Index.Value > otherAttachment.Parent.Index.Value and attachment.Parent or otherAttachment.Parent
					weld.Part0 = attachment.Parent.Index.Value > otherAttachment.Parent.Index.Value and attachment.Parent or otherAttachment.Parent
					weld.Part1 = weld.Part0 == attachment.Parent and otherAttachment.Parent or attachment.Parent

					print(attachment.Name .. " " .. otherAttachment.Name)
					attachment.Name = "Done"
					otherAttachment.Name = "Done"

					break
				end
			end
		end
	end
	print("\n")
end

Because of how the rest of my game works, changing the folder names is not viable. That would only be a short-term solution that would come back to haunt me once I add new weapons to the game. Any ideas or tips?

Thanks in advance.

Edit: In reading this over, I thought I should also mention that the reason I can’t manually move the parts is because I plan to use randomized meshparts that will vary in shape and size for each component.

1 Like

Hiwi! o/

Well, you mentioned that you are using the weldConstraints to attach and “move” the part to the right position, and weldsConstraing do not move the parts automatically where you want them, it only welds them in their initial position. (You already notice that the blade stays in the same initial position)

What I would do is a different approach.
Because probably I want some grips larger than others, and some bladles larger than others, and many customizations on each sword. I would use attachments on each part, exactly where I want each part to fuse to the another.
During the loop, change the CFrame of the new part that will be attached (the blade), following the corresponding previous Attachment CFrame of the last part (the grip). After positioning the blade exactly in the attachment of the handle then weld it.
Add as many attachments on each sword part to add more features to add. I would go with attachments for each of these common parts on a sword

For example, you want attachments like these, in order to use their CFrame and place the next parts on correct position
image

Maybe if you dont want that the sword is created on the hand of the player, maybe adding an anvil to make a system where player can forge their swords, etc.

Thank you for replying!

I don’t know if we’re interpreting this differently, but the method you stated is exactly what I’ve been working with. Because I won’t actually know the order of the table, using the index of the last part attachment looped through won’t guarantee that the part in question has the corresponding point.

I can screenshot the attachment system that I have when I get home if you want to see it.

Okay I think I found the issue. It seems that the problem is that you are always moving part2, without checking if that is this right one. Your script gives no guarantee that the part2 is the one that needs to be moved.

To solve this (assuming that is the issue) I would make a temporary table, in which I would save which parts have already been moved. It is better than potentially problematic renaming of parts.

local MovedParts = {}

for _, part in ipairs(folder:GetChildren()) do -- the meshpart object

	print(part.Name)
	
	for _, attachment in ipairs(part:GetChildren()) do -- the meshpart's attachment
		if not attachment:IsA("Attachment") or attachment.Name == "Done" then
			continue
		end

		print(attachment.Name)
		for _, otherAttachment in ipairs(folder:GetDescendants()) do -- loops through all attachments in the weapon

			if not otherAttachment:IsA("Attachment") then
				continue
			end

			if otherAttachment.Parent ~= attachment.Parent and otherAttachment.Name ~= "Done" then -- checks that otherAttachment is not the same object as attachment
				wait(2)
				if attachment.Name == otherAttachment.Name then -- Script by RoBoPoJu
					-- attach the parts, mark as done when complete
					local part = attachment.Parent
					local other = otherAttachment.Parent

					local part1, part2
					local attach1, attach2
					if MovedParts[other] then
						part1, part2 = other, part --other way round
						attach1, attach2 = otherAttachment, attachment --other way round
					else
						part1, part2 = part, other --original order
						attach1, attach2 = attachment, otherAttachment --original order
 					end

					MovedParts[part2] = true

					local part2PpCf = part2.CFrame

					local newPos = attach1.WorldPosition-attach2.Position
					local newPart2PpCf = part2PpCf-part2PpCf.Position+newPos

					part2.CFrame = newPart2PpCf

					-- weld the parts so they move together
					local weld = Instance.new("WeldConstraint")
					weld.Parent = attachment.Parent.Index.Value > otherAttachment.Parent.Index.Value and attachment.Parent or otherAttachment.Parent
					weld.Part0 = attachment.Parent.Index.Value > otherAttachment.Parent.Index.Value and attachment.Parent or otherAttachment.Parent
					weld.Part1 = weld.Part0 == attachment.Parent and otherAttachment.Parent or attachment.Parent

					print(attachment.Name .. " " .. otherAttachment.Name)
					attachment.Name = "Done"
					otherAttachment.Name = "Done"

					break
				end
			end
		end
	end
	print("\n")
end

I hope this helps!

1 Like

Eureka! I thought I was solving that issue when I was attaching the welds with the pseudo-ternary operator bits, but just as you said, I wasn’t actually changing the part that I was moving. Thank you so much for your help!