Universal Follow Behind free
Key RequiredUniversal

Universal Follow Behind free

Universal Scriptdiskkittendiskkitten 7K views 1.4K executesUpdated Aug 22, 2025
5.0/ 5(3 votes)
30100% Positive
Lua Script•432 lines (13505 chars)
local Players = game:GetService("Players");
local RunService = game:GetService("RunService");
local UserInputService = game:GetService("UserInputService");
local TweenService = game:GetService("TweenService");
local LocalPlayer = Players.LocalPlayer;
local Workspace = workspace;
local UPDATE_INTERVAL = 0.01;
local LIST_REFRESH = 0.1;
local BEHIND_STUDS = 4;
local HEIGHT_OFFSET = 2;
local TOGGLE_KEY = Enum.KeyCode.G;
local TWEEN_DURATION_FACTOR = 0.95;
local TWEEN_EASING_STYLE = Enum.EasingStyle.Sine;
local TWEEN_EASING_DIR = Enum.EasingDirection.Out;
local PLAYER_SMOOTH_SPEED = 8;
local CAMERA_ASSIST = 0.18;
local CAMERA_MIN_ASSIST = 0.03;
local CAMERA_ASSIST_SCALE = 12;
local MOUSE_OVERRIDE_WINDOW = 0.25;
local targetPlayer = nil;
local running = false;
local lastTpTick = 0;
local lastListTick = 0;
local lastMouseMove = 0;
local cameraSaved = nil;
local activeTween = nil;
local function getCharacter(p)
	return (p and p.Character) or nil;
end
local function getHRP(character)
	if not character then
		return nil;
	end
	return character:FindFirstChild("HumanoidRootPart");
end
local function getHumanoid(character)
	if not character then
		return nil;
	end
	return character:FindFirstChildOfClass("Humanoid");
end
local function distanceToPlayer(p)
	local myChar = getCharacter(LocalPlayer);
	local myHRP = myChar and getHRP(myChar);
	local theirHRP = getHRP(getCharacter(p));
	if not (myHRP and theirHRP) then
		return math.huge;
	end
	return (theirHRP.Position - myHRP.Position).Magnitude;
end
local function saveCameraState()
	local cam = Workspace.CurrentCamera;
	if not cam then
		return;
	end
	cameraSaved = {Type=cam.CameraType,Subject=cam.CameraSubject,CFrame=cam.CFrame};
end
local function restoreCameraState()
	local cam = Workspace.CurrentCamera;
	if (not cam or not cameraSaved) then
		return;
	end
	pcall(function()
		cam.CameraType = cameraSaved.Type or Enum.CameraType.Custom;
		cam.CameraSubject = cameraSaved.Subject;
		if cameraSaved.CFrame then
			cam.CFrame = cameraSaved.CFrame;
		end
	end);
	cameraSaved = nil;
end
local function smoothAlpha(speed, dt)
	return math.clamp(1 - math.exp(-speed * dt), 0, 1);
end
local ScreenGui = Instance.new("ScreenGui");
ScreenGui.Name = "TPBehindGui";
ScreenGui.ResetOnSpawn = false;
ScreenGui.Parent = LocalPlayer:WaitForChild("PlayerGui");
local Window = Instance.new("Frame");
Window.Name = "Window";
Window.Size = UDim2.fromOffset(320, 360);
Window.Position = UDim2.fromOffset(20, 110);
Window.BackgroundColor3 = Color3.fromRGB(28, 28, 30);
Window.BorderSizePixel = 0;
Window.Parent = ScreenGui;
Instance.new("UICorner", Window).CornerRadius = UDim.new(0, 8);
local Header = Instance.new("Frame", Window);
Header.Size = UDim2.new(1, 0, 0, 36);
Header.BackgroundTransparency = 1;
local Title = Instance.new("TextLabel", Header);
Title.Size = UDim2.new(0.7, -12, 1, 0);
Title.Position = UDim2.fromOffset(12, 0);
Title.BackgroundTransparency = 1;
Title.Font = Enum.Font.GothamBold;
Title.TextSize = 16;
Title.TextColor3 = Color3.fromRGB(235, 235, 235);
Title.TextXAlignment = Enum.TextXAlignment.Left;
Title.Text = "TP Behind";
local MinBtn = Instance.new("TextButton", Header);
MinBtn.Size = UDim2.fromOffset(28, 28);
MinBtn.Position = UDim2.new(1, -36, 0, 4);
MinBtn.Text = "-";
MinBtn.Font = Enum.Font.Gotham;
MinBtn.TextSize = 18;
MinBtn.BackgroundColor3 = Color3.fromRGB(40, 40, 40);
MinBtn.TextColor3 = Color3.fromRGB(230, 230, 230);
Instance.new("UICorner", MinBtn).CornerRadius = UDim.new(0, 6);
local ToggleBtn = Instance.new("TextButton", Header);
ToggleBtn.Size = UDim2.fromOffset(88, 28);
ToggleBtn.Position = UDim2.new(1, -132, 0, 4);
ToggleBtn.Text = "Start (G)";
ToggleBtn.Font = Enum.Font.GothamBold;
ToggleBtn.TextSize = 14;
ToggleBtn.BackgroundColor3 = Color3.fromRGB(72, 160, 80);
ToggleBtn.TextColor3 = Color3.fromRGB(255, 255, 255);
Instance.new("UICorner", ToggleBtn).CornerRadius = UDim.new(0, 6);
local targetLabel = Instance.new("TextLabel", Window);
targetLabel.Size = UDim2.new(1, -20, 0, 22);
targetLabel.Position = UDim2.fromOffset(10, 44);
targetLabel.BackgroundTransparency = 1;
targetLabel.Font = Enum.Font.Gotham;
targetLabel.TextSize = 14;
targetLabel.TextXAlignment = Enum.TextXAlignment.Left;
targetLabel.TextColor3 = Color3.fromRGB(200, 200, 200);
targetLabel.Text = "Target: (none)";
local LeftColumn = Instance.new("Frame", Window);
LeftColumn.Size = UDim2.new(1, -20, 1, -86);
LeftColumn.Position = UDim2.fromOffset(10, 70);
LeftColumn.BackgroundTransparency = 1;
local ListFrame = Instance.new("ScrollingFrame", LeftColumn);
ListFrame.Name = "PlayerList";
ListFrame.Size = UDim2.new(1, 0, 1, 0);
ListFrame.Position = UDim2.fromOffset(0, 0);
ListFrame.CanvasSize = UDim2.new(0, 0, 0, 0);
ListFrame.ScrollBarThickness = 6;
ListFrame.BorderSizePixel = 0;
ListFrame.BackgroundTransparency = 0.12;
ListFrame.BackgroundColor3 = Color3.fromRGB(18, 18, 18);
Instance.new("UICorner", ListFrame).CornerRadius = UDim.new(0, 6);
local UIListLayout = Instance.new("UIListLayout", ListFrame);
UIListLayout.Padding = UDim.new(0, 6);
UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder;
local minimized = false;
MinBtn.MouseButton1Click:Connect(function()
	minimized = not minimized;
	if minimized then
		Window.Size = UDim2.fromOffset(220, 46);
		ListFrame.Visible = false;
		targetLabel.Visible = false;
		MinBtn.Text = "+";
	else
		Window.Size = UDim2.fromOffset(320, 360);
		ListFrame.Visible = true;
		targetLabel.Visible = true;
		MinBtn.Text = "-";
	end
end);
local dragging, dragStartPos, windowStartPos = false, nil, nil;
Header.InputBegan:Connect(function(input)
	if (input.UserInputType == Enum.UserInputType.MouseButton1) then
		dragging = true;
		dragStartPos = input.Position;
		windowStartPos = Window.Position;
	end
end);
Header.InputEnded:Connect(function(input)
	if (input.UserInputType == Enum.UserInputType.MouseButton1) then
		dragging = false;
	end
end);
UserInputService.InputChanged:Connect(function(input)
	if (dragging and (input.UserInputType == Enum.UserInputType.MouseMovement)) then
		local delta = input.Position - dragStartPos;
		Window.Position = UDim2.new(windowStartPos.X.Scale, windowStartPos.X.Offset + delta.X, windowStartPos.Y.Scale, windowStartPos.Y.Offset + delta.Y);
	end
end);
local buttons = {};
local function highlightSelectedButton()
	for userId, rec in pairs(buttons) do
		if (targetPlayer and (tonumber(userId) == targetPlayer.UserId)) then
			rec.btn.BackgroundColor3 = Color3.fromRGB(60, 60, 60);
		else
			rec.btn.BackgroundColor3 = Color3.fromRGB(34, 34, 34);
		end
	end
end
local function setTarget(p)
	targetPlayer = p;
	if p then
		targetLabel.Text = ("Target: %s"):format(p.DisplayName or p.Name);
	else
		targetLabel.Text = "Target: (none)";
	end
	highlightSelectedButton();
end
local function createButtonFor(p)
	local b = Instance.new("TextButton");
	b.Name = "Btn_" .. tostring(p.UserId);
	b.Size = UDim2.new(1, -12, 0, 34);
	b.BackgroundColor3 = Color3.fromRGB(34, 34, 34);
	b.TextColor3 = Color3.fromRGB(236, 236, 236);
	b.Font = Enum.Font.Gotham;
	b.TextSize = 14;
	b.AutoButtonColor = true;
	b.Parent = ListFrame;
	Instance.new("UICorner", b).CornerRadius = UDim.new(0, 6);
	local conn = b.MouseButton1Click:Connect(function()
		setTarget(p);
	end);
	buttons[tostring(p.UserId)] = {btn=b,conn=conn};
	return b;
end
local function getSortedOthers()
	local out = {};
	for _, p in ipairs(Players:GetPlayers()) do
		if (p ~= LocalPlayer) then
			table.insert(out, {p=p,d=distanceToPlayer(p)});
		end
	end
	table.sort(out, function(a, b)
		if (a.d == b.d) then
			return (a.p.DisplayName or a.p.Name) < (b.p.DisplayName or b.p.Name);
		end
		return a.d < b.d;
	end);
	return out;
end
local function updatePlayerButtons()
	local sorted = getSortedOthers();
	local seen = {};
	for i, item in ipairs(sorted) do
		local p = item.p;
		local key = tostring(p.UserId);
		seen[key] = true;
		local brec = buttons[key];
		if not brec then
			local newBtn = createButtonFor(p);
			buttons[key].btn = newBtn;
			brec = buttons[key];
		end
		if (brec and brec.btn) then
			local txt = string.format("%s - %.1f studs", p.DisplayName or p.Name, item.d);
			brec.btn.Text = txt;
			brec.btn.LayoutOrder = i;
		end
	end
	for userId, rec in pairs(buttons) do
		local found = seen[userId];
		if not found then
			if rec.conn then
				rec.conn:Disconnect();
			end
			if rec.btn then
				rec.btn:Destroy();
			end
			buttons[userId] = nil;
		end
	end
	local itemHeight = 34;
	local padding = UIListLayout.Padding.Offset;
	local count = #sorted;
	ListFrame.CanvasSize = UDim2.new(0, 0, 0, (count * (itemHeight + padding)) + 8);
	if (not targetPlayer and (#sorted > 0)) then
		setTarget(sorted[1].p);
	end
	highlightSelectedButton();
end
Players.PlayerAdded:Connect(function(p)
	updatePlayerButtons();
end);
Players.PlayerRemoving:Connect(function(p)
	local key = tostring(p.UserId);
	if (targetPlayer == p) then
		setTarget(nil);
		running = false;
		ToggleBtn.BackgroundColor3 = Color3.fromRGB(72, 160, 80);
		ToggleBtn.Text = "Start (G)";
		restoreCameraState();
	end
	updatePlayerButtons();
end);
UserInputService.InputBegan:Connect(function(input, gameProcessed)
	if gameProcessed then
		return;
	end
	if (input.KeyCode == TOGGLE_KEY) then
		running = not running;
		if running then
			saveCameraState();
			ToggleBtn.BackgroundColor3 = Color3.fromRGB(180, 60, 60);
			ToggleBtn.Text = "Stop (G)";
		else
			ToggleBtn.BackgroundColor3 = Color3.fromRGB(72, 160, 80);
			ToggleBtn.Text = "Start (G)";
			restoreCameraState();
		end
	end
end);
ToggleBtn.MouseButton1Click:Connect(function()
	running = not running;
	if running then
		saveCameraState();
		ToggleBtn.BackgroundColor3 = Color3.fromRGB(180, 60, 60);
		ToggleBtn.Text = "Stop (G)";
	else
		ToggleBtn.BackgroundColor3 = Color3.fromRGB(72, 160, 80);
		ToggleBtn.Text = "Start (G)";
		restoreCameraState();
	end
end);
UserInputService.InputChanged:Connect(function(input)
	if (input.UserInputType == Enum.UserInputType.MouseMovement) then
		lastMouseMove = tick();
	end
end);
local function computeIdealBehind(targetHRP)
	local look = targetHRP.CFrame.LookVector;
	local behindPos = targetHRP.Position - (look * BEHIND_STUDS);
	behindPos = behindPos + Vector3.new(0, HEIGHT_OFFSET, 0);
	local frontPoint = behindPos + look;
	return CFrame.new(behindPos, frontPoint);
end
local function tweenPlaceBehind(myHRP, targetHRP)
	if not (myHRP and targetHRP) then
		return;
	end
	local desiredCf = computeIdealBehind(targetHRP);
	if activeTween then
		pcall(function()
			activeTween:Cancel();
		end);
		activeTween = nil;
	end
	local duration = math.max(0.03, UPDATE_INTERVAL * TWEEN_DURATION_FACTOR);
	local info = TweenInfo.new(duration, TWEEN_EASING_STYLE, TWEEN_EASING_DIR, 0, false, 0);
	local success, tween = pcall(function()
		return TweenService:Create(myHRP, info, {CFrame=desiredCf});
	end);
	if (success and tween) then
		activeTween = tween;
		pcall(function()
			tween:Play();
		end);
		tween.Completed:Connect(function()
			if (activeTween == tween) then
				activeTween = nil;
			end
		end);
	end
end
local cam = Workspace.CurrentCamera;
RunService:BindToRenderStep("TPBehindCameraAssist", Enum.RenderPriority.Camera.Value + 1, function(dt)
	cam = Workspace.CurrentCamera;
	if not running then
		return;
	end
	if not targetPlayer then
		return;
	end
	local tChar = getCharacter(targetPlayer);
	local tHRP = getHRP(tChar);
	if not (tChar and tHRP and getHumanoid(tChar) and (getHumanoid(tChar).Health > 0)) then
		return;
	end
	local desiredHrpCf = computeIdealBehind(tHRP);
	local look = tHRP.CFrame.LookVector;
	local camOffset = -(look * 2.2) + Vector3.new(0, 1.6, 0);
	local camPos = desiredHrpCf.Position + camOffset;
	local lookAt = tHRP.Position + Vector3.new(0, 1.2, 0);
	local desiredCamCf = CFrame.new(camPos, lookAt);
	local timeSinceMouse = tick() - lastMouseMove;
	local assist = ((timeSinceMouse > MOUSE_OVERRIDE_WINDOW) and CAMERA_ASSIST) or CAMERA_MIN_ASSIST;
	local alpha = smoothAlpha(CAMERA_ASSIST_SCALE * assist, dt);
	pcall(function()
		cam.CameraType = Enum.CameraType.Custom;
		cam.CFrame = cam.CFrame:Lerp(desiredCamCf, alpha);
	end);
end);
RunService.Heartbeat:Connect(function(dt)
	lastListTick = lastListTick + dt;
	if (lastListTick >= LIST_REFRESH) then
		lastListTick = 0;
		updatePlayerButtons();
		if targetPlayer then
			local tChar = getCharacter(targetPlayer);
			local tHRP = getHRP(tChar);
			local tHum = getHumanoid(tChar);
			if not (tChar and tHRP and tHum and (tHum.Health > 0)) then
				running = false;
				ToggleBtn.BackgroundColor3 = Color3.fromRGB(72, 160, 80);
				ToggleBtn.Text = "Start (G)";
				restoreCameraState();
			end
		end
	end
	if not running then
		return;
	end
	if not targetPlayer then
		return;
	end
	local myChar = getCharacter(LocalPlayer);
	if not myChar then
		return;
	end
	local myHRP = getHRP(myChar);
	local myHum = getHumanoid(myChar);
	if (not (myHRP and myHum) or (myHum.Health <= 0)) then
		return;
	end
	local tChar = getCharacter(targetPlayer);
	local tHRP = getHRP(tChar);
	local tHum = getHumanoid(tChar);
	if not (tChar and tHRP and tHum and (tHum.Health > 0)) then
		running = false;
		ToggleBtn.BackgroundColor3 = Color3.fromRGB(72, 160, 80);
		ToggleBtn.Text = "Start (G)";
		restoreCameraState();
		return;
	end
	lastTpTick = lastTpTick + dt;
	if (lastTpTick >= UPDATE_INTERVAL) then
		lastTpTick = 0;
		pcall(function()
			myHum.PlatformStand = false;
			tweenPlaceBehind(myHRP, tHRP);
		end);
	end
end);
updatePlayerButtons();
ToggleBtn.BackgroundColor3 = Color3.fromRGB(72, 160, 80);
ToggleBtn.Text = "Start (G)";

Features & Information

Auto-Follow – keeps you fixed directly behind the target player Smooth Tweening – natural movement, no snapping Dynamic Distance – constantly updates spacing to stay consistent Back Lock – always faces the target’s back automatically Sorted Player List – nearest to furthest, updates in real time GUI Controls – draggable, minimizable interface Real-Time Tracking – stays locked even if the target runs or teleports Toggle On/Off – quick control to enable or disable at will
#TP#teleport#follow#tween

Community Comments (2)

hailongcodinghailongcodingDec 12, 2025

Cool W script

tung_tung_scriptstung_tung_scriptsJun 20, 2026

L script

More Scripts for Universal Scripts