Help with miner npcs targetting the same ore

My miner npcs (controlled by collectionservice) are targetting the same ore no matter what I do to them, I tried adding tags, checking how many tags are in an object, inserting tags into objects, basically every possible thing involving tags (objectvalue ones).

Here is the script

local ReplicatedStorage = game:GetService('ReplicatedStorage')
local Players = game:GetService('Players')
local CollectionService = game:GetService('CollectionService')
local PathfindingService = game:GetService('PathfindingService')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)

local MinerNumber = 0

CollectionService:AddTag(ReplicatedStorage.AI.Miner,'Miner')

CollectionService:GetInstanceAddedSignal('Miner'):Connect(function(Miner)
	spawn(function()
		Miner.Name = Miner.Name..tostring(MinerNumber)
		MinerNumber += 1
		local humrp = Miner.HumanoidRootPart
		local hum = Miner.Humanoid
		local WalkAnim = hum:LoadAnimation(script.Parent.Anims.walk.WalkAnim)
		local MiningAnim = hum:LoadAnimation(script.Parent.Anims.Swing)
		local Mining = false
		local Params = RaycastParams.new()
		Params.FilterDescendantsInstances = {Miner,workspace.OreSpawningArea} --- remember to define our character!
		Params.FilterType = Enum.RaycastFilterType.Blacklist
		local newHitbox = RaycastHitbox.new(Miner.Handle)
		newHitbox:SetPoints(Miner.Handle,{Vector3.new(0,0,2)})
		newHitbox.RaycastParams = Params
		newHitbox.DetectionMode = RaycastHitbox.DetectionMode.PartMode
		-- relevant parts \/
		local function getTags(model)
			local tags = 0
			for _,tag in pairs(model:GetChildren()) do
				if string.find(tag.Name,'Miner') then
					tags += 1
				end
			end
			return tags
		end

		local function getNearestOre()
			local Ores = workspace.Ores:GetChildren()
			local closesetOre = Ores[1]
			for _, Ore in pairs(Ores) do
				if getTags(Ore) == 1 then
					print(getTags(Ore))
				if closesetOre:FindFirstChild(Miner.Name) then
						if Ore.Main and (closesetOre.Main.Position - humrp.Position).Magnitude > (closesetOre.Main.Position - humrp.Position).Magnitude then
							closesetOre = Ore
						end
					end
				else
					local Tag = Instance.new('ObjectValue')
					Tag.Parent = closesetOre
					Tag.Name = Miner.Name
				end
			end
			return closesetOre
		end
		--relevant parts /\
		local function breakOre()
			if Mining == false then
				Mining = true
				Miner:SetPrimaryPartCFrame(CFrame.new(humrp.Position,getNearestOre().Main.Position))
				MiningAnim:Play()
				newHitbox:HitStart()
				
				-- I apologize to my future self on my terrible readability on this code
				
				newHitbox.OnHit:Connect(function(hit)
					if hit:FindFirstAncestor('Ores') then
						local OreHealth = hit.Parent.Health.Value
						if OreHealth >= 2 then
							hit.Parent.Health.Value -= 1
							Miner.Handle.Hit:Play()
							local Particles = script.Smoke:Clone()
							delay(0.3,function()
								local newPart = Instance.new('Part')
								newPart.Transparency = 1
								newPart.Anchored = true
								newPart.Position = Miner.Handle.Attachment.WorldPosition
								newPart.Parent = Miner
								Particles.Parent = newPart
								delay(0.5,function()
									Particles.Enabled = false
									game:GetService('Debris'):AddItem(newPart,1)
								end)
							end)
						else
							Miner.Handle.Break:Play()
							for _,ore in pairs(hit.Parent:GetChildren()) do
								if ore.Name == 'Ore' then
									ore.Anchored = false
									ore.Parent = workspace
									ore.Touched:Connect(function(Hit)
										if Players:GetPlayerFromCharacter(Hit.Parent) then
											require(ReplicatedStorage.FactoryResources).Resources[ore.Name] += 1
										end
									end)
								end
							end
							if hit.Parent:IsA('Model') and hit.Parent.Name ~= 'Workspace' then
								hit.Parent:Destroy()
							end
						end
					end
				end)
				MiningAnim.Stopped:Wait()
				task.wait(0.5)
				newHitbox:HitStop()
				Mining = false
			end
		end
		
		local function walkToOre()
			local path = PathfindingService:CreatePath()

			local success, errorMessage = pcall(function()
				--path:ComputeAsync(humrp.Position, workspace.FrontOfFactory.Position + Vector3.new(math.random(1,10),math.random(1,10),math.random(1,10)))
				path:ComputeAsync(humrp.Position, getNearestOre().Main.Position + ((humrp.Position-getNearestOre().Main.Position).Unit * 6))
			end)
			WalkAnim:Play()
			if success and path.Status == Enum.PathStatus.Success then
				local waypoints = path:GetWaypoints()
				for _,waypoint in pairs(waypoints) do

					if waypoint.Action == Enum.PathWaypointAction.Jump then
						hum:ChangeState(Enum.HumanoidStateType.Jumping)
					end
					
					if getNearestOre() and (humrp.Position - getNearestOre().Main.Position).Magnitude <= 10 then
						breakOre()
					end
					
					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				end
			end
			
			WalkAnim:Stop()
			while task.wait(0.5) do
				walkToOre()
			end
		end
		
		walkToOre()
	end)
end)

local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))

Here is a gif of the problem
EDIT: making the gif embedded

As seen, the npcs just move towards a single ore, regardless whether one is closer to another or not.
I’ve tried too many things to solve this, i’ve spent about an hour yesterday and today researching stuff about this and how to solve it, and I am inconclusive.

Please help

2 Likes

Your formatting might be tricking you here

for _, Ore in pairs(Ores) do
	if getTags(Ore) == 1 then
		print(getTags(Ore))
	if closesetOre:FindFirstChild(Miner.Name) then

Be sure to run “Format Document” in the scripting tab.

This getNearestOre a small bug that prevents it from actually calculating the nearest one.

if Ore.Main and (closesetOre.Main.Position - humrp.Position).Magnitude > (closesetOre.Main.Position - humrp.Position).Magnitude then

This is checking if the closest ore is closer than the closest ore which should never return true, this will loop through and always return Ores[1]. Change one of these to check against Ore.Main.Position.

You should also definitely store this value and use it as a parameter for breakOre, calculating this many distance checks every time you call getNearestOre() (plus the nearest ore might change) is bad for performance.

local nearest = getNearestOre()
if nearest and (humrp.Position - nearest .Main.Position).Magnitude <= 10 then
	breakOre(nearest)
end
2 Likes

Thank you for helping me make this code more efficient.

I changed up the varaibles closestOre.Main to Ore.Main, but it didn’t seem to make much of a difference, here is a clip:


https://gyazo.com/af6444aefc74afe9a2247ddc720b016b (if embed failes then use this)

However, I am not as worried about that as much as the fact that all of the miners target the same ore at all times as seen in this clip.


https://gyazo.com/fe0cc2bee53c3d45bc7f2671f0e98a29 (if embed failes then use this)

I want the miners to target an individual ore, such as one miner goes to one ore, and another goes to the next, how would I do that?

you can save memory by moving the functions outside of the spawn function

2 Likes

You need to mark the closest ore as soon as it’s calculated

local function walkToOre()
	local path = PathfindingService:CreatePath()

    local nearest = getNearestOre()
    if nearest == nil then
        return
    end
    CollectionService:AddTag(nearest, "ToBeMined")

	local success, errorMessage = pcall(function()
		--path:ComputeAsync(humrp.Position, workspace.FrontOfFactory.Position + Vector3.new(math.random(1,10),math.random(1,10),math.random(1,10)))
		path:ComputeAsync(humrp.Position, nearest.Main.Position + ((humrp.Position-getNearestOre().Main.Position).Unit * 6))
	end)
	WalkAnim:Play()

-- change getNearestOre to ignore tagged
local function getNearestOre()
	local Ores = workspace.Ores:GetChildren()
	local closesetOre = nil
	local closestDistance = nil
	for _, Ore in pairs(Ores) do
		local my_distance = (Ore.Main.Position - humrp.Position).Magnitude
		if CollectionService:HasTag(Ore, "ToBeMined") then
			continue
		elseif closestOre == nil then -- nothing to compare add default
			closestOre = Ore
			closestDistance = my_distance
		elseif Ore.Main and closestDistance > my_distance then -- default was set, time to compare
			closestOre = Ore
			closestDistance = my_distance
		end
	end
	return closestOre
end
1 Like

Before:

After:

Not much of a difference really, am I doing it wrong?

new script:

local ReplicatedStorage = game:GetService('ReplicatedStorage')
local Players = game:GetService('Players')
local CollectionService = game:GetService('CollectionService')
local PathfindingService = game:GetService('PathfindingService')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)

local MinerNumber = 0

CollectionService:AddTag(ReplicatedStorage.AI.Miner,'Miner')

CollectionService:GetInstanceAddedSignal('Miner'):Connect(function(Miner)
		Miner.Name = Miner.Name..tostring(MinerNumber)
		MinerNumber += 1
		local humrp = Miner.HumanoidRootPart
		local hum = Miner.Humanoid
		local WalkAnim = hum:LoadAnimation(script.Parent.Anims.walk.WalkAnim)
		local MiningAnim = hum:LoadAnimation(script.Parent.Anims.Swing)
		local Mining = false
		local Params = RaycastParams.new()
		Params.FilterDescendantsInstances = {Miner,workspace.OreSpawningArea} --- remember to define our character!
		Params.FilterType = Enum.RaycastFilterType.Blacklist
		local newHitbox = RaycastHitbox.new(Miner.Handle)
		newHitbox:SetPoints(Miner.Handle,{Vector3.new(0,0,2)})
		newHitbox.RaycastParams = Params
		newHitbox.DetectionMode = RaycastHitbox.DetectionMode.PartMode
		local function getTags(model)
			local tags = 0
			for _,tag in pairs(model:GetChildren()) do
				if string.find(tag.Name,'Miner') then
					tags += 1
				end
			end
			return tags
		end

		local function getNearestOre()
			local Ores = workspace.Ores:GetChildren()
			local closesetOre = Ores[1]
			for _, Ore in pairs(Ores) do
				if getTags(Ore) == 1 then
					print(getTags(Ore))
					if closesetOre:FindFirstChild(Miner.Name) then
						if Ore.Main and (closesetOre.Main.Position - humrp.Position).Magnitude > (Ore.Main.Position - humrp.Position).Magnitude then
							closesetOre = Ore
						end
					end
				else
					local Tag = Instance.new('ObjectValue')
					Tag.Parent = closesetOre
					Tag.Name = Miner.Name
				end
			end
			return closesetOre
		end

		local function breakOre()
			if Mining == false then
				Mining = true
				Miner:SetPrimaryPartCFrame(CFrame.new(humrp.Position,getNearestOre().Main.Position))
				MiningAnim:Play()
				newHitbox:HitStart()

				-- I apologize to my future self on my terrible readability on this code

				newHitbox.OnHit:Connect(function(hit)
					if hit:FindFirstAncestor('Ores') then
						local OreHealth = hit.Parent.Health.Value
						if OreHealth >= 2 then
							hit.Parent.Health.Value -= 1
							Miner.Handle.Hit:Play()
							local Particles = script.Smoke:Clone()
							delay(0.3,function()
								local newPart = Instance.new('Part')
								newPart.Transparency = 1
								newPart.Anchored = true
								newPart.Position = Miner.Handle.Attachment.WorldPosition
								newPart.Parent = Miner
								Particles.Parent = newPart
								delay(0.5,function()
									Particles.Enabled = false
									game:GetService('Debris'):AddItem(newPart,1)
								end)
							end)
						else
							Miner.Handle.Break:Play()
							for _,ore in pairs(hit.Parent:GetChildren()) do
								if ore.Name == 'Ore' then
									ore.Anchored = false
									ore.Parent = workspace
									ore.Touched:Connect(function(Hit)
										if Players:GetPlayerFromCharacter(Hit.Parent) then
											require(ReplicatedStorage.FactoryResources).Resources[ore.Name] += 1
										end
									end)
								end
							end
							if hit.Parent:IsA('Model') and hit.Parent.Name ~= 'Workspace' then
								hit.Parent:Destroy()
							end
						end
					end
				end)
				MiningAnim.Stopped:Wait()
				task.wait(0.5)
				newHitbox:HitStop()
				Mining = false
			end
		end

		local function walkToOre()
			local path = PathfindingService:CreatePath()

			local success, errorMessage = pcall(function()
				--path:ComputeAsync(humrp.Position, workspace.FrontOfFactory.Position + Vector3.new(math.random(1,10),math.random(1,10),math.random(1,10)))
				path:ComputeAsync(humrp.Position, getNearestOre().Main.Position + ((humrp.Position-getNearestOre().Main.Position).Unit * 6))
			end)
			WalkAnim:Play()
			if success and path.Status == Enum.PathStatus.Success then
				local waypoints = path:GetWaypoints()
				for _,waypoint in pairs(waypoints) do

					if waypoint.Action == Enum.PathWaypointAction.Jump then
						hum:ChangeState(Enum.HumanoidStateType.Jumping)
					end

					local nearest = getNearestOre()
					if nearest and (humrp.Position - nearest .Main.Position).Magnitude <= 10 then
						breakOre(nearest)
					end

					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				end
			end

			WalkAnim:Stop()
			while task.wait(0.5) do
				walkToOre()
			end
	end
	spawn(function()
		walkToOre()
		end)
	end)

local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))

i would do

local function getNearestOre()
	local Ores = workspace.Ores:GetChildren()
	local closesetOre = Ores[1]
	local closestDistance = math.huge
	for _, Ore in pairs(Ores) do
		local my_distance = (Ore.Main.Position - humrp.Position).Magnitude

		if Ore.Main and closestDistance > my_distance then
			closestOre = Ore
			closestDistance = my_distance
		end
	end
	return closestOre
end

yes you move all the functions outside so

--functions here


CollectionService:AddTag(ReplicatedStorage.AI.Miner,'Miner')

CollectionService:GetInstanceAddedSignal('Miner'):Connect(function(Miner)
 --code here
end)


error! comparing nil > number. You removed two very intentional parts, the tag check and nil check.

just edited it to say 0 instead of nil

then this will never be true, zero is actually the worst default, try math.huge instead

if Ore.Main and 0 > my_distance then

The tag check is so the miners do not attack the same ore, like the author wants. Which is why I start with nil closest ore, if Ores[1] is tagged then it skips the whole system and can be targeted by all miners.

1 Like

what would the tag check be for?

oh ye sos got my numbers muddled


I put copied and pasted that script into the appropriate places but now this error appears and the miners appear to be very glitchy now:

New script:

local ReplicatedStorage = game:GetService('ReplicatedStorage')
local Players = game:GetService('Players')
local CollectionService = game:GetService('CollectionService')
local PathfindingService = game:GetService('PathfindingService')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)

local MinerNumber = 0

CollectionService:AddTag(ReplicatedStorage.AI.Miner,'Miner')

CollectionService:GetInstanceAddedSignal('Miner'):Connect(function(Miner)
	spawn(function()
		Miner.Name = Miner.Name..tostring(MinerNumber)
		MinerNumber += 1
		local humrp = Miner.HumanoidRootPart
		local hum = Miner.Humanoid
		local WalkAnim = hum:LoadAnimation(script.Parent.Anims.walk.WalkAnim)
		local MiningAnim = hum:LoadAnimation(script.Parent.Anims.Swing)
		local Mining = false
		local Params = RaycastParams.new()
		Params.FilterDescendantsInstances = {Miner,workspace.OreSpawningArea} --- remember to define our character!
		Params.FilterType = Enum.RaycastFilterType.Blacklist
		local newHitbox = RaycastHitbox.new(Miner.Handle)
		newHitbox:SetPoints(Miner.Handle,{Vector3.new(0,0,2)})
		newHitbox.RaycastParams = Params
		newHitbox.DetectionMode = RaycastHitbox.DetectionMode.PartMode
		
		local function getTags(model)
			local tags = 0
			for _,tag in pairs(model:GetChildren()) do
				if string.find(tag.Name,'Miner') then
					tags += 1
				end
			end
			return tags
		end

		local function getNearestOre()
			local Ores = workspace.Ores:GetChildren()
			local closestOre = nil
			local closestDistance = nil
			for _, Ore in pairs(Ores) do
				local my_distance = (Ore.Main.Position - humrp.Position).Magnitude
				if CollectionService:HasTag(Ore, "ToBeMined") then
					continue
				elseif closestOre == nil then -- nothing to compare add default
					closestOre = Ore
					closestDistance = my_distance
				elseif Ore.Main and closestDistance > my_distance then -- default was set, time to compare
					closestOre = Ore
					closestDistance = my_distance
				end
			end
			print(closestOre)
			return closestOre
		end

		local function breakOre()
			if Mining == false then
				Mining = true
				Miner:SetPrimaryPartCFrame(CFrame.new(humrp.Position,getNearestOre().Main.Position))
				MiningAnim:Play()
				newHitbox:HitStart()

				-- I apologize to my future self on my terrible readability on this code

				newHitbox.OnHit:Connect(function(hit)
					if hit:FindFirstAncestor('Ores') then
						local OreHealth = hit.Parent.Health.Value
						if OreHealth >= 2 then
							hit.Parent.Health.Value -= 1
							Miner.Handle.Hit:Play()
							local Particles = script.Smoke:Clone()
							delay(0.3,function()
								local newPart = Instance.new('Part')
								newPart.Transparency = 1
								newPart.Anchored = true
								newPart.Position = Miner.Handle.Attachment.WorldPosition
								newPart.Parent = Miner
								Particles.Parent = newPart
								delay(0.5,function()
									Particles.Enabled = false
									game:GetService('Debris'):AddItem(newPart,1)
								end)
							end)
						else
							Miner.Handle.Break:Play()
							for _,ore in pairs(hit.Parent:GetChildren()) do
								if ore.Name == 'Ore' then
									ore.Anchored = false
									ore.Parent = workspace
									ore.Touched:Connect(function(Hit)
										if Players:GetPlayerFromCharacter(Hit.Parent) then
											require(ReplicatedStorage.FactoryResources).Resources[ore.Name] += 1
										end
									end)
								end
							end
							if hit.Parent:IsA('Model') and hit.Parent.Name ~= 'Workspace' then
								hit.Parent:Destroy()
							end
						end
					end
				end)
				MiningAnim.Stopped:Wait()
				task.wait(0.5)
				newHitbox:HitStop()
				Mining = false
			end
		end

		local function walkToOre()
			local path = PathfindingService:CreatePath()

			local nearest = getNearestOre()
			if nearest == nil then
				return
			end
			CollectionService:AddTag(nearest, "ToBeMined")

			local success, errorMessage = pcall(function()
				--path:ComputeAsync(humrp.Position, workspace.FrontOfFactory.Position + Vector3.new(math.random(1,10),math.random(1,10),math.random(1,10)))
				path:ComputeAsync(humrp.Position, nearest.Main.Position + ((humrp.Position-getNearestOre().Main.Position).Unit * 6))
			end)
			WalkAnim:Play()
			if success and path.Status == Enum.PathStatus.Success then
				local waypoints = path:GetWaypoints()
				for _,waypoint in pairs(waypoints) do

					if waypoint.Action == Enum.PathWaypointAction.Jump then
						hum:ChangeState(Enum.HumanoidStateType.Jumping)
					end

					if nearest and (humrp.Position - nearest .Main.Position).Magnitude <= 10 then
						breakOre(nearest)
					end

					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				end
			end

			WalkAnim:Stop()
			while task.wait(0.5) do
				walkToOre()
			end
		end
		walkToOre()
	end)
end)

local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))

Good! You need to replace all of your getNearestOre functions with the variable found at the start. The way the function is set up it will Tag a new ore every time it is run, so the pool decreases very fast if you are running it more than once-per-miner.

1 Like

That would take a long time to do, I would have to replace all of my variables like humrp and stuff with, for example, walkToOre(humrp,hum,etc.), would it be worth it?

you wouldn’t have to do that. This line for example has a getNearestOre().Main.Position call that should just be nearest.Main.Position. You need only a careful find-and-replace.

path:ComputeAsync(humrp.Position, nearest.Main.Position + ((humrp.Position-getNearestOre().Main.Position).Unit * 6))
1 Like

this is what i think it should look like

ocal ReplicatedStorage = game:GetService('ReplicatedStorage')
local Players = game:GetService('Players')
local CollectionService = game:GetService('CollectionService')
local PathfindingService = game:GetService('PathfindingService')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)

local MinerNumber = 0

local function getTags(model)
			local tags = 0
			for _,tag in pairs(model:GetChildren()) do
				if string.find(tag.Name,'Miner') then
					tags += 1
				end
			end
			return tags
		end

local function getNearestOre()
			local Ores = workspace.Ores:GetChildren()
			local closestOre = nil
			local closestDistance = nil
			for _, Ore in pairs(Ores) do
				local my_distance = (Ore.Main.Position - humrp.Position).Magnitude
				if CollectionService:HasTag(Ore, "ToBeMined") then
					continue
				elseif closestOre == nil then -- nothing to compare add default
					closestOre = Ore
					closestDistance = my_distance
				elseif Ore.Main and closestDistance > my_distance then -- default was set, time to compare
					closestOre = Ore
					closestDistance = my_distance
				end
			end
			print(closestOre)
			return closestOre
		end

local function breakOre()
			if Mining == false then
				Mining = true
				Miner:SetPrimaryPartCFrame(CFrame.new(humrp.Position,getNearestOre().Main.Position))
				MiningAnim:Play()
				newHitbox:HitStart()

				-- I apologize to my future self on my terrible readability on this code

				newHitbox.OnHit:Connect(function(hit)
					if hit:FindFirstAncestor('Ores') then
						local OreHealth = hit.Parent.Health.Value
						if OreHealth >= 2 then
							hit.Parent.Health.Value -= 1
							Miner.Handle.Hit:Play()
							local Particles = script.Smoke:Clone()
							delay(0.3,function()
								local newPart = Instance.new('Part')
								newPart.Transparency = 1
								newPart.Anchored = true
								newPart.Position = Miner.Handle.Attachment.WorldPosition
								newPart.Parent = Miner
								Particles.Parent = newPart
								delay(0.5,function()
									Particles.Enabled = false
									game:GetService('Debris'):AddItem(newPart,1)
								end)
							end)
						else
							Miner.Handle.Break:Play()
							for _,ore in pairs(hit.Parent:GetChildren()) do
								if ore.Name == 'Ore' then
									ore.Anchored = false
									ore.Parent = workspace
									ore.Touched:Connect(function(Hit)
										if Players:GetPlayerFromCharacter(Hit.Parent) then
											require(ReplicatedStorage.FactoryResources).Resources[ore.Name] += 1
										end
									end)
								end
							end
							if hit.Parent:IsA('Model') and hit.Parent.Name ~= 'Workspace' then
								hit.Parent:Destroy()
							end
						end
					end
				end)

local function walkToOre()
			local path = PathfindingService:CreatePath()

			local nearest = getNearestOre()
			if nearest == nil then
				return
			end
			CollectionService:AddTag(nearest, "ToBeMined")

			local success, errorMessage = pcall(function()
				--path:ComputeAsync(humrp.Position, workspace.FrontOfFactory.Position + Vector3.new(math.random(1,10),math.random(1,10),math.random(1,10)))
				path:ComputeAsync(humrp.Position, nearest.Main.Position + ((humrp.Position-getNearestOre().Main.Position).Unit * 6))
			end)
			WalkAnim:Play()
			if success and path.Status == Enum.PathStatus.Success then
				local waypoints = path:GetWaypoints()
				for _,waypoint in pairs(waypoints) do

					if waypoint.Action == Enum.PathWaypointAction.Jump then
						hum:ChangeState(Enum.HumanoidStateType.Jumping)
					end

					if nearest and (humrp.Position - nearest .Main.Position).Magnitude <= 10 then
						breakOre(nearest)
					end

					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				end
			end

			WalkAnim:Stop()
			while task.wait(0.5) do
				walkToOre()
			end
		end
		walkToOre()
	end)

then

CollectionService:AddTag(ReplicatedStorage.AI.Miner,'Miner')

CollectionService:GetInstanceAddedSignal('Miner'):Connect(function(Miner)
spawn(function()
		Miner.Name = Miner.Name..tostring(MinerNumber)
		MinerNumber += 1
		local humrp = Miner.HumanoidRootPart
		local hum = Miner.Humanoid
		local WalkAnim = hum:LoadAnimation(script.Parent.Anims.walk.WalkAnim)
		local MiningAnim = hum:LoadAnimation(script.Parent.Anims.Swing)
		local Mining = false
		local Params = RaycastParams.new()
		Params.FilterDescendantsInstances = {Miner,workspace.OreSpawningArea} --- remember to define our character!
		Params.FilterType = Enum.RaycastFilterType.Blacklist
		local newHitbox = RaycastHitbox.new(Miner.Handle)
		newHitbox:SetPoints(Miner.Handle,{Vector3.new(0,0,2)})
		newHitbox.RaycastParams = Params
		newHitbox.DetectionMode = RaycastHitbox.DetectionMode.PartMode



call functions in same order

end)




1 Like

The script does seem to work a lot better now!

local ReplicatedStorage = game:GetService('ReplicatedStorage')
local Players = game:GetService('Players')
local CollectionService = game:GetService('CollectionService')
local PathfindingService = game:GetService('PathfindingService')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)

local MinerNumber = 0

CollectionService:AddTag(ReplicatedStorage.AI.Miner,'Miner')

CollectionService:GetInstanceAddedSignal('Miner'):Connect(function(Miner)
	spawn(function()
		Miner.Name = Miner.Name..tostring(MinerNumber)
		MinerNumber += 1
		local humrp = Miner.HumanoidRootPart
		local hum = Miner.Humanoid
		local WalkAnim = hum:LoadAnimation(script.Parent.Anims.walk.WalkAnim)
		local MiningAnim = hum:LoadAnimation(script.Parent.Anims.Swing)
		local Mining = false
		local Params = RaycastParams.new()
		Params.FilterDescendantsInstances = {Miner,workspace.OreSpawningArea} --- remember to define our character!
		Params.FilterType = Enum.RaycastFilterType.Blacklist
		local newHitbox = RaycastHitbox.new(Miner.Handle)
		newHitbox:SetPoints(Miner.Handle,{Vector3.new(0,0,2)})
		newHitbox.RaycastParams = Params
		newHitbox.DetectionMode = RaycastHitbox.DetectionMode.PartMode

		local function getNearestOre()
			local Ores = workspace.Ores:GetChildren()
			local closestOre = nil
			local closestDistance = nil
			for _, Ore in pairs(Ores) do
				local my_distance = (Ore.Main.Position - humrp.Position).Magnitude
				if CollectionService:HasTag(Ore, "ToBeMined") then
					continue
				elseif closestOre == nil then -- nothing to compare add default
					closestOre = Ore
					closestDistance = my_distance
				elseif Ore.Main and closestDistance > my_distance then -- default was set, time to compare
					closestOre = Ore
					closestDistance = my_distance
				end
			end
			return closestOre
		end

		local function breakOre(nearest)
			if Mining == false then
				Mining = true
				Miner:SetPrimaryPartCFrame(CFrame.new(humrp.Position,nearest.Main.Position))
				MiningAnim:Play()
				newHitbox:HitStart()

				-- I apologize to my future self on my terrible readability on this code

				newHitbox.OnHit:Connect(function(hit)
					if hit:FindFirstAncestor('Ores') then
						local OreHealth = hit.Parent.Health.Value
						if OreHealth >= 2 then
							hit.Parent.Health.Value -= 1
							Miner.Handle.Hit:Play()
							local Particles = script.Smoke:Clone()
							delay(0.3,function()
								local newPart = Instance.new('Part')
								newPart.Transparency = 1
								newPart.Anchored = true
								newPart.Position = Miner.Handle.Attachment.WorldPosition
								newPart.Parent = Miner
								Particles.Parent = newPart
								delay(0.5,function()
									Particles.Enabled = false
									game:GetService('Debris'):AddItem(newPart,1)
								end)
							end)
						else
							Miner.Handle.Break:Play()
							for _,ore in pairs(hit.Parent:GetChildren()) do
								if ore.Name == 'Ore' then
									ore.Anchored = false
									ore.Parent = workspace
									ore.Touched:Connect(function(Hit)
										if Players:GetPlayerFromCharacter(Hit.Parent) then
											require(ReplicatedStorage.FactoryResources).Resources[ore.Name] += 1
										end
									end)
								end
							end
							if hit.Parent:IsA('Model') and hit.Parent.Name ~= 'Workspace' then
								hit.Parent:Destroy()
							end
						end
					end
				end)
				MiningAnim.Stopped:Wait()
				task.wait(0.5)
				newHitbox:HitStop()
				Mining = false
			end
		end

		local function walkToOre()
			local path = PathfindingService:CreatePath()

			local nearest = getNearestOre()
			if nearest == nil then
				return
			end
			CollectionService:AddTag(nearest, "ToBeMined")

			local success, errorMessage = pcall(function()
				--path:ComputeAsync(humrp.Position, workspace.FrontOfFactory.Position + Vector3.new(math.random(1,10),math.random(1,10),math.random(1,10)))
				path:ComputeAsync(humrp.Position, nearest.Main.Position + ((humrp.Position-nearest.Main.Position).Unit * 6))
			end)
			WalkAnim:Play()
			if success and path.Status == Enum.PathStatus.Success then
				local waypoints = path:GetWaypoints()
				for _,waypoint in pairs(waypoints) do

					if waypoint.Action == Enum.PathWaypointAction.Jump then
						hum:ChangeState(Enum.HumanoidStateType.Jumping)
					end

					if nearest and (humrp.Position - nearest .Main.Position).Magnitude <= 10 then
						breakOre(nearest)
					end

					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				end
			end

			WalkAnim:Stop()
			while task.wait(0.5) do
				walkToOre()
			end
		end
		walkToOre()
	end)
end)

local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))
task.wait(0.1)
local clone = ReplicatedStorage.AI.Miner:Clone()
clone.Parent = workspace
clone:SetPrimaryPartCFrame(CFrame.new(Vector3.new(-150.777, 4, 50.515)))

However it still runs into the issue of this:


Do I pcall it or check if Main is a child of Ore?

I’m afraid that there are just too many issues with this


I would need to manually define all of the variables like local function breakOre(Mining,Miner,MiningAnim,newHitbox) for every single function, it would just be too much work and I don’t think that the performance change would be significant enough.