Made an ability that teleports another user, they disappear for a second and then come back immediately

So I am making an ability that during a certain step of it on the server side, it teleports another player that isn’t the user 5000 studs upwards for positioning reasons. Although it works when I manually make the victim myself, as in my own character, when I make it actually another user as intended they vanish, presumably to the correct position (although we have no proof it was the correct position just making it clear, so take it with a grain of salt), but then to appear back a frame later.

This is the part of my code that does the positioning, specifically at the end:

local function CreateObby(character:Model)
	local humanoid:Humanoid = character:FindFirstChild("Humanoid")
	local humanoidRootPart:BasePart = character:FindFirstChild("HumanoidRootPart")
	local obbyLevel = humanoid:GetAttribute("BloxxerGrabObbyLevel")
	local startPosition = Vector3.new(humanoidRootPart.Position.X,humanoidRootPart.Position.Y + 5000, humanoidRootPart.Position.Z)
	local nextPosition = CFrame.new(startPosition)
	local obbySelection = easyObbies:GetChildren()
	for i = 0, 3 + obbyLevel, 1 do
		local selectedObby:Model = obbySelection[math.random(1,#obbySelection)]:Clone()
		selectedObby:PivotTo(nextPosition)
		selectedObby.Parent = workspace
		nextPosition = selectedObby.ObbyEnd.CFrame
	end
	local newObbyComplete = obbyComplete:Clone()
	obbyComplete.CFrame = nextPosition
	newObbyComplete.Parent = workspace
	humanoidRootPart.CFrame = CFrame.new(startPosition + Vector3.new(0,5,0))
end

I have verified that the start position itself should be correct so its something else.

Here is a video of this happening:

assuming the code is client-sided, their position is being updated by the physics engine: if you anchor their root then they should stay put when you teleport them (anchoring stops the physics)

Anchoring the other player in the grab situation did not allow me to teleport them, although I thought it was a pretty solid idea nonetheless. Also just to note it is a server sided script, not a client sided one. If it was client sided you wouldn’t be able to manipulate other players positions at all.

unless you have a pocket dimension 5000 studs in the air or something, i dont see why you’d do that

why not just loop through every object in the victim’s character, search for parts and decals, and make them fully transparent? if you want them to come back then just save the transparency for when the attacker lets go or is interrupted?

but if you really need to put them 5000 studs up, make a RemoteEvent called “tp_up” or something, fire it to the victim’s client when the little white highlight thing appears, and in the victim’s client you can make their character tp 5000 studs up (preferrably using character:PivotTo() instead of changing the hrp position maybe?)

Assuming the code is client sided, You can’t move another players character on your client, this needs to be done on the server.

What is happening is your client moves the player to the intended position before the physics engine updates that character’s position back to their position.

I would recommend using some sort of method to allow the client to communicate to the server during the ability, either a RemoteEvent or RemoteFunction will work well assuming you do sanity checks on the server to prevent exploits.

PivotTo() would perform the exact same as moving the HumanoidRootPart as PivotTo() moves the entire model by the primary part which is the rootPart. Also the HumanoidRootWill infact move the rest of the rig if moved.

Also additionally, the moving of 5000 studs upwards is infact important, yes you could say I have a “pocket dimension” as you call it. It is placed extremely far away so its not visible to other players.

1 Like

I’m sorry but again, I have stated that the script shown is server sided. Please fully read my beginning post in full detail.

Sorry if it wasn’t made clear enough :sweat_smile:

1 Like

all of the possible causes I can think of

  • (most likely) you have some other script messing with their position
  • the client is updating their own position, like by hacking (would be solved by taking Network Ownership, like by anchoring)
    • client could also have code messing with their position
  • you have some funky shenanigans going on with StreamingEnabled

good test to see if it’s other code affecting the position is by running it in a new, empty place

also

you tots can, it’ll only be on your client though

Streaming just in general is off in my game. For the streaming enabled, I actually did test that setting the network owner to myself worked before making this post although I didn’t know how to set it back without breaking the tool again, because the second I made it set the network owner after the position happens back, it broke again.

I don’t have any code that I made myself that manipulates position though. Also anchoring them didn’t seem to work either.

I’m gonna open the scope of the full script now as its come to my attention that there might be other things goin on:

local TweenService = game:GetService("TweenService")
local ServerStorage = game:GetService("ServerStorage")
local WorldRoot = game:GetService("Workspace")
local tool = script.Parent
local event = tool:WaitForChild("bloxxerGrabEvent")
local hitbox = ServerStorage.Hitboxes.BloxxerGrabHitbox
local highlight = ServerStorage.Assets["Bloxxer Grab"].ArmGlow
local grabMotor = tool.GrabMotor

local allObbies = ServerStorage.Assets["Bloxxer Grab"].Obbies
local easyObbies = allObbies.Easy
--local mediumObbies = allObbies.Medium
--local hardObbies = allObbies.Hard
local obbyComplete = allObbies.ObbyComplete

local armGlowTweenInfo = TweenInfo.new(1,Enum.EasingStyle.Linear,Enum.EasingDirection.In,0,false,0)
local armGlowTweenInfo2 = TweenInfo.new(2.5,Enum.EasingStyle.Exponential,Enum.EasingDirection.InOut,0,false,0)

local function CreateObby(character:Model)
	local humanoid:Humanoid = character:FindFirstChild("Humanoid")
	local humanoidRootPart:BasePart = character:FindFirstChild("HumanoidRootPart")
	local obbyLevel = humanoid:GetAttribute("BloxxerGrabObbyLevel")
	local startPosition = CFrame.new(humanoidRootPart.Position.X,humanoidRootPart.Position.Y + 5000, humanoidRootPart.Position.Z)
	local nextPosition = startPosition
	local obbySelection = easyObbies:GetChildren()
	local obbyCollection = Instance.new("Model")
	obbyCollection.Name = "RandomlyGeneratedObby"
	obbyCollection.WorldPivot = startPosition
	obbyCollection.Parent = workspace
	for i = 1, 3 + obbyLevel, 1 do
		local selectedObby:Model = obbySelection[math.random(1,#obbySelection)]:Clone()
		selectedObby:PivotTo(nextPosition)
		selectedObby.Parent = obbyCollection
		nextPosition = selectedObby.ObbyEnd.CFrame
	end
	local newObbyComplete = obbyComplete:Clone()
	newObbyComplete.CFrame = nextPosition
	newObbyComplete.Parent = obbyCollection
	--humanoidRootPart:SetNetworkOwner(nil)
	humanoidRootPart.CFrame = CFrame.new(startPosition.Position + Vector3.new(0,3.5,0))
	--task.wait(1)
	--humanoidRootPart:SetNetworkOwner(game.Players:GetPlayerFromCharacter(character))
end

local function EventCalled(player:Player,eventType:string)
	local character = player.Character
	local humanoid = character:FindFirstChild("Humanoid")
	local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
	local rightArm = character:FindFirstChild("Right Arm")
	if eventType == "StartGrab" then
		humanoid.WalkSpeed = 2
		humanoid.JumpHeight = 0
	elseif eventType == "GrabCheck" then
		local newHitbox = hitbox:Clone()
		newHitbox.Parent = workspace
		newHitbox.CFrame = humanoidRootPart.CFrame + (humanoidRootPart.CFrame.LookVector * 3.5)
		local hitboxArray = WorldRoot:GetPartsInPart(newHitbox)
		for i, part in pairs(hitboxArray) do
			local enemyCharacter = part.Parent
			local enemyHumanoid = enemyCharacter:FindFirstChild("Humanoid")
			local enemyHumanoidRootPart = enemyCharacter:FindFirstChild("HumanoidRootPart")
			local enemyTorso = enemyCharacter:FindFirstChild("Torso")
			if enemyHumanoid and enemyHumanoid ~= humanoid then
				local newPosition = humanoidRootPart.CFrame.Position + (humanoidRootPart.CFrame.LookVector * 3)
				enemyHumanoid.AutoRotate = false
				humanoidRootPart.Anchored = true
				enemyHumanoid.PlatformStand = true
				event:FireClient(player)
				enemyHumanoidRootPart.CFrame = CFrame.new(newPosition,humanoidRootPart.CFrame.Position)
				grabMotor.Part0 = rightArm
				grabMotor.Part1 = enemyHumanoidRootPart
				grabMotor.C0 = rightArm.RightGripAttachment.CFrame
				grabMotor.C1 = enemyTorso.BodyFrontAttachment.CFrame * CFrame.Angles(-190.1,135.1,0):Inverse()
				local newArmGlow = highlight:Clone()
				local newGuyGlow = highlight:Clone()
				local armGlowTween = TweenService:Create(newArmGlow,armGlowTweenInfo,{FillTransparency = 0})
				local guyGlowTween = TweenService:Create(newGuyGlow,armGlowTweenInfo,{FillTransparency = 0})
				newArmGlow.Parent = rightArm
				newGuyGlow.Parent = enemyCharacter
				armGlowTween:Play()
				guyGlowTween:Play()
				armGlowTween.Completed:Once(function()
					newGuyGlow:Destroy()
					grabMotor:Destroy()
					grabMotor.Part0 = nil
					grabMotor.Part1 = nil
					enemyHumanoid.PlatformStand = false
					enemyHumanoid.AutoRotate = true
					if enemyHumanoid:GetAttribute("BloxxerGrabObbyLevel") == nil then
						enemyHumanoid:SetAttribute("BloxxerGrabObbyLevel",0)
					else
						enemyHumanoid:SetAttribute("BloxxerGrabObbyLevel",enemyHumanoid:GetAttribute("BloxxerGrabObbyLevel") + 1)
					end
					CreateObby(enemyCharacter)
				end)
				break
			end
		end
		newHitbox:Destroy()
	elseif eventType == "GrabComplete" then
		local newArmGlow = rightArm:FindFirstChild("ArmGlow")
		local armGlowTween = TweenService:Create(newArmGlow,armGlowTweenInfo2,{FillTransparency = 1})
		armGlowTween:Play()
		armGlowTween.Completed:Once(function()
			newArmGlow:Destroy()
		end)
	elseif eventType == "Finish" then
		humanoidRootPart.Anchored = false
		humanoid.WalkSpeed = 16
		humanoid.JumpHeight = 7.2
	end 
end

event.OnServerEvent:Connect(EventCalled)

If I remove the motor6d code in EventCalled it works although I don’t understand why It doesn’t work with it, since you can actively see I disable it.

Removing the animation does nothing. Same goes for tweens. Anchoring doesn’t work, and network ownership works although it is janky and for me to be able to reassign ownership I have to wait for some reason, which I don’t want to do.

I have been able to change other HRM Cframes in the past with other abilities, but I don’t understand what’s different here other than the Motor6D, which I presume is the culprit now, but I don’t know an effective way to fix that. Just to make it clear as well, the motor6d isn’t actually animated, it’s just there to connect the arm to the victims torso to make a good looking grab.

yeahh, unfortunate mix of motors, humanoid, and physics — those system are all pretty jank and get exponentially moreso when together. there really is no graceful or pretty solution.

It’s annoying but instead of Motor6Ds, you can try AlignPosition & AlignOrientation constraints with Rigidity enabled

If you wanted to try the network ownership again, you can turn on the network owners within the viewport’s setting (or, I think, Settings>Physics>Show Network Owners).

…sometimes the server just straight up rejects your attempts to set the network owners, and if that happens, you just keep setting it every heartbeat until it stays set afaik

Saying that is somewhat ironic (not saying you’re wrong) but motor6d’s are pretty much meant to be the welds, for humanoids. They pretty much are meant to work together especially in physics situations which is annoying me how it isn’t working right now.

I think its something to do with the fact that the motor6d is stopping the teleport despite the fact that it gets disabled beforehand? This issue is really annoying me.

Additionally, for some reason if i change the network owner, if i want to set it back I need to add a task.wait(1) before setting it back even if it gets set after the teleport. Why is this? Surely after the network owner gets set, it should just do the teleport and then fix itself? Why is a line that happens afterwards effect previous code.

I honestly hate how jank roblox is sometimes.

little thing you can try if you haven’t, when the player is grabbed, set their networkownership to the server then, not during CreateObby. they shouldn’t even have authority over themselves once grabbed.

then during CreateObby after everything is all said and done, set their ownership back to themselves.

code example:

local TweenService = game:GetService("TweenService")
local ServerStorage = game:GetService("ServerStorage")
local WorldRoot = game:GetService("Workspace")
local tool = script.Parent
local event = tool:WaitForChild("bloxxerGrabEvent")
local hitbox = ServerStorage.Hitboxes.BloxxerGrabHitbox
local highlight = ServerStorage.Assets["Bloxxer Grab"].ArmGlow
local grabMotor = tool.GrabMotor

local allObbies = ServerStorage.Assets["Bloxxer Grab"].Obbies
local easyObbies = allObbies.Easy
--local mediumObbies = allObbies.Medium
--local hardObbies = allObbies.Hard
local obbyComplete = allObbies.ObbyComplete

local armGlowTweenInfo = TweenInfo.new(1,Enum.EasingStyle.Linear,Enum.EasingDirection.In,0,false,0)
local armGlowTweenInfo2 = TweenInfo.new(2.5,Enum.EasingStyle.Exponential,Enum.EasingDirection.InOut,0,false,0)

local function CreateObby(character:Model)
	local humanoid:Humanoid = character:FindFirstChild("Humanoid")
	local humanoidRootPart:BasePart = character:FindFirstChild("HumanoidRootPart")
	local obbyLevel = humanoid:GetAttribute("BloxxerGrabObbyLevel")
	local startPosition = CFrame.new(humanoidRootPart.Position.X,humanoidRootPart.Position.Y + 5000, humanoidRootPart.Position.Z)
	local nextPosition = startPosition
	local obbySelection = easyObbies:GetChildren()
	local obbyCollection = Instance.new("Model")
	obbyCollection.Name = "RandomlyGeneratedObby"
	obbyCollection.WorldPivot = startPosition
	obbyCollection.Parent = workspace
	for i = 1, 3 + obbyLevel, 1 do
		local selectedObby:Model = obbySelection[math.random(1,#obbySelection)]:Clone()
		selectedObby:PivotTo(nextPosition)
		selectedObby.Parent = obbyCollection
		nextPosition = selectedObby.ObbyEnd.CFrame
	end
	local newObbyComplete = obbyComplete:Clone()
	newObbyComplete.CFrame = nextPosition
	newObbyComplete.Parent = obbyCollection
	humanoidRootPart.CFrame = CFrame.new(startPosition.Position + Vector3.new(0,3.5,0))
	humanoidRootPart:SetNetworkOwner(game.Players:GetPlayerFromCharacter(character)) -- set it here AFTER everthing is all said and done.
end

local function EventCalled(player:Player,eventType:string)
	local character = player.Character
	local humanoid = character:FindFirstChild("Humanoid")
	local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
	local rightArm = character:FindFirstChild("Right Arm")
	if eventType == "StartGrab" then
		humanoid.WalkSpeed = 2
		humanoid.JumpHeight = 0
	elseif eventType == "GrabCheck" then
		local newHitbox = hitbox:Clone()
		newHitbox.Parent = workspace
		newHitbox.CFrame = humanoidRootPart.CFrame + (humanoidRootPart.CFrame.LookVector * 3.5)
		local hitboxArray = WorldRoot:GetPartsInPart(newHitbox)
		for i, part in pairs(hitboxArray) do
			local enemyCharacter = part.Parent
			local enemyHumanoid = enemyCharacter:FindFirstChild("Humanoid")
			local enemyHumanoidRootPart = enemyCharacter:FindFirstChild("HumanoidRootPart")
			local enemyTorso = enemyCharacter:FindFirstChild("Torso")
			if enemyHumanoid and enemyHumanoid ~= humanoid then
                enemyHumanoidRootPart:SetNetworkOwner(nil) -- set it here, they aren't controlling themselves anymore at this point.
				local newPosition = humanoidRootPart.CFrame.Position + (humanoidRootPart.CFrame.LookVector * 3)
				enemyHumanoid.AutoRotate = false
				humanoidRootPart.Anchored = true
				enemyHumanoid.PlatformStand = true
				event:FireClient(player)
				enemyHumanoidRootPart.CFrame = CFrame.new(newPosition,humanoidRootPart.CFrame.Position)
				grabMotor.Part0 = rightArm
				grabMotor.Part1 = enemyHumanoidRootPart
				grabMotor.C0 = rightArm.RightGripAttachment.CFrame
				grabMotor.C1 = enemyTorso.BodyFrontAttachment.CFrame * CFrame.Angles(-190.1,135.1,0):Inverse()
				local newArmGlow = highlight:Clone()
				local newGuyGlow = highlight:Clone()
				local armGlowTween = TweenService:Create(newArmGlow,armGlowTweenInfo,{FillTransparency = 0})
				local guyGlowTween = TweenService:Create(newGuyGlow,armGlowTweenInfo,{FillTransparency = 0})
				newArmGlow.Parent = rightArm
				newGuyGlow.Parent = enemyCharacter
				armGlowTween:Play()
				guyGlowTween:Play()
				armGlowTween.Completed:Once(function()
					newGuyGlow:Destroy()
					grabMotor:Destroy()
					grabMotor.Part0 = nil
					grabMotor.Part1 = nil
					enemyHumanoid.PlatformStand = false
					enemyHumanoid.AutoRotate = true
					if enemyHumanoid:GetAttribute("BloxxerGrabObbyLevel") == nil then
						enemyHumanoid:SetAttribute("BloxxerGrabObbyLevel",0)
					else
						enemyHumanoid:SetAttribute("BloxxerGrabObbyLevel",enemyHumanoid:GetAttribute("BloxxerGrabObbyLevel") + 1)
					end
					CreateObby(enemyCharacter)
				end)
				break
			end
		end
		newHitbox:Destroy()
	elseif eventType == "GrabComplete" then
		local newArmGlow = rightArm:FindFirstChild("ArmGlow")
		local armGlowTween = TweenService:Create(newArmGlow,armGlowTweenInfo2,{FillTransparency = 1})
		armGlowTween:Play()
		armGlowTween.Completed:Once(function()
			newArmGlow:Destroy()
		end)
	elseif eventType == "Finish" then
		humanoidRootPart.Anchored = false
		humanoid.WalkSpeed = 16
		humanoid.JumpHeight = 7.2
	end 
end

event.OnServerEvent:Connect(EventCalled)

i commented on the lines i added.

1 Like

It seemed to work initially when i tested it on a rig (i am now realizing why)

But during team test / server and clients testing it didn’t seem to work on actual players.

It probably will always work on rigs due to them always being server or another players network ownerships.

In case it sparks any ideas, the grabs in any game I’ve made work by doing the following:

  • (on client) Having duplicate “fake” characters of every foreign client, that they can modify freely

    • (on client) Parent the foreign “real” characters to ReplicatedStorage, which still receive replication updates like health & position
  • On the server, take authority, attach grabbed players (Defenders) to the grabbing players (Attacks) via rigid AlignPosition & AlignOrientation

  • On the Attacker client, attach the Defender’s FakeCharacter root via WeldConstraint

annnd that works well enough for me. Of course that is significant foundational setup, & you’d need to deal with the fake characters position+animations — which is a lot … That being said:


!!!
I feel like you could completely skip the motor6d & the fake character.
If you’re really against Align constraints (they don’t combine assemblies) all you should hopefully have to do:

  • have the server use a WeldConstraint (theyre newer+behave a little better)
  • if necessary, have the clients manually position & weld the foreign clients.
    • Only stop the visuals on clients when they’re properly positioned

I’m not entirely sure but it happens very frequently on non-character objects as well, especially when newly instantiated or welded.

I think it’s something to do with physics not registering new assemblies until after a few frames, and clients sometimes not being able to take ownership (for a multitude of possible network+physics reasons)

view the following:

Hmmmm. I have tried just using an empty task.wait() before (which if you don’t know just waits a single frame) and even then it wasn’t happy with me setting back the network owner so “quickly”

Not trying to be difficult but I want to stray away from solutions that require large systems or a change that would modify pretty much everything. Especially since this would make 2 copies of a character which is client and server sided, with a bunch of positioning relating alignments, that would definitely mess up pretty much 75% of the abilities in the game to a severe degree.

Also to note, my game is a randomizer game, see games like “Item Asylum” and “Blood and Pixels” for reference, meaning every tool acts differently and does different stuff, and yes I do intend to add large systems and rework some that I have already, e.g. a damage handler system, ragdoll handler. But generally overall tools will be unique to each other, just using modular systems for cooldowns and things that come up extremely often.

I could hypothetically make the tools themselves completely modular, but i think they all are too different from each other categorize into tool classes, so I just make them use custom modules for things they do often in every tool as I said before.

this is the code I use

local function SetNetworkOwner(Target:BasePart)
	-- For some reason, it's possible that the network owner of the root gets set back to the player. The network owner is set multiple times to account for that.
	local goodSets = 0
	local startTick = tick()
	repeat
		if Target:CanSetNetworkOwnership() then
			goodSets += 1
			Target:SetNetworkOwner(nil)
			print("Set network owner in loop")
		else
			goodSets = 0
		end
		if not Target or not Target.Parent then
			break
		end
		RunService.Heartbeat:Wait()
	until goodSets > 8 or (tick() - startTick) > 2
	
end