Issue Offsetting Part from Camera CFrame

I’m trying to prototype a climbing game and when the player isn’t using their hands i want them to follow the camera at the bottom of the screen. However, when I tried doing this my solution isn’t properly holding the position when rotating and when looking straight up or down.

Heres a recording of what I mean:

As you can see, the hands positioning is okay when looking straight, but as soon as the camera begins to look up or down they drift closer together.

Heres a copy of my code:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

local function AdjustHandPosition(hand : Instance)
	local cameraCFrame = camera.CFrame
	local cameraUnitLookVector = cameraCFrame.LookVector.Unit
	local leftHandOffset = CFrame.new(2,-1,-2)
	local rightHandOffset = CFrame.new(2,-1,2)
	
	
	leftHandOffset = Vector3.new(

		(leftHandOffset.X * cameraUnitLookVector.X) - (leftHandOffset.Z * cameraUnitLookVector.Z),

		(leftHandOffset.X * cameraUnitLookVector.Y) + (leftHandOffset.Y * (math.abs(cameraUnitLookVector.X) + math.abs(cameraUnitLookVector.Z))),

		(leftHandOffset.X * cameraUnitLookVector.Z) + (leftHandOffset.Z * cameraUnitLookVector.X)

	)
	
	
	rightHandOffset = Vector3.new(
		
		(rightHandOffset.X * cameraUnitLookVector.X) - (rightHandOffset.Z * cameraUnitLookVector.Z),
		
		(rightHandOffset.X * cameraUnitLookVector.Y) + (rightHandOffset.Y * (math.abs(cameraUnitLookVector.X) + math.abs(cameraUnitLookVector.Z))),
		
		(rightHandOffset.X * cameraUnitLookVector.Z) + (rightHandOffset.Z * cameraUnitLookVector.X)
		
	)
	
	
	print(cameraCFrame.LookVector)
	print(rightHandOffset)
	
	if hand.Name == "HandLeft" then
		local goal = cameraCFrame + (leftHandOffset) 
		local tween = TweenService:Create(hand, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), {CFrame = goal})
		tween:Play()
	elseif hand.Name == "HandRight" then
		local goal = cameraCFrame + (rightHandOffset)
		local tween = TweenService:Create(hand, TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), {CFrame = goal})
		tween:Play()
	end
end

local function InitializeClimbing()
	local handLeft = ReplicatedStorage:WaitForChild("HandTemplate"):Clone()
	local handRight = ReplicatedStorage:WaitForChild("HandTemplate"):Clone()
	handLeft.Name = "HandLeft"
	handRight.Name = "HandRight"
	handLeft.Parent = player.Character
	handRight.Parent = player.Character
	
	
	RunService.Stepped:Connect(function()
		AdjustHandPosition(handLeft)
		AdjustHandPosition(handRight)
	end)
end

player.CharacterAdded:Connect(function()
	InitializeClimbing()
end)

I couldn’t find any solutions to this problem when I looked around on the forum, so I’d really appreciate if someone could point me in the right direction with all of this. I have a feeling that my solution to this problem is pretty naive, but I don’t have that much experience handing problems like these. Also, forgive me if my code is sloppy, I wrote it in 2 hours late last night and this is just for prototyping.


Is this what you were looking for? I messed around with your code and made a re-scripted version of my own. From my understanding, the problem lies in how you calculated the offsets. You only used the LookVector and not the UpVector or RightVector taken into consideration.

local rp = game:GetService("ReplicatedStorage")
local plrs = game:GetService("Players")
local rService = game:GetService("RunService")
local tService = game:GetService("TweenService")

local player = plrs.LocalPlayer
local camera = workspace.CurrentCamera

local offsets = {
	HandLeft = Vector3.new(-2, -1, 3),
	HandRight = Vector3.new(2, -1, 3),
}

local function adjustHandPos(hand: BasePart)
	local cameraCFrame = camera.CFrame
	local offset = offsets[hand.Name]

	if not offset then return end

	local worldOffset =
		(cameraCFrame.RightVector * offset.X) +
		(cameraCFrame.UpVector * offset.Y) +
		(cameraCFrame.LookVector * offset.Z)

	local goalCFrame = CFrame.new(cameraCFrame.Position + worldOffset)

	local tween = tService:Create(
		hand,
		TweenInfo.new(0.05, Enum.EasingStyle.Linear, Enum.EasingDirection.Out),
		{ CFrame = goalCFrame }
	)
	tween:Play()
end

local function initClimb(char: Model)
	local handTemplate = rp:WaitForChild("HandTemplate")

	local handLeft = handTemplate:Clone()
	handLeft.Name = "HandLeft"
	handLeft.Parent = char

	local handRight = handTemplate:Clone()
	handRight.Name = "HandRight"
	handRight.Parent = char

	rService.RenderStepped:Connect(function()
		adjustHandPos(handLeft)
		adjustHandPos(handRight)
	end)
end

player.CharacterAdded:Connect(initClimb)
if player.Character then
	initClimb(player.Character)
end

2 Likes

Thank you so much! I wasn’t sure if the other CFrame components were useful in this usecase cause the documentation was so foreign to me lol
Is there somewhere that I can look to understand them (and vectors in general) some more?


Here are a few posts I could find, but my personal suggestion is just to mess around with it more and breakdown the concept in your own ways by experimenting with it.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.