Make player camera face where the character is facing?

i have a checkpoint script that spawns the player facing the part’s frame, it works fine but the camera is facing the other direction, the game is in first person so the character faces the camera too ;-; how would i fix it?

Use CFrame.lookAt() on the camera’s CFrame.

workspace.CurrentCamera.CFrame = CFrame.lookAt(DesiredPosition, FramePosition)

hmm it dosn’t seem to work…heres some pictures
Screenshot2021_08_12_072152

it faces the red arrow when I lockfirstperson

hmm i tried lock first person using script but it still faces where the red arrow faces

i have this checkpoint script, i tried dup the char line but didn’t work

local function NewCharacter(player, char)
	local TempCurrentStage = CurrentStage[player.UserId]
	if TempCurrentStage ~= nil then
		local TempCheckpoint = Checkpoints:FindFirstChild(TempCurrentStage)
		if TempCheckpoint ~= nil then
			repeat wait(0.1) until char.PrimaryPart ~= nil
	
			char:SetPrimaryPartCFrame(CFrame.new(TempCheckpoint.Position + Vector3.new(0, 3, 0)) * CFrame.Angles(0, math.rad(TempCheckpoint.Orientation.Y) + TempCheckpoint.Orientation.Z, 0))
			
		end
	end
end

This function in the code below that I made will reset the camera direction to wherever the local player’s character is facing. It must be in a LocalScript ran on the client. If a server script needs to be able to do it, you should use a RemoteEvent to trigger the function on the client.

local function setCameraFacingPlayerDirection()
	local player = game:GetService("Players").LocalPlayer
	local character = player.Character or player.CharacterAdded:Wait()
	local camera = workspace.CurrentCamera
	
	--Values used for the camera cframe calculations
	local playerCf = character:WaitForChild("Head").CFrame
	local lastDiff = playerCf.Position - camera.CFrame.Position
	local lastDiffY = lastDiff.Y * Vector3.new(0, -1, 0)
	if (lastDiff.Magnitude < 1) then
		return --prevent setting it if they are already in first person
	end
	local lastType = camera.CameraType
	local cameraPos = (playerCf + lastDiffY - (lastDiff.Magnitude * Vector3.new(1, 0, 1) * playerCf.LookVector)).Position
	
	--Set camera cframe manually and then give control back to camera script
	camera.CameraType = Enum.CameraType.Scriptable
	camera.CFrame = CFrame.lookAt(cameraPos, playerCf.Position)
	camera.CameraType = lastType
end

NOTE: The function above will not work correctly if they are already in first-person when they respawn! If your game has first person when the character spawns, you will need to first disable it for a moment as they are respawning via scripting because it will override the character’s direction and the code has no way of knowing where the player is supposed to be facing. An alternative solution to this is to instead directly make the camera face your part’s frame on spawn instead of the character (cutting out the middle man).

I hope this is somewhat helpful. I couldn’t test out the alternative solution I mentioned above right now since it’s late. If you are still having troubles and no one else gives a solution, I might be able to help more when I am back on and have more time.

1 Like

starter pack or starterplayer?, i tried putting this every where but it don’t work for some reason

i tried put a wait in front of the check point new char but it still faces the red arrow in first person

I created a new script that I tested (works for me) and correctly makes the player face a target part upon respawning when in locked first person.

Put this in a LocalScript that is inside StarterPlayer.StarterCharacterScripts:

--CHANGE THE VARIABLE BELOW TO WHATEVER PART THE PLAYER WILL FACE
local partToFaceOnSpawn = workspace:WaitForChild("TargetPart")

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

local function createModalButton()
	local button = Instance.new("TextButton")
	button.Text = ""
	button.BackgroundTransparency = 1
	button.Active = false
	button.Visible = true
	button.Modal = true
	local gui = Instance.new("ScreenGui")
	gui.Name = "CameraResetModal"
	button.Parent = gui
	gui.Parent = player:WaitForChild("PlayerGui")
	return gui
end

if player.CameraMode == Enum.CameraMode.LockFirstPerson then
	camera.Changed:Wait()
	local lastType = camera.CameraType
	local modal = createModalButton()
	camera.CameraType = Enum.CameraType.Scriptable
	
	local cameraPos = camera:GetRenderCFrame().Position
	local partPos = partToFaceOnSpawn.CFrame.Position
	local targetPos = partPos * Vector3.new(1, 0, 1) + Vector3.new(0, cameraPos.Y, 0)
	local newCf = CFrame.lookAt(cameraPos, targetPos)
	camera.CFrame = newCf
	
	camera.CameraType = lastType
	modal:Destroy()
end
1 Like

i’m sorry i didn’t tell you this before but uh… there is some checkpoints facing another direction so facing one won’t really work ;-;

You should be able to change the partToFaceOnSpawn variable in my code to reference whatever target part their current checkpoint is (dynamically), it doesn’t have to be the same part every time (that’s just what I put as an example).

I don’t know what the other code is in your game or how it works so I can’t supply a full working script. It depends on how your checkpoint system works for what that variable will be set to.

Here is a pseudocode example for how something dynamic like that could work:

--some code/function that gets the player's current checkpoint (would return a number value)
local currentLevel = getPlayerCheckpointLevel()

--find the part for this checkpoint the player is on
local partToFaceOnSpawn = workspace.Checkpoints:FindFirstChild(tostring(currentLevel))

hmm can i like just rotate the player again? like wait till the player spawns and rotate the cframe again

What do you mean by that? Can you give an example?

Are the checkpoints in your game just SpawnLocations, or are they a custom part (the part you are standing on in the screenshot)?

You could rotate the player instead to face the forward direction of the part they spawned on. I can supply code for this later today if you want.

the checkpoint is just a normal part, and the script is a server script, i tried rotate it after 0.1 second but it don’t work

Can you post a screenshot of how the checkpoints are set up in Workspace, as well as the server script code for the checkpoint system?

the script:

local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local ObbyDataStore = DataStoreService:GetDataStore("ObbyDataStore")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local SkipStage = ReplicatedStorage:WaitForChild("SkipStage")
local ResetStage = ReplicatedStorage:WaitForChild("ResetStage")

local Checkpoints = workspace:WaitForChild("Checkpoints")
local inGameStartupPlayers = {}
local CurrentStage = {}
local TouchDb = {}

local ProductId = 1193721691 -- Change to your developer product id

local function NewCharacter(player, char)
	local TempCurrentStage = CurrentStage[player.UserId]
	if TempCurrentStage ~= nil then
		local TempCheckpoint = Checkpoints:FindFirstChild(TempCurrentStage)
		if TempCheckpoint ~= nil then
			repeat wait(0.1) until char.PrimaryPart ~= nil
	wait(0.1)
			char:SetPrimaryPartCFrame(CFrame.new(TempCheckpoint.Position + Vector3.new(0, 3, 0)) * CFrame.Angles(0, math.rad(TempCheckpoint.Orientation.Y) + TempCheckpoint.Orientation.Z, 0))
			
		end
	end
end

local function NewPlayer(player)
	local success, stage = pcall(function()
		return (ObbyDataStore:GetAsync(player.UserId)) or 1
	end)

	CurrentStage[player.UserId] = stage

	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"
	local Stage = Instance.new("IntValue", leaderstats)
	Stage.Name = "Stage"
	Stage.Value = stage

	local TempChar = player.Character
	if TempChar ~= nil then
		NewCharacter(player, TempChar)
	end
	player.CharacterAdded:Connect(function(char)
		NewCharacter(player, char)
	end)
end

Players.PlayerAdded:Connect(function(player)
	if inGameStartupPlayers[player] == nil then
		NewPlayer(player)
	end
end)

Players.PlayerRemoving:Connect(function(player)
	local success = pcall(function()
		ObbyDataStore:SetAsync(player.UserId, CurrentStage[player.UserId])
		wait (1)
	end)
	CurrentStage[player.UserId] = nil
end)

SkipStage.OnServerInvoke = function(player)
	
	local connection
	local leaderstats = player:FindFirstChild("leaderstats")
	if leaderstats ~= nil then
		local Stage = leaderstats:FindFirstChild("Stage")
		if Stage ~= nil then
			if #Checkpoints:GetChildren() ~= Stage.Value then
				local PurchaseResult = "Purchase Failed"
				connection = MarketplaceService.PromptProductPurchaseFinished:Connect(function(userId, productId, purchased)
				
					if player.UserId == userId and productId == ProductId then
						if purchased == true then
							PurchaseResult = "Success"
							
						end
					end
					connection:Disconnect()
				end)
				MarketplaceService:PromptProductPurchase(player, ProductId)
				repeat wait(0.1) until connection.Connected == false or Players:GetPlayerByUserId(player.UserId) == nil
				return PurchaseResult
			else
				return "You have reached the highest stage!"
			end
		end
	end
end

ResetStage.OnServerEvent:Connect(function(player)
	CurrentStage[player.UserId] = 1
	local TempLeaderstats = player:FindFirstChild("leaderstats")
	if TempLeaderstats ~= nil then
		local TempStage = TempLeaderstats:FindFirstChild("Stage")
		if TempStage ~= nil then
			TempStage.Value = 1
		end
	end     
	NewCharacter(player, player.Character)
end)

MarketplaceService.ProcessReceipt = function(recieptInfo)
	if recieptInfo.ProductId == ProductId then
		local player = Players:GetPlayerByUserId(recieptInfo.PlayerId)
		if player ~= nil then
			CurrentStage[player.UserId] = CurrentStage[player.UserId] + 1
			local leaderstats = player:FindFirstChild("leaderstats")
			if leaderstats ~= nil then
				local Stage = leaderstats:FindFirstChild("Stage")
				if Stage ~= nil then
					Stage.Value = CurrentStage[player.UserId]
				end
			end
			local TempChar = player.Character
			if TempChar ~= nil then
				NewCharacter(player, TempChar)
			end
			return Enum.ProductPurchaseDecision.PurchaseGranted
		end
	end
	return Enum.ProductPurchaseDecision.NotProcessedYet
end

for i,v in pairs(Checkpoints:GetChildren()) do
	local StageNum = tonumber(v.Name)
	v.Touched:Connect(function(hit)
		local char = hit.Parent
		if char ~= nil then
			local Humanoid = char:FindFirstChildOfClass("Humanoid")
			if Humanoid ~= nil and Humanoid.Health > 0 then
				local player = Players:GetPlayerFromCharacter(char)
				if player ~= nil and (TouchDb[player.UserId] or 0) + 1 <= os.time() then
					TouchDb[player.UserId] = os.time()
					local TempCurrentStage = CurrentStage[player.UserId]
					if TempCurrentStage == StageNum - 1 then
						CurrentStage[player.UserId] = StageNum
						local TempLeaderstats = player:FindFirstChild("leaderstats")
						if TempLeaderstats ~= nil then
							local TempStage = TempLeaderstats:FindFirstChild("Stage")
							if TempStage ~= nil then
								TempStage.Value = StageNum
							end
						end
					end
				end
			end
		end
	end)
end

inGameStartupPlayers = Players:GetPlayers()
for i,v in pairs(inGameStartupPlayers) do
	spawn(function()
		NewPlayer(v)
	end)
end

game:BindToClose(function()
	for i,v in pairs(Players:GetPlayers()) do
		ObbyDataStore:SetAsync(v.UserId, CurrentStage[v.UserId])
		wait(6)
	end
	wait(1)
end)

inGameStartupPlayers = {}

the checkpoints are just in a folder called checkpoints

Alright, try this, I believe it should work:

  • First, create a RemoteEvent called SetCameraDirection and put it directly in ReplicatedStorage
  • Replace your NewCharacter function inside the server script with this code:
local SetCameraDirection: RemoteEvent = ReplicatedStorage.SetCameraDirection
local function NewCharacter(player: Player, char: Model)
	local TempCurrentStage = CurrentStage[player.UserId]
	local TempCheckpoint: BasePart = TempCurrentStage and Checkpoints:FindFirstChild(tostring(TempCurrentStage))
	if TempCheckpoint then
		task.wait()
		char:MoveTo(TempCheckpoint.Position)
		SetCameraDirection:FireClient(player, TempCheckpoint.Orientation)
	end
end
  • Create a new LocalScript inside StarterPlayer.StarterPlayerScripts with the following code:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LocalPlayer = Players.LocalPlayer
local Camera = workspace.CurrentCamera

local function CreateModalButton()
	local button = Instance.new("TextButton")
	button.Text = ""
	button.BackgroundTransparency = 1
	button.Active = false
	button.Visible = true
	button.Modal = true
	local gui = Instance.new("ScreenGui")
	gui.Name = "CameraResetModal"
	button.Parent = gui
	gui.Parent = LocalPlayer:WaitForChild("PlayerGui")
	return gui
end

local SetCameraDirection: RemoteEvent = ReplicatedStorage:WaitForChild("SetCameraDirection")
SetCameraDirection.OnClientEvent:Connect(function(dir: Vector3)
	assert(LocalPlayer.CameraMode == Enum.CameraMode.LockFirstPerson, "Set Player.LockFirstPerson to true! (currently false)")
	Camera.Changed:Wait()
	local lastType = Camera.CameraType
	local modal = CreateModalButton()
	Camera.CameraType = Enum.CameraType.Scriptable

	local cameraPos = Camera:GetRenderCFrame().Position
	local newCf = CFrame.new(cameraPos) * CFrame.Angles(math.rad(dir.X), math.rad(dir.Y), math.rad(dir.Z))
	Camera.CFrame = newCf

	Camera.CameraType = lastType
	modal:Destroy()
end)
  • Make sure LockFirstPerson is set to true for the game.
1 Like