How do I rotate the screen after a teleport?

Hello! So, getting to the point, my goal is that I’d like to make a teleporter mechanism. It works like this: The player touches a part, and they get teleported to the location of another part (or, above it a little.) Including this, I’d like it to rotate the player by 180 degrees, so that, even if they’re in first person or in shiftlock, they will be facing the opposite direction when they’re teleported.

The issue is, I’ve got the teleporting part down, but I can’t for the love of me figure out how to rotate my player. When I’m not in first person or in shiftlock, my script works fine, and rotates the player. But how do I rotate the screen/first person too?

I’ve looked at various tutorials and solutions, but I can’t seem to follow a single one, or none of them work.

This is my script.

local location = script.Parent.Location
local teleporter = script.Parent.Teleporter

teleporter.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("HumanoidRootPart") then

		hit.Parent.HumanoidRootPart.CFrame = location.CFrame + Vector3.new(0,3,0)
		hit.Parent.HumanoidRootPart.Rotation = Vector3.new(0,0,0)

	end
end)

Could anyone help me figure this out?

1 Like

Could you try sending a remote event to the client, and on the client rotate their workspace.CurrentCamera using TweenService? If thats the “screen” you’re talking about?

How would I do that? I’m not vary familiar with TweenService

you have to adjust the Camera.CFrame along with the player’s HumanoidRootPart.CFrame.

try using this one & test it out.

local location = script.Parent.Location
local teleporter = script.Parent.Teleporter

teleporter.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("HumanoidRootPart") then
		local character = hit.Parent
		local humanoidRootPart = character.HumanoidRootPart
		
		-- Teleport the player and move them above the target location
		humanoidRootPart.CFrame = location.CFrame + Vector3.new(0, 3, 0)

		-- Rotate the player's HumanoidRootPart by 180 degrees (about the Y-axis)
		humanoidRootPart.CFrame = humanoidRootPart.CFrame * CFrame.Angles(0, math.rad(180), 0)

		-- Also rotate the camera by 180 degrees so it follows the new direction
		local camera = game.Workspace.CurrentCamera
		camera.CFrame = CFrame.new(camera.CFrame.Position, camera.CFrame.Position + camera.CFrame.LookVector * -1)
	end
end)

so basically you have to implement these things:

  1. Teleport the player: The player is teleported to the new location (with a little offset on the Y-axis).
  2. Rotate the character: The player’s HumanoidRootPart is rotated by 180 degrees around the Y-axis using CFrame.Angles(0, math.rad(180), 0).
  3. Rotate the camera: To rotate the camera as well (in first-person or shiftlock), we adjust the Camera.CFrame. This ensures the camera’s direction matches the new rotation of the player. We use camera.CFrame.Position to get the camera’s current position and then adjust the LookVector so it faces the opposite direction after teleporting.

This should work for both normal, first-person, and shiftlock camera modes.

1 Like

for some reason, this doesn’t seem to work, either.
i’ve tried with no shiftlock or first person, i’ve tried with it. it doesn’t work. let me provide a video through google drive.

check the camera type and then adjust it based on whether the player is in first-person or shiftlock. In these modes, the camera has more control over the rotation, so you need to manually update it after teleportation. By checking the CameraType and ensuring the camera’s direction matches the player’s facing direction, you can fix the issue.

try this one:

local location = script.Parent.Location
local teleporter = script.Parent.Teleporter

teleporter.Touched:Connect(function(hit)
    if hit.Parent:FindFirstChild("HumanoidRootPart") then
        local character = hit.Parent
        local humanoidRootPart = character.HumanoidRootPart
        
        -- Teleport the player and move them above the target location
        humanoidRootPart.CFrame = location.CFrame + Vector3.new(0, 3, 0)

        -- Rotate the player's HumanoidRootPart by 180 degrees (about the Y-axis)
        humanoidRootPart.CFrame = humanoidRootPart.CFrame * CFrame.Angles(0, math.rad(180), 0)

        -- Rotate the camera by 180 degrees
        local camera = game.Workspace.CurrentCamera

        -- If the player is in first-person or shiftlock, adjust the camera rotation
        if camera.CameraType == Enum.CameraType.Scriptable then
            -- Adjust the camera CFrame to rotate it by 180 degrees
            camera.CFrame = CFrame.new(camera.CFrame.Position, camera.CFrame.Position + camera.CFrame.LookVector * -1)
        else
            -- Otherwise, manually rotate the camera by 180 degrees
            camera.CFrame = CFrame.new(camera.CFrame.Position, camera.CFrame.Position + (camera.CFrame.LookVector * -1))
        end
    end
end)

This should properly rotate both the player and the camera after teleportation, regardless of whether they are in first-person or shiftlock mode.

I noticed something strange. This script doesn’t work, but its peculiar what does.
When I change
humanoidRootPart.CFrame = humanoidRootPart.CFrame * CFrame.Angles(0, math.rad(180), 0)
to
humanoidRootPart.CFrame = humanoidRootPart.CFrame * CFrame.Angles(0, math.rad(0), 0)
it rotates the player the opposite direction, despite it being at 0.

The camera rotation isn’t working at all, though. And I can’t seem to figure out why.

note these things:

  • humanoidRootPart.CFrame * CFrame.Angles(...) rotates the current orientation, so it builds off the existing direction. If you’re already facing 180°, applying another 180° brings you back to 0°.
  • Camera rotation doesn’t work unless you set the CameraType to Scriptable, take control, then restore it.
  • When in first-person or shiftlock, the camera is tightly bound to the character and won’t let you force a rotation without switching the mode temporarily.

updated script:

  • now we are taking over the camera just briefly to manually adjust its direction. then we re-align the camera to face the same way the player is facing. after a moment, we return camera control back to Roblox (so first-person/shiftlock keeps working normally).
local location = script.Parent.Location
local teleporter = script.Parent.Teleporter

teleporter.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("HumanoidRootPart") then
		local character = hit.Parent
		local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
		local player = game.Players:GetPlayerFromCharacter(character)
		local camera = workspace.CurrentCamera

		-- Teleport the player above the target location
		humanoidRootPart.CFrame = location.CFrame + Vector3.new(0, 3, 0)

		-- Rotate the player 180 degrees
		humanoidRootPart.CFrame = humanoidRootPart.CFrame * CFrame.Angles(0, math.rad(180), 0)

		-- Force the camera to match the new direction
		if player then
			local originalType = camera.CameraType
			camera.CameraType = Enum.CameraType.Scriptable

			-- Move camera to the player's head, facing the new direction
			local head = character:FindFirstChild("Head")
			if head then
				camera.CFrame = CFrame.new(head.Position, head.Position + humanoidRootPart.CFrame.LookVector)
			end

			-- Give Roblox a short moment before resetting camera control
			task.delay(0.1, function()
				camera.CameraType = originalType
			end)
		end
	end
end)

Agh, why is roblox being so stubborn? I truly apologize at this point for the troubles this script is causing us, but despite changing the CameraType to Scriptable and using your script, all seems to work BUT the camera! What’s going on? Is there anything I’ve fucked up? Is there information I’m lacking? I’ll provide another video explaining my problem. Again, I’m so sorry for the trouble.

Possibly these are the things you are encountering:

  1. CameraType issues:
    Even if you set CameraType = Scriptable, Roblox might instantly override it if you’re in first-person or if the player is actively moving. You have to “fight” the default camera controller a bit.
  2. LocalScript matters:
    Any changes to the CurrentCamera must be done in a LocalScript, not a ServerScript. Otherwise, the camera will ignore it or revert.
  3. Timings can be weird:
    Sometimes the camera changes too early, before Roblox has finished repositioning the character. We can try adding a tiny delay (task.wait()) to give Roblox time to catch up before we modify the camera.

Let’s try this LocalScript-based approach:

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

local teleporter = workspace:WaitForChild("Teleporter")
local location = teleporter:WaitForChild("Location")

teleporter.Touched:Connect(function(hit)
	local character = player.Character
	if not character or not character:FindFirstChild("HumanoidRootPart") then return end
	
	if hit:IsDescendantOf(character) then
		local hrp = character.HumanoidRootPart

		-- Move player above destination
		hrp.CFrame = location.CFrame + Vector3.new(0, 3, 0)

		-- Rotate character 180 degrees
		hrp.CFrame = hrp.CFrame * CFrame.Angles(0, math.rad(180), 0)

		-- Small delay before changing camera to avoid getting overridden
		task.wait(0.1)

		-- Force camera mode and direction
		camera.CameraType = Enum.CameraType.Scriptable
		camera.CFrame = CFrame.new(camera.CFrame.Position, camera.CFrame.Position - camera.CFrame.LookVector)

		-- Optional: switch back to follow mode so the player regains control
		task.wait(0.2)
		camera.CameraType = Enum.CameraType.Custom
	end
end)

Oh, sorry for the question, where am I meant to put this localscript exactly?

I’m still not sure but, however and wherever I put this script, NOTHING works for it. The teleporting doesn’t work, the camera stays the same. UNLESS, IVE PUT IT IN THE WRONG PLACE FORGIVE ME :sob: but it does not work generally :frowning:

you should place the LocalScript inside StarterPlayerScripts or StarterCharacterScripts.

agh… Still, the script isn’t doing anything at all. No matter what i do.

Hello! to do this, you’ll have to set the camera mode to “Scriptable”,
then rotate your camera based on the position of the humanoidRootPart,
and make sure that your camera always follow the player’s character.

But first, you’ll have to create a remote event to communicate between the server-side and the client-side and use it in your server script that handles when the “teleporter” part is touched. Something like this :

-- ...
local remoteEvent = game.ReplicatedStorage.RemoteEvent -- the remote event path
teleporter.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("HumanoidRootPart") then
        local plrWhoTouched = game.Players:GetPlayerFromCharacter(hit.Parent) -- get the player who touched the part
		remoteEvent:FireClient(plrWhoTouched)
	end
end)

Then, in a localscript :

local remoteEvent = game.ReplicatedStorage.RemoteEvent -- the same remote event path

local plr = game.Players.LocalPlayer
local character = plr.Character
local humanoidRootPart = character.PrimaryPart

local camera = workspace.CurrentCamera
local offset = Vector3.new(0, 0, -10) -- the distance between the camera and the character

local connection

remoteEvent.OnClientEvent:Connect(function()
   camera.CameraType = Enum.CameraType.Scriptable

   connection  = Game:GetService("RunService").RenderStepped:Connect(function() -- this function rotate the camera and make it always follow the character
      camera.CFrame = CFrame.new(humanoidRootPart.Position + offset, humanoidRootPart.Position) * CFrame.Angles(0, 0, math.rad(180))
   end)
end)

-- to disable the rotation effect, you can use "connection:Disconnect()" and set the camera mode back to "Custom"

If this code isn’t in a localscript it won’t work as you can access to the camera only on the client side. You can put this localscript in StarterPlayer.StarterCharacterScripts.

If you want to disable the rotation effect, you’ll need to create another remote event that will disconnect the “RenderStepped” function as explained above.

So, you’re saying I should have a Server Script seperate there, and connect a seperate LocalScript to it to handle the camera thing?

would i have to replace remoteEvent = game.ReplicatedStorage.RemoteEvent here with the pathway to the serverscript?
and how would the serverscript look? is there anything i have to add to the localscript you showed there?

Sorry for my confusion, this script is well over my knowledge of how things work :frowning:

No problem! Well, i’m saying that your current server script, this one :

local location = script.Parent.Location
local teleporter = script.Parent.Teleporter

teleporter.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("HumanoidRootPart") then

		hit.Parent.HumanoidRootPart.CFrame = location.CFrame + Vector3.new(0,3,0)
		hit.Parent.HumanoidRootPart.Rotation = Vector3.new(0,0,0)

	end
end)

you can replace it by this one :

local location = script.Parent.Location
local teleporter = script.Parent.Teleporter
local remoteEvent = game.ReplicatedStorage.RemoteEvent -- !! In this line : replace "game.ReplicatedStorage.RemoteEvent" with the location of the remote event, example : game.ReplicatedStorage.RemoteEvent. In this example, the remote event is located in ReplicatedStorage.

teleporter.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("HumanoidRootPart") then
        local plrWhoTouched = game.Players:GetPlayerFromCharacter(hit.Parent) -- get the player who touched the part
		remoteEvent:FireClient(plrWhoTouched) -- this line sends a code execution request to the client-side
	end
end)

In this code, replace “game.ReplicatedStorage.RemoteEvent” with the location of the remote event, example : game.ReplicatedStorage.RemoteEvent.

Then, in a localscript (you can place it in “StarterPlayer.StarterCharacterScripts”), paste this code :

local remoteEvent = game.ReplicatedStorage.RemoteEvent -- replace "game.ReplicatedStorage.RemoteEvent" with the same path you used in your server-side script

local plr = game.Players.LocalPlayer
local character = plr.Character
local humanoidRootPart = character.PrimaryPart

local camera = workspace.CurrentCamera
local offset = Vector3.new(0, 0, -10) -- the distance between the camera and the character

local connection

remoteEvent.OnClientEvent:Connect(function() -- when the server-side code execution request is called, it executes the code below: 
   camera.CameraType = Enum.CameraType.Scriptable

   connection  = Game:GetService("RunService").RenderStepped:Connect(function() -- this function rotate the camera and make it always follow the character
      camera.CFrame = CFrame.new(humanoidRootPart.Position + offset, humanoidRootPart.Position) * CFrame.Angles(0, 0, math.rad(180))
   end)
end)

-- to disable the rotation effect, you can use "connection:Disconnect()" and set the camera mode back to "Custom" with another remote event

This code makes your camera rotate while following your character. That works with the RenderStepped function, which executes the code every frame.

I hope this helps!

THIS SEEMS TO WORK IN TERMS OF ITS FUNCTION! We’ve got an issue, though. Despite the gruesome process this has been.

My camera is upside down, and frozen.
Your script does say that if i wanted to unfreeze the camera I could do so,
(-- And I don’t know how to do so. I’d desperately need help with that --)
but my camera is completely upside down.

–EDIT! UPDATE! i managed to fix the upside down thing myself, im just confused. how do i break the conenction exactly, and return the payer’s camera back to normal?

I just found out a way easier and with the camera not frozen !
Just replace the localscript with this :

local remoteEvent = game.ReplicatedStorage.remoteEvent -- the path of the remote event

local player = game.Players.LocalPlayer
local character = player.Character
local humanoid = character:FindFirstChild("Humanoid")

local camera = workspace.CurrentCamera

local connection
remoteEvent.OnClientEvent:Connect(function()
	connection = game:GetService("RunService").RenderStepped:Connect(function()
		camera.CFrame *= CFrame.Angles(0, 0, math.rad(180))
	end)
end)

this script does not seem to change my camera, it keeps it still entirely!! this is problem i had with the previous programmer who was so kind to help me out :frowning:

your previous idea of the frozen thing completely worked, i just need a way to unfreeze it after it is frozen, can you help me with that?