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