Needing help with linear interpolation!

Hello. Since i can make linear interpolation on one segment, i need your help how to make an object to go to other segment. There’s an image:


and the code:

local RunService = game:GetService("RunService")

local part = script.Parent

local trackNetwork = {}

local NodeSegments = {
	seg1 = {
		Vector3.new(-130, 0.5, -70),
		Vector3.new(-130, 0.5, -20),
		Vector3.new(-151.75, 0.5, 18),
		Vector3.new(-151.75, 0.5, 68),
	},
	seg2 = {
		Vector3.new(-151.75, 0.5, 68),
		Vector3.new(-151.75, 0.5, 113),
		Vector3.new(-151.75, 0.5, 163),
		Vector3.new(-136.75, 0.5, 213),
		Vector3.new(-96.75, 0.5, 248),
	},
	seg3 = {
		Vector3.new(-96.75, 0.5, 248),
		Vector3.new(-46.75, 0.5, 263),
		Vector3.new(3.25, 0.5, 263),
		Vector3.new(53.25, 0.5, 263),
		Vector3.new(103.25, 0.5, 263),
	}
}

trackNetwork[1] = NodeSegments.seg1
trackNetwork[2] = NodeSegments.seg2
trackNetwork[3] = NodeSegments.seg3

local nodes = trackNetwork[1]

local segmentLengths = {}
local totalTrackLength = 0

for i = 1, #nodes - 1 do
	local len = (nodes[i+1] - nodes[i]).Magnitude
	segmentLengths[i] = len
	totalTrackLength = totalTrackLength + len
end

local triggers =
	{
		distance = 50,
		name = "Ograniczenie_40",
	}

local function getPositionAtDistance(targetDist)
	targetDist = math.clamp(targetDist, 0, totalTrackLength)
	
	local run = 0
	for i = 1, #segmentLengths do
		local segLen = segmentLengths[i]
		
		if run + segLen >= targetDist then
			targetDist -= run
			local a = targetDist/segLen
			return nodes[i]:Lerp(nodes[i+1], a)
		end
		run += segLen
	end
	return nodes[#nodes] --safety reasons if the table returns nil
end

local speed = 20
local currentDist = 0

local function checkTriggers(prevDist, currDist)
	--print(prevDist, currDist)
	for _, trigger in pairs(triggers) do
		-- Sprawdzamy, czy w tej klatce minęliśmy pozycję wyzwalacza (jazda do przodu)
		if prevDist < triggers.distance and currDist >= triggers.distance then
			print("test")
		end	
	end
end

wait(5)
print(3)
wait(1)
print(2)
wait(1)
print(1)
wait(1)
RunService.Heartbeat:Connect(function(dt)
	if totalTrackLength == 0 then return end
	
	local previousDist = currentDist - 10

	currentDist = math.clamp(currentDist + speed * dt, 0, totalTrackLength)
	
	part.Position = getPositionAtDistance(currentDist)
	checkTriggers(previousDist, currentDist)
	
end)

How to make an object to go to next segment (from segment 1 to segment 2, from segment 2 to segment 3 etc.)?
I tried to use this code inside Hearbeat:

for i = 1, #trackNetwork do
		print(trackNetwork[i])
		if currentDist >= totalTrackLength then
			currentDist=0
			nodes = trackNetwork[i]
		end
	end

At first, it seems okay, object moves to segment 2, but when the object is inside segment 2, instead of going to segment 3, it loops inside segment 2. Should I convert a for loop of totalTrackLength into function?

Correct me if I’m wrong, but aren’t you only getting the nodes from segment1?

I don’t see ‘nodes’ being updated anywhere, and all the code that moves the part is using the current nodes (always segment1) and then setting the part position

I may have misunderstood something though, so my apologies

1 Like

I have no idea how to update the nodes…

You could likely make this into a function that gets called (I’ll just name it ‘update’)

(Also these variables aren’t representing what their name says, segmentLength is just ‘node length’ and totalTrackLength is just the length of one segment in the entire track - seg1 / seg2 / seg3… )

--When you first define nodes
local trackMax = 3 --How many segments you have in the track
local current = 1
local nodes = trackNetwork[current]
RunService.Heartbeat:Connect(function(dt)
	--... Other code here

	if currentDist >= totalTrackLength then
		currentDist = 0 --Distance in the current segment?
		if current < trackMax then
			current += 1 --We reached the end of the current segment, go to the next one
		else
			current = 1 --If you want to make the track loop after reaching the end, otherwise you can remove this
		end
		nodes = trackNetwork[current]
		update() --Function that recalculates segmentLengths and totalTrackLength
		end

	
	part.Position = getPositionAtDistance(currentDist)
	
	--... Other code here

end)

Reason that this is looping segment 2, is because ‘i’ will never reach 3 (Because you weren’t updating totalTrackLength and you were looping through every segment in the track → if there is a longer segment right before a shorter one, it would infinitely loop there

Let me know if it fixes it

yeah make it a function. your lengths only get calculated once for seg1 so when you swap to seg2 it’s still using seg1’s lengths, and the for loop doesn’t really move you to the next segment. just keep track of which segment you’re on:

local currentSegment = 1
local nodes
local segmentLengths
local totalTrackLength

local function loadSegment(index)
	nodes = trackNetwork[index]
	segmentLengths = {}
	totalTrackLength = 0

	for i = 1, #nodes - 1 do
		local len = (nodes[i+1] - nodes[i]).Magnitude
		segmentLengths[i] = len
		totalTrackLength = totalTrackLength + len
	end
end

loadSegment(currentSegment)

then in heartbeat:

RunService.Heartbeat:Connect(function(dt)
	if totalTrackLength == 0 then return end
	
	local previousDist = currentDist
	currentDist = currentDist + speed * dt

	if currentDist >= totalTrackLength then
		if currentSegment < #trackNetwork then
			currentDist = currentDist - totalTrackLength
			currentSegment = currentSegment + 1
			loadSegment(currentSegment)
		else
			currentDist = totalTrackLength
		end
	end
	
	part.Position = getPositionAtDistance(currentDist)
	checkTriggers(previousDist, currentDist)
end)

also your triggers print twice cause pairs is looping over distance and name. make it a list:

local triggers = {
	{distance = 50, name = "Ograniczenie_40"},
}

local function checkTriggers(prevDist, currDist)
	for _, trigger in pairs(triggers) do
		if prevDist < trigger.distance and currDist >= trigger.distance then
			print(trigger.name)
		end
	end
end

btw trigger distances are per segment now since currentDist resets each time

2 Likes