No I want everything to be initialized the same as the first design, but now i want track. Dont change anything I need to do. Here is the code again. Script:-- TemplateScript (ServerScript)
-- Place this script in ServerScriptService or an appropriate location

local RollerCoasterManager = require(game.ServerScriptService:WaitForChild("RollerCoasterManager"))

-- Define the list of coasters you want to interact with
local coasterNames = {"Coaster", "Coaster2", "Coaster3"} -- Add more as needed

-- Function to handle touch events with waypoint numbers
local function HandleCoasterTouch(waypointNumber, coasterName)
	print("[TemplateScript] Cart passed waypoint #" .. waypointNumber .. " on coaster:", coasterName)

	-- Example Action: Activate a booster when a waypoint is touched
	local boosterAmount = 10 -- Adjust as needed
	RollerCoasterManager.ActivateBooster(coasterName, boosterAmount)

	-- Additional Custom Logic Here
	-- For example, spawn effects, update UI, etc.
end

-- Attach the touch handler to each coaster's touch event
local function SetupTouchHandlers()
	for _, coasterName in ipairs(coasterNames) do
		local controller = RollerCoasterManager.GetController(coasterName)
		if controller then
			controller:OnTouched(function(waypointNumber, coasterName)
				HandleCoasterTouch(waypointNumber, coasterName)
			end)
		else
			warn("[TemplateScript] No controller found for '" .. coasterName .. "'.")
		end
	end
end

-- Example Interactions
local function ExampleInteractions()
	-- Start Physics for Coaster2 after 5 seconds
	wait(5)
	RollerCoasterManager.StartPhysics("Coaster2")

	-- Set Velocity for Coaster3 to 20 m/s after 10 seconds
	wait(5)
	RollerCoasterManager.SetVelocity("Coaster3", 20)

	-- Restart Coaster1 after 15 seconds
	wait(5)
	RollerCoasterManager.RestartCoaster("Coaster")

	-- Stop Physics for Coaster2 after 20 seconds
	wait(5)
	RollerCoasterManager.StopPhysics("Coaster2")
end

-- Initialize the template script
local function Initialize()
	SetupTouchHandlers()
	ExampleInteractions()
end

-- Run the Initialize function on script start
Initialize()
Module:-- RollerCoasterModule (ModuleScript)
-- Place this script in ServerScriptService

local RollerCoasterController = {}
RollerCoasterController.__index = RollerCoasterController

-- Services
local RunService = game:GetService("RunService")
local Workspace = workspace

-- Constants (Adjust as needed)
local GRAVITY = 9.8 -- Gravity acceleration (m/s²)
local FRICTION_COEFFICIENT = 0.2 -- Kinetic friction coefficient (0 < μ < 1)
local SAMPLE_STEP = 0.001 -- Arc-length sampling step (smaller for higher accuracy)
local INITIAL_SPEED = 0 -- Starting speed of the cart (m/s)

-- Constructor
function RollerCoasterController.new(coasterFolderName)
	local self = setmetatable({}, RollerCoasterController)

	-- References
	self.coasterFolder = Workspace:WaitForChild(coasterFolderName)
	self.cart = self.coasterFolder:WaitForChild("Cart") -- Each coaster has its own Cart

	-- Verify Cart Properties
	self.cart.Anchored = false -- Ensure cart is not anchored
	self.cart.Velocity = Vector3.new(0, 0, 0) -- Reset initial velocity

	-- Track Data
	self.trackPoints = {} -- Sorted list of track point positions
	self.touchableWaypoints = {} -- List of touchable waypoint numbers and their s positions
	self.arcLengthMap = {} -- Mapping of cumulative arc-length to spline parameters
	self.totalTrackLength = 0 -- Total length of the track

	-- Events
	self.TouchEvent = Instance.new("BindableEvent")
	self.TouchEvent.Name = "TouchEvent"
	self.TouchEvent.Parent = self.coasterFolder

	-- Initialize movement variables
	self.s = 0 -- Current arc-length position
	self.v = INITIAL_SPEED -- Current velocity (m/s)
	self.isPhysicsRunning = false
	self.moveConnection = nil

	-- Load track points and setup
	self:LoadTrackPoints()
	self:PrecomputeArcLengthMap()
	self:IdentifyTouchableWaypoints()

	print("[RollerCoasterController] Initialized for:", coasterFolderName)
	return self
end

-- Load and sort track points based on 'Value' IntValue
function RollerCoasterController:LoadTrackPoints()
	local unsortedParts = {}

	for _, part in ipairs(self.coasterFolder:GetChildren()) do
		if part:IsA("Part") then
			local orderValue = part:FindFirstChild("Value")
			if orderValue and orderValue:IsA("IntValue") then
				table.insert(unsortedParts, { part = part, order = orderValue.Value })
			end
		end
	end

	-- Sort parts based on 'Value'
	table.sort(unsortedParts, function(a, b)
		return a.order < b.order
	end)

	-- Populate trackPoints with sorted positions
	for _, entry in ipairs(unsortedParts) do
		table.insert(self.trackPoints, entry.part.Position)
	end

	-- Ensure there are enough points
	if #self.trackPoints < 2 then
		error("[RollerCoasterController] Not enough track points in '" .. self.coasterFolder.Name .. "'.")
	end

	print("[RollerCoasterController] Loaded", #self.trackPoints, "track points for:", self.coasterFolder.Name)
end

-- Precompute arc-length mapping for the track
function RollerCoasterController:PrecomputeArcLengthMap()
	self.arcLengthMap = {}
	local cumulativeLength = 0

	for segment = 1, #self.trackPoints - 1 do
		local lastPos = self.trackPoints[segment]
		for t = SAMPLE_STEP, 1, SAMPLE_STEP do
			local currentPos = self:GetSplinePoint(t, segment)
			local delta = (currentPos - lastPos).Magnitude
			cumulativeLength = cumulativeLength + delta
			table.insert(self.arcLengthMap, { s = cumulativeLength, segment = segment, t = t })
			lastPos = currentPos
		end
	end

	self.totalTrackLength = cumulativeLength
	print("[RollerCoasterController] Total track length:", self.totalTrackLength, "meters for:", self.coasterFolder.Name)
end

-- Catmull-Rom Spline Interpolation
function RollerCoasterController:CatmullRomSpline(p0, p1, p2, p3, t)
	local t2 = t * t
	local t3 = t2 * t

	return 0.5 * (
		(2 * p1) +
			(-p0 + p2) * t +
			(2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 +
			(-p0 + 3 * p1 - 3 * p2 + p3) * t3
	)
end

-- Get Spline Point at (t, segment)
function RollerCoasterController:GetSplinePoint(t, segment)
	local p0 = self.trackPoints[math.max(segment - 1, 1)]
	local p1 = self.trackPoints[segment]
	local p2 = self.trackPoints[math.min(segment + 1, #self.trackPoints)]
	local p3 = self.trackPoints[math.min(segment + 2, #self.trackPoints)]

	return self:CatmullRomSpline(p0, p1, p2, p3, t)
end

-- Find spline parameters (segment and t) for a given arc-length s
function RollerCoasterController:FindSplineParameters(s)
	if s <= 0 then
		return 1, 0
	elseif s >= self.totalTrackLength then
		return #self.trackPoints - 1, 1
	end

	-- Binary search
	local left = 1
	local right = #self.arcLengthMap
	local mid

	while left <= right do
		mid = math.floor((left + right) / 2)
		if self.arcLengthMap[mid].s < s then
			left = mid + 1
		elseif self.arcLengthMap[mid].s > s then
			right = mid - 1
		else
			return self.arcLengthMap[mid].segment, self.arcLengthMap[mid].t
		end
	end

	-- Interpolate between right and left
	local entry1 = self.arcLengthMap[right]
	local entry2 = self.arcLengthMap[left]

	local ratio = (s - entry1.s) / (entry2.s - entry1.s)
	local segment = entry1.segment
	local t = entry1.t + ratio * (entry2.t - entry1.t)

	return segment, t
end

-- Identify touchable waypoints and store their numbers and s positions
function RollerCoasterController:IdentifyTouchableWaypoints()
	for _, part in ipairs(self.coasterFolder:GetChildren()) do
		if part:IsA("Part") then
			local touchable = part:FindFirstChild("Touchable")
			if touchable and touchable:IsA("BoolValue") and touchable.Value then
				local value = part:FindFirstChild("Value")
				if value and value:IsA("IntValue") then
					local waypointNumber = value.Value
					-- Approximate s position
					local touchS = 0
					for _, entry in ipairs(self.arcLengthMap) do
						local pos = self:GetSplinePoint(entry.t, entry.segment)
						local distance = (pos - self.trackPoints[waypointNumber]).Magnitude
						if distance < 0.5 then -- Threshold to find closest s
							touchS = entry.s
							break
						end
					end
					table.insert(self.touchableWaypoints, { number = waypointNumber, s = touchS, touched = false })
					print("[RollerCoasterController] Touchable waypoint #" .. waypointNumber .. " at s =", touchS, "meters.")
				end
			end
		end
	end
end

-- Start the physics simulation
function RollerCoasterController:StartPhysics()
	if self.isPhysicsRunning then
		warn("[RollerCoasterController] Physics already running for:", self.coasterFolder.Name)
		return
	end

	self.isPhysicsRunning = true
	self.s = 0
	self.v = INITIAL_SPEED

	-- Position the cart at the start
	local startPos = self.trackPoints[1]
	self.cart.Position = startPos
	local initialTangent = (self.trackPoints[2] - self.trackPoints[1]).Unit
	self.cart.CFrame = CFrame.lookAt(startPos, startPos + initialTangent)
	self.cart.Velocity = Vector3.new(0, 0, 0)

	-- Connect to Heartbeat
	self.moveConnection = RunService.Heartbeat:Connect(function(deltaTime)
		if not self.isPhysicsRunning then return end

		-- Calculate acceleration
		local segment, t = self:FindSplineParameters(self.s)
		local pos = self:GetSplinePoint(t, segment)
		local tangent = (self:GetSplinePoint(t + 0.0001, segment) - pos).Unit
		local sinTheta = tangent.Y
		local cosTheta = math.sqrt(1 - sinTheta ^ 2) or 0.0001 -- Prevent division by zero
		local a_gravity = -GRAVITY * sinTheta
		local frictionDirection = self.v > 0 and -1 or (self.v < 0 and 1 or 0)
		local a_friction = FRICTION_COEFFICIENT * GRAVITY * cosTheta * frictionDirection
		local a_total = a_gravity + a_friction

		-- Update velocity and position
		self.v = self.v + a_total * deltaTime
		self.s = self.s + self.v * deltaTime

		-- Clamp s within track limits
		if self.s < 0 then
			self.s = 0
			self.v = 0
		elseif self.s >= self.totalTrackLength then
			self.s = self.totalTrackLength
			self.v = 0
			self:StopPhysics()
			print("[RollerCoasterController] Cart reached end of track:", self.coasterFolder.Name)
			return
		end

		-- Update cart position and orientation
		local newSegment, newT = self:FindSplineParameters(self.s)
		local newPos = self:GetSplinePoint(newT, newSegment)
		self.cart.Position = newPos

		local newTangent = (self:GetSplinePoint(newT + 0.0001, newSegment) - newPos).Unit
		self.cart.CFrame = CFrame.lookAt(newPos, newPos + newTangent)

		-- Check for touchable waypoints
		for _, waypoint in ipairs(self.touchableWaypoints) do
			if not waypoint.touched and self.s >= waypoint.s then
				waypoint.touched = true
				print("[RollerCoasterController] Touched waypoint #" .. waypoint.number .. " on " .. self.coasterFolder.Name)
				self.TouchEvent:Fire(waypoint.number, self.coasterFolder.Name)
			end
		end
	end)

	print("[RollerCoasterController] Physics started for:", self.coasterFolder.Name)
end

-- Stop the physics simulation
function RollerCoasterController:StopPhysics()
	if not self.isPhysicsRunning then
		warn("[RollerCoasterController] Physics not running for:", self.coasterFolder.Name)
		return
	end

	self.isPhysicsRunning = false
	if self.moveConnection then
		self.moveConnection:Disconnect()
		self.moveConnection = nil
	end

	print("[RollerCoasterController] Physics stopped for:", self.coasterFolder.Name)
end

-- Set the cart's velocity externally
function RollerCoasterController:SetVelocityExternal(newVelocity)
	self.v = newVelocity
	print("[RollerCoasterController] Velocity set to", newVelocity, "m/s for:", self.coasterFolder.Name)
end

-- Activate a booster to increase velocity
function RollerCoasterController:ActivateBooster(boostAmount)
	self.v = self.v + boostAmount
	print("[RollerCoasterController] Booster activated! New velocity:", self.v, "m/s for:", self.coasterFolder.Name)

	-- Example: Play booster sound if available
	local boosterSound = self.coasterFolder:FindFirstChild("BoosterSound")
	if boosterSound and boosterSound:IsA("Sound") then
		boosterSound:Play()
	end
end

-- Restart the coaster simulation
function RollerCoasterController:RestartCoaster()
	print("[RollerCoasterController] Restarting coaster:", self.coasterFolder.Name)
	self:StopPhysics()
	-- Reset touchable waypoints
	for _, waypoint in ipairs(self.touchableWaypoints) do
		waypoint.touched = false
	end
	self:StartPhysics()
	print("[RollerCoasterController] Coaster restarted:", self.coasterFolder.Name)
end

-- Attach a callback function to the TouchEvent
function RollerCoasterController:OnTouched(callback)
	if typeof(callback) ~= "function" then
		error("[RollerCoasterController] OnTouched expects a function.")
	end
	self.TouchEvent.Event:Connect(callback)
end

return RollerCoasterController
Second Module:-- RollerCoasterManager (ModuleScript)
-- Place this script in ServerScriptService

local RollerCoasterModule = require(script:WaitForChild("RollerCoasterModule"))

-- List of Roller Coaster Folder Names
local coasterFolderNames = {
	"Coaster",    -- Existing coaster
	"Coaster2",   -- Additional coaster
	"Coaster3",   -- Add more as needed
	-- Add more coaster folder names here
}

-- Table to hold RollerCoasterController instances
local RollerCoasterControllers = {}

-- Initialize All Roller Coasters
local function InitializeRollerCoasters()
	for _, folderName in ipairs(coasterFolderNames) do
		local coasterFolder = workspace:FindFirstChild(folderName)
		if coasterFolder then
			-- Ensure each folder has a "Cart" part
			local cart = coasterFolder:FindFirstChild("Cart")
			if cart and cart:IsA("BasePart") then
				-- Instantiate the RollerCoasterController
				local controller = RollerCoasterModule.new(folderName)
				RollerCoasterControllers[folderName] = controller

				-- Start Physics Automatically
				controller:StartPhysics()

				print("[RollerCoasterManager] Initialized and started physics for:", folderName)
			else
				warn("[RollerCoasterManager] Folder '" .. folderName .. "' is missing a 'Cart' part or it's not a BasePart.")
			end
		else
			warn("[RollerCoasterManager] Folder '" .. folderName .. "' not found in Workspace.")
		end
	end
end

-- Initialize coasters on script start
InitializeRollerCoasters()

-- Exposed Functions to Manage Coasters

local RollerCoasterManager = {}

-- Start Physics for a Specific Coaster
function RollerCoasterManager.StartPhysics(folderName)
	local controller = RollerCoasterControllers[folderName]
	if controller then
		controller:StartPhysics()
		print("[RollerCoasterManager] Physics started for:", folderName)
	else
		warn("[RollerCoasterManager] No controller found for '" .. folderName .. "'.")
	end
end

-- Stop Physics for a Specific Coaster
function RollerCoasterManager.StopPhysics(folderName)
	local controller = RollerCoasterControllers[folderName]
	if controller then
		controller:StopPhysics()
		print("[RollerCoasterManager] Physics stopped for:", folderName)
	else
		warn("[RollerCoasterManager] No controller found for '" .. folderName .. "'.")
	end
end

-- Set Velocity for a Specific Coaster
function RollerCoasterManager.SetVelocity(folderName, newVelocity)
	local controller = RollerCoasterControllers[folderName]
	if controller then
		controller:SetVelocityExternal(newVelocity)
		print("[RollerCoasterManager] Velocity set to", newVelocity, "m/s for:", folderName)
	else
		warn("[RollerCoasterManager] No controller found for '" .. folderName .. "'.")
	end
end

-- Activate Booster for a Specific Coaster
function RollerCoasterManager.ActivateBooster(folderName, boostAmount)
	local controller = RollerCoasterControllers[folderName]
	if controller then
		controller:ActivateBooster(boostAmount)
		print("[RollerCoasterManager] Booster activated by", boostAmount, "m/s for:", folderName)
	else
		warn("[RollerCoasterManager] No controller found for '" .. folderName .. "'.")
	end
end

-- Restart a Specific Coaster
function RollerCoasterManager.RestartCoaster(folderName)
	local controller = RollerCoasterControllers[folderName]
	if controller then
		controller:RestartCoaster()
		print("[RollerCoasterManager] Coaster restarted for:", folderName)
	else
		warn("[RollerCoasterManager] No controller found for '" .. folderName .. "'.")
	end
end

-- Function to Get a Controller (for attaching custom event listeners)
function RollerCoasterManager.GetController(folderName)
	return RollerCoasterControllers[folderName]
end

-- Return the Manager table
return RollerCoasterManager