Enemies getting stuck

My TD enemies randomly get stuck, and BulkMoveTo stops moving them. Any way I could fix this?

Here is the code:
Replicator:

--!native
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local replicator = {}
replicator.enemies = {}
replicator.pathTable = {}
local BezierPath = require(ReplicatedStorage.Packages:WaitForChild("BezierPath"))
local BridgeNet = require(ReplicatedStorage.Packages:WaitForChild("bridgenet2"))
local enemyclass = require(ServerStorage:WaitForChild("EnemyClass"))
local packetBridge = BridgeNet.ServerBridge("enemyReplication")
local createBridge = BridgeNet.ServerBridge("enemyCreation")
local deleteBridge = BridgeNet.ServerBridge("enemyDeletion")
local janitor = require(ReplicatedStorage.Packages:WaitForChild("janitor")).new()
local hashCreator = require(ServerStorage.HashCreator)

replicator.createnavpath = function(nodefolder: Folder, index)
	local nodePositions = { nodefolder.enemyspawn.Position }

	for i = 1, #nodefolder:GetChildren() - 2 do
		table.insert(nodePositions, nodefolder[i].Position)
	end

	table.insert(nodePositions, nodefolder.exit.Position)
	local pathtable = {
		["path"] = BezierPath.new(nodePositions, 3),
		["nodefolder"] = nodefolder,
	}
	replicator.pathTable[index] = pathtable
end

replicator.getEnemyData = function()
	task.desynchronize()
	local enemyData = {}
	for hash, enemy: enemyclass.enemy in pairs(replicator.enemies) do
		enemyData[hash] = enemy.position
		if enemy.translated >= 1 then
			hashCreator.putBack(hash)
			deleteBridge:Fire(BridgeNet.AllPlayers(), hash)
			replicator.enemies[hash] = nil
		end
	end
	task.synchronize()
	return enemyData
end

replicator.getEnemyCount = function()
	return #replicator.enemies
end


replicator.getTargetingData = function()
	task.desynchronize()
	local enemyData = {}
	for hash, enemy: enemyclass.enemy in pairs(replicator.enemies) do
		table.insert(enemyData, { hash, enemy.position, enemy.health })
	end
	task.synchronize()
	return enemyData
end

replicator.enemyEffect = function(hash, effect, new)
	local enemy = replicator.enemies[hash]
	enemy[effect] = new
	if effect == "health" then
		if enemy["health"] <= 0 then
			hashCreator.putBack(hash)
			deleteBridge:Fire(BridgeNet.AllPlayers(), hash)
			replicator.enemies[hash] = nil
		end
	end
end

replicator.getSingleEnemy = function(enemyHash)
	local enemy = replicator.enemies[enemyHash]
	return enemy
end

replicator.update = function()
	packetBridge:Fire(BridgeNet.AllPlayers(), replicator.getEnemyData())
end

replicator.addEnemy = function(enemyName, pathNum)
	local eClass = enemyclass.create(enemyName, replicator.pathTable[pathNum])
	createBridge:Fire(BridgeNet.AllPlayers(), { eClass["hash"], eClass["class"] })
	local hash = eClass["hash"]
	task.wait()
	replicator.enemies[hash] = eClass
	replicator.update()
end

return replicator

EnemyClass:

--!native
--!strict
local class = {}
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BezierPath = require(ReplicatedStorage.Packages:WaitForChild("BezierPath"))
local enemystats = require(ServerStorage:WaitForChild("EnemyStats"))
local bosshealthbar = ReplicatedStorage.AddHealthBar
local AbilityManager = require(ServerStorage.AbilityManager)
local Enemies = ReplicatedStorage.Enemies
local HashCreator = require(ServerStorage.HashCreator)
local refresh_rate = 0.1
class.__index = class

export type enemy = {
	speed: number,
	health: number,
	hash: string,
	position: CFrame,
	updateConnection: RBXScriptConnection,
}

function class.create(enemyName, path: BezierPath.Path)
	ReplicatedStorage.DebugValues.EnemyCount.Value += 1
	local self = setmetatable({}, class)
	self.path = path
	self.position = CFrame.new(0,0,0)
	self.class = enemyName
	self.StartTime = os.clock()
	local enemyData = enemystats.get(enemyName)
	self.speed = enemyData.speed
	self.health = enemyData.health
	self.hash = HashCreator.retriveHash()
	self.renderID = 1
	local _, boundingBox = Enemies[enemyName]:GetBoundingBox()
	local sizeAddition = boundingBox.Y / 2
	local rotationalOffset
	local offset = math.random(-1, 1)
	if enemyData.modifiers["boss"] then
		bosshealthbar:FireAllClients(enemyData.name, self, self.health)
	end
	if enemyData.rotationOffset ~= nil then
		rotationalOffset = enemyData.rotationOffset
	else
		rotationalOffset = Vector3.zero
	end
	if enemyData["abilities"] then
		task.spawn(function()
			for ability, _ in pairs(enemyData["abilities"]) do
				AbilityManager.register("enemies", ability, self)
			end
		end)
	end
	self.updateConnection = task.spawn(function()
		local PreviousTime = os.clock()
		self.translated = 0
		while self.translated < 1 do
			task.desynchronize()
			self.translated += (os.clock() - PreviousTime) * self.speed / path["path"]:GetPathLength()
			PreviousTime = os.clock()
			local transformedcframe: CFrame = path["path"]:CalculateUniformCFrame(self.translated)
				* CFrame.new(offset, sizeAddition, 0)
				* CFrame.Angles(rotationalOffset.X, rotationalOffset.Y, rotationalOffset.Z)
			task.synchronize()
			self.position = transformedcframe
			task.wait(refresh_rate)
			task.synchronize()
		end
		ReplicatedStorage.DebugValues.EnemyCount.Value -= 1
		if self.health > 0 then
			ReplicatedStorage.BaseHealth.Value -= self.health
			self.health = 0
		end
	end)
	return self
end

function class:damage(damage)
	local clampedDamage = math.clamp(self.health - damage, 0, 9999999999)
	self.health -= clampedDamage
	if self.health <= 0 then
		class.health = 0
	end
end


return class

Client:

local Players = game:GetService("Players")


local player = Players.LocalPlayer
repeat
	task.wait()
until player:WaitForChild("loaded").Value == true
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Map = ReplicatedStorage:WaitForChild("Map")
local Enemies = ReplicatedStorage:WaitForChild("Enemies")
local loadedEnemies = {}
local Workspace = game:GetService("Workspace")
local MapObject = Workspace:WaitForChild(Map.Value)
local BridgeNet2 = require(ReplicatedStorage.Packages.bridgenet2)
local enemyPacketBridge = BridgeNet2.ReferenceBridge("enemyReplication")
local enemyCreationBridge = BridgeNet2.ReferenceBridge("enemyCreation")
local enemyDeletionBridge = BridgeNet2.ReferenceBridge("enemyDeletion")
local RunService = game:GetService("RunService")
local cframeData = {}
local function createEnemy(content)
	local hash, name = content[1], content[2]
	local enemyModel: Model = Enemies:FindFirstChild(name):Clone()
	enemyModel.Name = hash
	enemyModel.Parent = MapObject.enemies
	loadedEnemies[hash] = enemyModel.PrimaryPart
	local animation = enemyModel.Animations.walk
	local track = enemyModel.AnimationController.Animator:LoadAnimation(animation)
	track:Play()
end

local function deleteEnemy(content)
	local enemy = loadedEnemies[content]
	enemy.Parent:Destroy()
	loadedEnemies[content] = nil
end
local alpha = 0
local function updatePosition(delta)
	local Parts = {}
	local CFrames = {}
	local index = 1
	for hash, cframe in pairs(cframeData) do
		local enemyPrimary = loadedEnemies[hash]
		alpha = math.clamp(delta / 0.1, 0,1)
		Parts[index] = enemyPrimary
		CFrames[index] = enemyPrimary.CFrame:Lerp(cframe, alpha)
		index += 1
	end
	task.synchronize()
	Workspace:BulkMoveTo(Parts, CFrames, Enum.BulkMoveMode.FireCFrameChanged)
end

local function updateData(content)
	cframeData = content
end
enemyCreationBridge:Connect(createEnemy)
enemyPacketBridge:Connect(updateData)
enemyDeletionBridge:Connect(deleteEnemy)
RunService.Heartbeat:ConnectParallel(updatePosition)

Thanks in Advance!

Edit: I have narrowed it down to the function in the replicator that gets enemy data, and the enemy positioning is retuning nil. Still have no idea how to fix it

1 Like

Hi! Is it possible to get a short video of the issue happening?

2 Likes

Hi, here you go!

2 Likes

Interesting! So is it also safe to assume that if you only had a small number of enemies this issue persists?

1 Like

um
I think so? i think it breaks at about 300+ enemies.

So assuming that it works perfectly well in cases under 300 enemies, then I would suppose you can try splitting your Parts into multiple tables, and have multiple BulkMoveTo functions to deal with the tables with less elements. I am not sure if this approach even works, but it might be worth a shot? Sorry I couldn’t offer concrete advice!

2 Likes

wait nevermind when it gets stuck randomly, idk whats happening now

BulkMoveTo is working, but the remotes are losing packets. Any way to fix this?

1 Like

any help? I need to get this fixed