Max range teleportation

Hey, so I’m trying to teleport a player to another player, but only if they do not surpass the maximum range, if they do, I want to teleport basically to the max range but in the direction where the opposing player is looking to.
Here is my current math-thingy, it’s quite broken though, any ideas?

				PlayerRoot.Position = (TargetHead.Position - PlayerHead.Position).Unit * CharTPRange + PlayerHead.Position

you can use math.max for that
math | Roblox Creator Documentation

like if it surpass the maximum number its gonna return the maximum number like to what you said

Looking at your code you seem to have the math wrong. Also, you should use PVInstance:PivotTo instead of Part.Position when repositioning the player.
Also, math.max returns the larger of the two numbers passed instead of restricting a value to a range. Instead you should use math.clamp. The first parameter is the value you want to restrict, the second the minimum value allowed, and the third the maximum value allowed. Then if the first value is less than the second it returns the second value and if it’s larger than the third value it returns the third value. If the first value is in-between the min and max values then it returns the first value.

So to do this I would do something like this instead:

local player = teleportPlayer -- the player we want to teleport
local target = targetPlayer -- the player we want to teleport to

local maxDistance = 200 -- the maximum distance from the Target the player can be

-- The function we use to teleport the players’ character to the targets’ character
local function teleport(playerCharacter, targetCharacter)
    local distance = (playerCharacter:GetPivot().Position - targetCharacter:GetPivot().Position).Magnitude -- the distance the player is from the target

    distance -= math.clamp(distance, 2, maxDistance) -- will be 0 if we are within the max distance. If we are less than 2 studs away it will default to two. Otherwise it will be the distance of how much further we are out of range.

    targetCharacter:PivotTo(targetCharacter: GetPivot() + targetCharacter:GetPivot().LookVector * distance) -- teleports the player distance studs in front of the target player.
end

-- teleport the player to the target player
teleport(player.Character, target.Character)

Edit: One thing I should mention! PVInstance | Roblox Creator Documentation will teleport the player to the new position regardless of whether or not something is at that location. This means they could get trapped inside a wall, fall through terrain, or something. If this behavior is undesired then you should use Model | Roblox Creator Documentation. It will teleport the player on top of whatever obstruction is in the way.

4 Likes

Thank you, sadly, this doesn’t work for me, it teleports to very weird places. Here is my code.

--Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local ServerStorage = game:GetService("ServerStorage")

--Folders
local Modules = ServerScriptService:WaitForChild("Modules")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")

--Remotes
local TeleportRemote = Remotes:WaitForChild("RequestTeleport")

--Modules
local Utils = require(Modules:WaitForChild("Utils"))
local CharacterAbilities = require(Modules:WaitForChild("CharacterAbilities"))

--On server event
TeleportRemote.OnServerEvent:Connect(function(plr, mouse)
	--Get player data
	local Data = Utils.GetData(plr)
	if not Data then return plr:Kick(Utils.Error("No data.")) end
	--Check player fighter
	local PlayerFighter = Data.Character.Value
	if PlayerFighter ~= "None" then
		--If player has a character selected, we get that characters teleport range
		local CharTPRange = CharacterAbilities[PlayerFighter].TPRange
		--Next, we declare some variables regarding Player's character
		local PlayerChar = plr.Character
		local PlayerRoot = PlayerChar.HumanoidRootPart
		local PlayerHead = PlayerChar.Head
		--Now, let's check if the player is currently locked onto somebody.
		local LockedValue = Data.lockedOnto.Value
		--If the player is locked, let's teleport to the locked on player.
		if LockedValue ~= "None" then
			--Let's get some info about the locked on player.
			local TargetPlayer = Players:FindFirstChild(LockedValue)
			local TargetChar = TargetPlayer.Character
			local TargetRoot = TargetChar.HumanoidRootPart
			local TargetHead = TargetChar.Head
			--Let's see if the location of the 2nd player does not exceed the maximum allowed Teleport Range.
			local MagnitudeLock = (PlayerHead.Position - TargetHead.Position).Magnitude
			if MagnitudeLock <= CharTPRange then
				--If everything is alright, we can proceed with the teleport. First, let's set the offset of the player.
				local OFFSET = 4
				--Before we completely teleport, let's check if the player has sufficient mana to teleport.
				local Mana = Data.Mana.Value
				local ManaCost = CharacterAbilities[PlayerFighter].TPManaCost
				if Mana >= ManaCost then
					--Let's deduct the mana.
					local ManaEquation = Mana - ManaCost
					Data.Mana.Value = ManaEquation
					--Now we teleport!
					PlayerRoot.CFrame = TargetRoot.CFrame * CFrame.new(0, 0, -OFFSET)
				end
				-- If the Teleport Range is not sufficient, we teleport to the max allowed range.
			elseif MagnitudeLock > CharTPRange then
				local distance = (PlayerChar:GetPivot().Position - TargetChar:GetPivot().Position).Magnitude -- the distance the player is from the target

				distance -= math.clamp(distance, 2, CharTPRange) -- will be 0 if we are within the max distance. If we are less than 2 studs away it will default to two. Otherwise it will be the distance of how much further we are out of range.

				TargetChar:PivotTo(TargetChar:GetPivot() + TargetChar:GetPivot().LookVector * distance)
			
			end
		end
	end
end)

Raycast, you could create a ray and teleport the player to the ray’s hitting location (but also make them teleport a bit above to prevent ground phasing).

And any idea why this doesnt work?
local distance = (PlayerChar:GetPivot().Position - TargetChar:GetPivot().Position).Magnitude – the distance the player is from the target

			distance -= math.clamp(distance, 2, CharTPRange) -- will be 0 if we are within the max distance. If we are less than 2 studs away it will default to two. Otherwise it will be the distance of how much further we are out of range.

			TargetChar:PivotTo(TargetChar:GetPivot() + TargetChar:GetPivot().LookVector * distance)

Why don’t you just use Magnitude like you first did, use an if statement with < and =, then proceed to create the ray to teleport to the limiting blocks?

Or, since it’s a teleport to target character, have it simply be a magnitude check.

Sorry about that. I tested my code and found a few issues, so here’s the working version:

local player = teleportPlayer -- the player we want to teleport
local target = targetPlayer -- the player we want to teleport to

local maxDistance = 200 -- the maximum distance from the Target the player can be

-- The function we use to teleport the players’ character to the targets’ character
local function teleport(playerCharacter, targetCharacter, offset)
	offset = offset or 4
	local tarFrame = targetCharacter:GetPivot() -- the CFrame of the targetCharacter
	
	local distance = (playerCharacter:GetPivot().Position - tarFrame.Position).Magnitude -- the distance the player is from the target
	if distance >= (maxDistance + offset) then
		distance -= math.clamp(distance, offset, maxDistance)
	else
		distance += offset - math.clamp(distance, offset, maxDistance)
	end
	
	playerCharacter:PivotTo(CFrame.lookAt((tarFrame + tarFrame.LookVector * distance).Position, tarFrame.Position)) -- teleports the player distance studs in front of the target player.
end

-- teleport the player to the target player
teleport(player.Character, target.Character)

Here’s what it might look like in your code:

--Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local ServerStorage = game:GetService("ServerStorage")

--Folders
local Modules = ServerScriptService:WaitForChild("Modules")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")

--Remotes
local TeleportRemote = Remotes:WaitForChild("RequestTeleport")

--Modules
local Utils = require(Modules:WaitForChild("Utils"))
local CharacterAbilities = require(Modules:WaitForChild("CharacterAbilities"))


-- The function we use to teleport the players’ character to the targets’ character
local function teleportToTarget(playerCharacter, targetCharacter, offset)
	offset = offset or 4
	local tarFrame = targetCharacter:GetPivot() -- the CFrame of the targetCharacter
	
	local distance = (playerCharacter:GetPivot().Position - tarFrame.Position).Magnitude -- the distance the player is from the target
	if distance >= (maxDistance + offset) then
		distance -= math.clamp(distance, offset, maxDistance)
	else
		distance += offset - math.clamp(distance, offset, maxDistance)
	end
	
	playerCharacter:PivotTo(CFrame.lookAt((tarFrame + tarFrame.LookVector * distance).Position, tarFrame.Position)) -- teleports the player distance studs in front of the target player.
end

--On server event
TeleportRemote.OnServerEvent:Connect(function(plr, mouse)
	--Get player data
	local Data = Utils.GetData(plr)
	if not Data then return plr:Kick(Utils.Error("No data.")) end
	--Check player fighter
	local PlayerFighter = Data.Character.Value
	if PlayerFighter ~= "None" then
		--If player has a character selected, we get that characters teleport range
		local CharTPRange = CharacterAbilities[PlayerFighter].TPRange
		--Next, we declare some variables regarding Player's character
		local PlayerChar = plr.Character
		local PlayerRoot = PlayerChar.HumanoidRootPart
		local PlayerHead = PlayerChar.Head
		--Now, let's check if the player is currently locked onto somebody.
		local LockedValue = Data.lockedOnto.Value
		--If the player is locked, let's teleport to the locked on player.
		if LockedValue ~= "None" then
			--Let's get some info about the locked on player.
			local TargetPlayer = Players:FindFirstChild(LockedValue)
			local TargetChar = TargetPlayer.Character

			--Before we completely teleport, let's check if the player has sufficient mana to teleport.
			
			local ManaCost = CharacterAbilities[PlayerFighter].TPManaCost
			if Data.Mana.Value >= ManaCost then
				Data.Mana.Value -= ManaCost --Let's deduct the mana.

				teleportToTarget(PlayerChar, TargetChar, 4) -- teleport with offset of 4
			end
		end
	end
end)
1 Like

Thank you so much, however this still does not seem to work for me, I get this error:

ServerScriptService.Player.TeleportHandler:25 Attempt to perform an arithmetic (add) on an Instance and number.

Here is my code:

--Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local ServerStorage = game:GetService("ServerStorage")

--Folders
local Modules = ServerScriptService:WaitForChild("Modules")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")

--Remotes
local TeleportRemote = Remotes:WaitForChild("RequestTeleport")

--Modules
local Utils = require(Modules:WaitForChild("Utils"))
local CharacterAbilities = require(Modules:WaitForChild("CharacterAbilities"))


-- The function we use to teleport the players’ character to the targets’ character
local function teleportToTarget(playerCharacter, targetCharacter, offset, maxDistance)
	offset = offset or 4
	local tarFrame = targetCharacter:GetPivot() -- the CFrame of the targetCharacter

	local distance = (playerCharacter:GetPivot().Position - tarFrame.Position).Magnitude -- the distance the player is from the target
	if distance >= (playerCharacter + offset) then
		distance -= math.clamp(distance, offset, maxDistance)
	else
		distance += offset - math.clamp(distance, offset, maxDistance)
	end

	playerCharacter:PivotTo(CFrame.lookAt((tarFrame + tarFrame.LookVector * distance).Position, tarFrame.Position)) -- teleports the player distance studs in front of the target player.
end

--On server event
TeleportRemote.OnServerEvent:Connect(function(plr, mouse)
	--Get player data
	local Data = Utils.GetData(plr)
	if not Data then return plr:Kick(Utils.Error("No data.")) end
	--Check player fighter
	local PlayerFighter = Data.Character.Value
	if PlayerFighter ~= "None" then
		--If player has a character selected, we get that characters teleport range
		local CharTPRange = CharacterAbilities[PlayerFighter].TPRange
		--Next, we declare some variables regarding Player's character
		local PlayerChar = plr.Character
		local PlayerRoot = PlayerChar.HumanoidRootPart
		local PlayerHead = PlayerChar.Head
		--Now, let's check if the player is currently locked onto somebody.
		local LockedValue = Data.lockedOnto.Value
		--If the player is locked, let's teleport to the locked on player.
		if LockedValue ~= "None" then
			--Let's get some info about the locked on player.
			local TargetPlayer = Players:FindFirstChild(LockedValue)
			local TargetChar = TargetPlayer.Character

			--Before we completely teleport, let's check if the player has sufficient mana to teleport.

			local ManaCost = CharacterAbilities[PlayerFighter].TPManaCost
			if Data.Mana.Value >= ManaCost then
				Data.Mana.Value -= ManaCost --Let's deduct the mana.

				teleportToTarget(PlayerChar, TargetChar, 4, CharTPRange) -- teleport with offset of 4
			end
		end
	end
end)

Looks likes you accidentally changed the code when adding the maxDistance parameter to the teleportToTarget function. You replaced the original maxDistance variable with the playerCharacter variable, so it’s trying to add the playerCharacter instance and the offset variable together. Just replace

with

Try using.magnitude that might help

Thank you, it actually did teleport me this time but sadly it teleported me under the map, any ideas?

--Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local ServerStorage = game:GetService("ServerStorage")

--Folders
local Modules = ServerScriptService:WaitForChild("Modules")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")

--Remotes
local TeleportRemote = Remotes:WaitForChild("RequestTeleport")

--Modules
local Utils = require(Modules:WaitForChild("Utils"))
local CharacterAbilities = require(Modules:WaitForChild("CharacterAbilities"))


-- The function we use to teleport the players’ character to the targets’ character
local function teleportToTarget(playerCharacter, targetCharacter, offset, maxDistance)
	offset = offset or 4
	local tarFrame = targetCharacter:GetPivot() -- the CFrame of the targetCharacter

	local distance = (playerCharacter:GetPivot().Position - tarFrame.Position).Magnitude -- the distance the player is from the target
	if distance >= (maxDistance + offset) then
		distance -= math.clamp(distance, offset, maxDistance)
	else
		distance += offset - math.clamp(distance, offset, maxDistance)
	end

	playerCharacter:PivotTo(CFrame.lookAt((tarFrame + tarFrame.LookVector * distance).Position, tarFrame.Position)) -- teleports the player distance studs in front of the target player.
end

--On server event
TeleportRemote.OnServerEvent:Connect(function(plr, mouse)
	--Get player data
	local Data = Utils.GetData(plr)
	if not Data then return plr:Kick(Utils.Error("No data.")) end
	--Check player fighter
	local PlayerFighter = Data.Character.Value
	if PlayerFighter ~= "None" then
		--If player has a character selected, we get that characters teleport range
		local CharTPRange = CharacterAbilities[PlayerFighter].TPRange
		--Next, we declare some variables regarding Player's character
		local PlayerChar = plr.Character
		local PlayerRoot = PlayerChar.HumanoidRootPart
		local PlayerHead = PlayerChar.Head
		--Now, let's check if the player is currently locked onto somebody.
		local LockedValue = Data.lockedOnto.Value
		--If the player is locked, let's teleport to the locked on player.
		if LockedValue ~= "None" then
			--Let's get some info about the locked on player.
			local TargetPlayer = Players:FindFirstChild(LockedValue)
			local TargetChar = TargetPlayer.Character

			--Before we completely teleport, let's check if the player has sufficient mana to teleport.

			local ManaCost = CharacterAbilities[PlayerFighter].TPManaCost
			if Data.Mana.Value >= ManaCost then
				Data.Mana.Value -= ManaCost --Let's deduct the mana.

				teleportToTarget(PlayerChar, TargetChar, 4, CharTPRange) -- teleport with offset of 4
			end
		end
	end
end)

Sorry for the late response.

You could try replacing

playerCharacter:MoveTo

with

playerCharacter:PivotTo

Or, if you wanted to, you could create some invisible bricks that collectively contain the map and then check to see if the teleport location is within one of them.
Here’s a function to check if a position is within a part:

local function isPosInPart(position, part) -- returns true if the position is in the part and false if it isn’t
	position = part:PointToObjectSpace(position)
	return (math.abs(position.X) <= part.Size.X and math.abs(position.Y) <= part.Size.Y and math.abs(position.Z) <= part.Size.Z)
end

Then you could loop through the invisible bricks to check and see if the teleport location is within one of them.

I think this is exactly what you want, paste it into a LocalScript inside of StarterCharacterScripts

local localPlayer = game:GetService('Players').LocalPlayer
local mouse = localPlayer:GetMouse()
local character = localPlayer.Character

local maxRange = 25

mouse.Button1Down:Connect(function()
	local otherCharacterCFrame = CFrame.new(Vector3.new(0,3,0))
	local otherCharacterPosition = otherCharacterCFrame.Position
	local direction = (otherCharacterPosition-character.PrimaryPart.Position)
	local newPosition = (direction.Magnitude <= maxRange and otherCharacterPosition) or otherCharacterCFrame.Position+otherCharacterCFrame.LookVector*maxRange
	
	character:SetPrimaryPartCFrame(CFrame.new(newPosition))
end)

Thank you, however it is not, since I need to make it ServerSided, due to the fact that I am making a cooldown and that I am deducting Mana from the player, both of which are ServerSided.

Having it local was just to show you how its done and so you can test it easily, not to make it work with your system. The calculations would still work on the server