Bezier Path works but not really at the same time?

  1. What do you want to achieve? I’m making my tower defense game and i used Bezier path module script so enemies can walk in corners in Bezier curve

  2. What is the issue? It works but not really, at the first waypoint it doesn’t work but it does at the same time i guess? but at the second and others next it does, and the MovingTo is being calculated wrong which makes the tower’s targeting really bad, it aims at the last enemy instead of the first, it’s my first time doing something like this.

  3. What solutions have you tried so far? I know how people used this but I’m really bad at calculation

Enemy Moving Function

function enemy.Move(enemy, map, movingToValue)
	local humanoid = enemy:WaitForChild("Humanoid")
	local waypoints = map.Waypoints

	local Positions = {}
	for _, waypoint in ipairs(waypoints:GetChildren()) do
		table.insert(Positions, waypoint.Position)
	end

	local NewPath = BezierPath.new(Positions, 3)

	if movingToValue == nil then
		for t = 0, 1, 1/100 do
			enemy.Config.MovingTo.Value = math.floor(t * #Positions) + 1
			repeat
				humanoid:MoveTo(NewPath:CalculateUniformPosition(t))
			until humanoid.MoveToFinished:Wait()
		end

		enemy:Destroy()
		if enemy.Name ~= "Arrow" then
			map.Base.Humanoid:TakeDamage(humanoid.Health)
			TakeDamageEvent:FireAllClients()
		end
	else
		for t = (movingToValue - 1)/#Positions, 1, 1/100 do
			enemy.Config.MovingTo.Value = math.floor(t * #Positions) + 1
			repeat
				humanoid:MoveTo(NewPath:CalculateUniformPosition(t))
			until humanoid.MoveToFinished:Wait()
		end

		enemy:Destroy()
		if enemy.Name ~= "Arrow" then
			map.Base.Humanoid:TakeDamage(humanoid.Health)
			TakeDamageEvent:FireAllClients()
		end
	end
end

BezierPath Module:

Hey I am also using this module too, but I don’t think my infrastructure is the same as you and I don’t think that the proper way to do it either.

I usually hook the moving using RunService like a game loop and some of the other stuff too, you can see when to use it and most things about it here Game Loop · Sequencing Patterns · Game Programming Patterns.

Anyway here what I did:

First I create 3 tables to use to store information about stuff: Enemies, NextCFrames, ToRemoves. Then the generated Path

When a new enemy is added I add that enemy data to the Enemies table maybe like this: { SumDeltaTime = 0, PhysicalEnemyInstance = EnemyInstace, TotalTime = PathLength / Speed }

And there will be a loop outside that runs every frame using RunService.Heartbeat

so it will probably be like this:

local Enemies = {}
local EnemiesPrimaryPart = {}
local NextCFrames = {}
local ToRemoves = {}

local Path = BezierPath.new(Waypoints)
local PathLength = Path:GetPathLength()

local enemy = {}

function enemy.Add(enemy, speed)
	table.insert(Enemies, {SumDeltaTime = 0, PhysicalEnemyInstance = EnemyModel, TotalTime = PathLength / speed } )
end

game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
	for index, enemy in ipairs(Enemies) do
		local t = (enemy.SumDeltaTime + deltaTime) / enemy.TotalTime

		if t >= 1 then
			table.insert(ToRemoves, index)
		else
			local cf = Path:CalculateUniformCFrame(t)
			table.insert(EnemiesPrimaryPart, enemy.PhysicalEnemyInstance.PrimaryPart)
			table.insert(NextCFrames, cf)
		end
	end

	for _, removeValue in ipairs(ToRemoves) do
		Enemies[removeValue].PhysicalEnemyInstance:Destroy()
		table.remove(Enemies, removeValue)
	end

	-- nice optimization from https://youtu.be/UamdKI7k4Mw?si=amsFfbJsNHHen0W5&t=5005
	workspace:BulkMoveTo(EnemiesPrimaryPart, NextCFrames)
	
	table.clear(EnemiesPrimaryPart)
	table.clear(NextCFrames)
	table.clear(ToRemoves)
end)

btw it just a prototype code that based off of my incomplete code I am working on currently, also there are some optimization consider such as reusing enemy after it reaches the end of the map instead of creating new one when needed, but that isn’t the things you asked, so I think that all there to its.