Pet following system not working as intended

Hey, I wanted to make a system where equipped towers in my tower defense game would follow the player kind of like pets in a simulator. They follow the way I want but only in 2 directions. The other 2 directions have them misaligned and all weird. I also wanted to have them look at the tower Infront of them so it could be a smoother “train” per say.


local runService = game:GetService("RunService")
local towers = ReplicatedStorage:WaitForChild("Towers")
local towerPets = {}

-- Load the selected towers and place them in the workspace
for i, v in pairs(playerData.SelectedTowers) do
	local tower = ReplicatedStorage.Towers:FindFirstChild(v):Clone()
	tower:WaitForChild("HumanoidRootPart").CFrame = game.Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart").CFrame
	tower.Parent = workspace
	table.insert(towerPets, tower)
end

-- Function to create a tween for smooth movement
local function createTween(target, goal, time)
	local tweenInfo = TweenInfo.new(time, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
	return TweenService:Create(target, tweenInfo, goal)
end

-- Function to get the ground position below a given point using raycasting
local function getGroundPosition(position)
	local rayOrigin = position
	local rayDirection = Vector3.new(0, -1000, 0)
	local raycastParams = RaycastParams.new()
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.FilterDescendantsInstances = towerPets

	local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
	if raycastResult then
		return raycastResult.Position
	else
		return position
	end
end

-- Main loop to continuously update positions
runService.RenderStepped:Connect(function(deltaTime)
	local playerCFrame = game.Players.LocalPlayer.Character.PrimaryPart.CFrame
	local previousCFrame = playerCFrame * CFrame.new(0, 0, 2) -- Start directly behind the player

	for i, tower in ipairs(towerPets) do
		local humanoidRootPart = tower:WaitForChild("HumanoidRootPart")
		local offset = CFrame.new(0, 0, 1.5) -- Spacing of 2 studs apart

		-- Calculate the target position based on the previous position and offset
		local targetCFrame = previousCFrame * offset

		-- Get the ground position below the target position
		local groundPosition = getGroundPosition(targetCFrame.Position)
		targetCFrame = CFrame.new(groundPosition) * CFrame.Angles(0, targetCFrame:ToEulerAnglesXYZ())

		createTween(humanoidRootPart, {CFrame = targetCFrame}, 0.1):Play()

		-- Update the previous position and orientation for the next tower
		previousCFrame = humanoidRootPart.CFrame
	end
end)

I tried using chatgpt for this but it took forever to get working like it is right now and it never figured out the rotation that I wanted.

2 Likes