Merging doesn't work

I’m making a merging game and i want that if 2 spheres of the same lvl (attribute) touches eachothers, they both delete themselves to spawn a higher level sphere. (Inspired of Merge!).

The issue is, since I use a system that it detects when it touches another sphere and checks if it’s the good one, if it is, it then spawns a new sphere. This all works until since the detecting system works on both spheres, it spawns not 1 higher level sphere, but 2.

I haven’t tried any solutions, as I do not know how to fix the problem or neither know how to search this up (how to word my problem and potentially found someone else having the same problem).

I also have a problem where newly created spheres can’t merge, i tried turning the Sphere.Touched function into a local function and using Sphere.Touched AND NewSphere.Touched but since NewSphere was in the function i can’t use it.

Any help is appreciated, here’s my code :

local SSS, MaxSpawnLVL = workspace.Values.SphereSpawnSpeed.Value, workspace.Values.MaxSpawnLVL.Value

while true do
	wait(SSS)
	local Sphere = Instance.new("Part")
	Sphere.Shape = Enum.PartType.Ball
	Sphere:SetAttribute("Level", math.random(1, MaxSpawnLVL))
	local SizeLVLRatio = 4 + Sphere:GetAttribute("Level")
	Sphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
	local BSize = workspace.Baseplate.Size.X / 3 - 10
	Sphere.Position = Vector3.new(math.random(0 - BSize, BSize), 52, math.random(0 - BSize, BSize))
	Sphere.Massless = true
	Sphere.TopSurface, Sphere.BottomSurface = "Smooth", "Smooth"
	Sphere.Parent = workspace.SphereHolder
	
	Sphere.Touched:Connect(function(hit)
		local HA = hit:GetAttribute("Level")
		
		if HA == Sphere:GetAttribute("Level") then
			local NewSphere = Instance.new("Part")
			NewSphere.CFrame = CFrame.new(Sphere.CFrame.X, Sphere.CFrame.Y + 10, Sphere.CFrame.Z)
			NewSphere:SetAttribute("Level", Sphere:GetAttribute("Level") + 1)
			local SizeLVLRatio = 4 + NewSphere:GetAttribute("Level")
			NewSphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
			NewSphere.Shape = Enum.PartType.Ball
			NewSphere.TopSurface, NewSphere.BottomSurface = "Smooth", "Smooth"
			NewSphere.Massless = true
			NewSphere.Parent = workspace.SphereHolder
			Sphere:Destroy()
			hit:Destroy()
			local Sphere = NewSphere
			print("Touching test")
		end
	end)
1 Like

A quick and dumb (but working) fix would be applying an ID to each sphere, and then when both functions? detect collision, only a script with a higher/lower ID have rights to delete and create new spheres

local sphereId = 0

-- your sphere creation loop
local currentSphereId = sphereId
sphereId += 1
local sphere = Instance.new('Part')
sphere:SetAttribute('SphereId', currentSphereId)
...
--when collision detected and has passed first statement
local otherSphereId = otherSphere:GetAttribute('SphereId')
if currentSphereId > otherSphereId then
-- do your things, should only run once
end

The touched event happens on both touching spheres, so it fires twice.
I’ve done some testing in studio and this code seems to work, not sure if its efficient though:

local SSS, MaxSpawnLVL = workspace.Values.SphereSpawnSpeed.Value, workspace.Values.MaxSpawnLVL.Value

while task.wait(SSS) do
	local Sphere = Instance.new("Part")
	local Touched = Instance.new("BoolValue",Sphere)
	Touched.Name = "Touched"
	Sphere.Shape = Enum.PartType.Ball
	Sphere:SetAttribute("Level", math.random(1, MaxSpawnLVL))
	local SizeLVLRatio = 4 + Sphere:GetAttribute("Level")
	Sphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
	local BSize = workspace.Baseplate.Size.X / 3 - 10
	Sphere.Position = Vector3.new(math.random(0 - BSize, BSize), 52, math.random(0 - BSize, BSize))
	Sphere.Massless = true
	Sphere.TopSurface, Sphere.BottomSurface = "Smooth", "Smooth"
	Sphere.Parent = workspace.SphereHolder

	Sphere.Touched:Connect(function(hit)
		local HA = hit:GetAttribute("Level")
		if HA == Sphere:GetAttribute("Level") then
			Touched.Value = true
			if hit:FindFirstChild("Touched").Value == false then
				local NewSphere = Instance.new("Part")
				NewSphere.CFrame = CFrame.new(Sphere.CFrame.X, Sphere.CFrame.Y + 10, Sphere.CFrame.Z)
				NewSphere:SetAttribute("Level", Sphere:GetAttribute("Level") + 1)
				local SizeLVLRatio = 4 + NewSphere:GetAttribute("Level")
				NewSphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
				NewSphere.Shape = Enum.PartType.Ball
				NewSphere.TopSurface, NewSphere.BottomSurface = "Smooth", "Smooth"
				NewSphere.Massless = true
				NewSphere.Parent = workspace.SphereHolder
				Sphere:Destroy()
				hit:Destroy()
				local Sphere = NewSphere
				print("Touching test")
			end
		end
	end)
end

I can’t test if it works, as when i do it, it just spawns thousands of upgraded spheres, probably because of the repeated if condition, I can’t change it to “false” or "true’ to stop the condition from working as it actually sends “nil”, so i do == nil, since Touched doesn’t exist in “hit” parameter.

Did you check to make sure workspace.Values.SphereSpawnSpeed.Value is not 0?

Reason I’m asking is because that’s the value you’re currently using to set the wait time in task.wait(), and if its 0 then the spheres will spawn extremely fast

SphereSpawnSpeed is at 4, and it’s not the one that spawns the new spheres, it’s the one that makes the naturally generated spheres. I don’t see any times where it changes in the code and it always is at 4 (doesn’t even need to print, playing would show that each 4 seconds the spheres spawn)

I THINK (not sure) it’s because the othersphere/hit ALSO have their currentSphereID so it bugs/overlaps + let’s say Ball A + Ball B, the expectation is that Ball A touches Ball B and they compare themselves, but technically there’s no real “A” and “B”, both are balls that are similar so they both carry the same ids, so a sphere could technically have both currentSphereID and the otherSphereID. Anyways i receive this error message :

attempt to compare nil < number - Client - LocalScript:29

Okay so firstly is this the only script that controls the sphere spawns?
And it would be helpful to include the modified script

  1. Yes, i have only one script that handles the spheres and it’s spawning etc.

  2. Here’s the new code :

local SSS, MaxSpawnLVL = workspace.Values.SphereSpawnSpeed.Value, workspace.Values.MaxSpawnLVL.Value
local sphereId = 0

while true do
	wait(SSS)
	local currentSphereId = sphereId
	print(currentSphereId)
	sphereId += 1
	local Sphere = Instance.new("Part")
	Sphere:SetAttribute('SphereId', currentSphereId)
	Sphere.Shape = Enum.PartType.Ball
	Sphere:SetAttribute("Level", math.random(1, MaxSpawnLVL))
	local SizeLVLRatio = 4 + Sphere:GetAttribute("Level")
	Sphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
	local BSize = workspace.Baseplate.Size.X / 3 - 10
	--print(BSize, 0 - BSize)
	Sphere.Position = Vector3.new(math.random(0 - BSize, BSize), 52, math.random(0 - BSize, BSize))
	Sphere.Massless = true
	Sphere.TopSurface, Sphere.BottomSurface = "Smooth", "Smooth"
	Sphere.Parent = workspace.SphereHolder
	
	--Sphere.Touched:Connect(function(hit)
	Sphere.Touched:Connect(function(hit)
		local HA = hit:GetAttribute("Level")
		local otherSphereId = hit:GetAttribute('SphereId')
		if currentSphereId > otherSphereId then
			Sphere.Touched:Connect(function(hit)
				local HA = hit:GetAttribute("Level")
				if HA == Sphere:GetAttribute("Level") then
					local NewSphere = Instance.new("Part")
					NewSphere.CFrame = CFrame.new(Sphere.CFrame.X, Sphere.CFrame.Y + 10, Sphere.CFrame.Z)
					NewSphere:SetAttribute("Level", Sphere:GetAttribute("Level") + 1)
					local SizeLVLRatio = 4 + NewSphere:GetAttribute("Level")
					NewSphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
					NewSphere.Shape = Enum.PartType.Ball
					NewSphere.TopSurface, NewSphere.BottomSurface = "Smooth", "Smooth"
					NewSphere.Massless = true
					NewSphere.Parent = workspace.SphereHolder
					Sphere:Destroy()
					hit:Destroy()
					local Sphere = NewSphere
					print("Touching test")
				end
			end)
		end
   end)
end

Are you making sure that the only parts this sphere can touch are other spheres? For example this sphere can touch characters, baseplate or other map models, and this is most likely a problem

I added this :

		if hit.Parent == workspace.SphereHolder then

between the Sphere.Touched function and the local HA, it works when I then add a print but now i have no errors nor the spawning new sphere system works. it’s like if it works but doesn’t work at the same time?

u can def use remoteevents for stuff like this (serverscript to main server script) not sure if its the best way tho

eg. :egg:

local sdp = {}

CombineEvent.OnServerEvent:Connect(function(localSphere, otherSphere)
if sdp[otherSphere] ~= localSphere then
-- do stuff here
sdp[localSphere] = otherSphere
end
end)

I’m not so good at remote events, and i’m kinda confused right now. I wanted to transfer Sphere and hit (othersphere) with the remote event but when i print them it says that sphere = Player(my username) and hit = nil. I don’t understand. Here’s the code :

Local Script in StarterCharacterScripts :

while true do
	wait(SSS)
	--local currentSphereId = sphereId
	--print(currentSphereId)
	--sphereId += 1
	local Sphere = Instance.new("Part")
	--Sphere:SetAttribute('SphereId', currentSphereId)
	--local Touched = Instance.new("BoolValue")
	--Touched.Value = false
	--Touched.Parent = Sphere
	Sphere.Shape = Enum.PartType.Ball
	Sphere:SetAttribute("Level", math.random(1, MaxSpawnLVL))
	local SizeLVLRatio = 4 + Sphere:GetAttribute("Level")
	Sphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
	local BSize = workspace.Baseplate.Size.X / 3 - 10
	--print(BSize, 0 - BSize)
	Sphere.Position = Vector3.new(math.random(0 - BSize, BSize), 52, math.random(0 - BSize, BSize))
	Sphere.Massless = true
	Sphere.TopSurface, Sphere.BottomSurface = "Smooth", "Smooth"
	Sphere.Parent = workspace.SphereHolder
	
	Sphere.Touched:Connect(function(hit)
			local HA = hit:GetAttribute("Level")
			--local otherSphereId = hit:GetAttribute('SphereId')
			--if currentSphereId > otherSphereId then
				Sphere.Touched:Connect(function(hit)
					local HA = hit:GetAttribute("Level")
					if HA == Sphere:GetAttribute("Level") then
						workspace.RemoteEventsF.CombineEvent:FireServer(Sphere, hit)
			end
		end)
   end)
end

The script in ServerScriptService :

local sdp = {}

workspace.RemoteEventsF.CombineEvent.OnServerEvent:Connect(function(Sphere, hit)
	
	print(Sphere, hit)
	
	if sdp[hit] ~= Sphere then
		
		local NewSphere = Instance.new("Part")
		NewSphere.CFrame = CFrame.new(Sphere.CFrame.X, Sphere.CFrame.Y + 10, Sphere.CFrame.Z)
		NewSphere:SetAttribute("Level", Sphere:GetAttribute("Level") + 1)
		local SizeLVLRatio = 4 + NewSphere:GetAttribute("Level")
		NewSphere.Size = Vector3.new(SizeLVLRatio, SizeLVLRatio, SizeLVLRatio)
		NewSphere.Shape = Enum.PartType.Ball
		NewSphere.TopSurface, NewSphere.BottomSurface = "Smooth", "Smooth"
		NewSphere.Massless = true
		NewSphere.Parent = workspace.SphereHolder
		Sphere:Destroy()
		hit:Destroy()
		local Sphere = NewSphere
		print("Touching test")
		sdp[Sphere] = hit
		
	end
	
end)

I’m not so good at remote events and i never understand this problem, thanks for the help!

Sorry, i made an accident. In every remote fire lies the first argument as a player. The corrected mockup would be:

local sdp = {}

CombineEvent.OnServerEvent:Connect(function(player, localSphere, otherSphere)
if sdp[otherSphere] ~= localSphere then
-- do stuff here
sdp[localSphere] = otherSphere
end
end)