How do I scale a CFrame?

I am trying to make a smooth return to the original camera and hand position in a view model, but I can’t figure out how to scale a CFrame especially since its a 4x4 matrix. I am currently learning algebra 2 in school and have next to no knowledge of matrices let alone translating multiplication of matrices to code.

Here’s what I’m trying to do (similar to how you would scale a Vector3):

cf = cf * .75

However I am receiving an error in the console that says ‘invalid argument 2’. I have searched far and wide for a simple answer, but the closest thing I found was a function that is not really for scaling.

1 Like

To clarify - what component are you trying to scale?
The position, the rotation, or both?

1 Like

I’m trying to scale both as the offset is CFrame * CFrame.Angles

are you trying to change influence of cframe operations or something?

1 Like

here is a code snippet

--Countinued...

        local bobble = CFrame.new(x2*clamp, y2*clamp, 0) * CFrame.Angles(x2R*clamp, 0, zR*clamp)

		cam.CFrame = cam.CFrame:Lerp(cam.CFrame * bobble, .1)
		
		local bobble2 = CFrame.new(0, yR*clamp, xR*clamp)
		
		final = final:Lerp(sway * bobble2, .1)
		
		hand.CFrame = hand.CFrame:ToWorldSpace(final)
	else
		
		
		hand.CFrame = offset * sway
		
		cam.CFrame = cam.CFrame
	end
end

Code makes no sense past like 4 lines…

You need to understand what CFrames are in the first place.

???

Maybe you have something like: hand.CFrame = defaultHandCFrame * (offset * sway)?

1 Like

I’ll just give you the full thing to see where the values are coming from

--//Services
local rst = game:GetService("ReplicatedStorage")
local rs = game:GetService("RunService")
local ts = game:GetService("TweenService")

--//Player
local player = game:GetService("Players").LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local hum = char.Humanoid or char:FindFirstChild("Humanoid")
local humR = char.HumanoidRootPart or char:FindFirstChild("HumanoidRootPart")

--//Stuff
local hand = rst.Hand:Clone()
local handOffset = CFrame.new(2, -2.5, -2.6) * CFrame.Angles(math.rad(96), math.rad(-292.5), math.rad(40))

--//Camera
local cam = workspace.CurrentCamera
local lastCamCF = cam.CFrame
local originalCamCF = cam.CFrame
hum.CameraOffset = Vector3.new(0, 2, 0)

--//Main

hand.Parent = workspace

local final = CFrame.new()
local sway = CFrame.new()
local mult = 3

local function update()
	local rot = cam.CFrame:ToObjectSpace(lastCamCF)
	local x,y,z = rot:ToEulerAngles()
	sway = sway:Lerp(CFrame.new(-math.sin(y)*mult, 0, -math.sin(y)*mult), .02)
	
	local offset = cam.CFrame:ToWorldSpace(handOffset)
	
	hand.CFrame = offset * sway
	
	lastCamCF = cam.CFrame
	
	if hum.MoveDirection.Magnitude > 0 then
		local now = os.clock()
		local vel = humR.AssemblyLinearVelocity
		local clamp = math.min(1, vel.Magnitude / hum.WalkSpeed)

		local xR = math.sin(hum.WalkSpeed*now*.5)/ 40
		local yR = math.abs(math.cos(hum.WalkSpeed*now*.5))/ -20
		
		local x2 = math.sin(hum.WalkSpeed*now*.5) * 1.5
		local y2 = math.abs(math.cos(hum.WalkSpeed*now*.5)) * 2
		
		local zR = math.sin(hum.WalkSpeed*now*.5)/ 20
		local x2R = math.sin(hum.WalkSpeed*now)/ 400
		
		local bobble = CFrame.new(x2*clamp, y2*clamp, 0) * CFrame.Angles(x2R*clamp, 0, zR*clamp)

		cam.CFrame = cam.CFrame:Lerp(cam.CFrame * bobble, .1)
		
		local bobble2 = CFrame.new(0, yR*clamp, xR*clamp)
		
		final = final:Lerp(sway * bobble2, .1)
		
		hand.CFrame = hand.CFrame:ToWorldSpace(final)
	else
		
		
		hand.CFrame = offset * sway
		
		cam.CFrame = cam.CFrame
	end
end

I think you can just do:

local exampleRotation = exampleCFrame.Rotation
exampleCFrame = (exampleCFrame.Position * 0.75) * CFrame.Angles(exampleRotation.X, exampleRotation.Y, exampleRotation.Z)

Probably an easier way to do this but I’m not sure how.

1 Like

I tried it, but it didn’t work, it gave me ‘invalid argument #1 (CFrame expected, got Vector3)’. I tried changing (exampleCFrame.Position * 0.75) to CFrame.new(cam.CFrame.Position*.75), but it just made it look weird. I’ve tried Vector3 conversion a lot, but it doesn’t want to cooperate.

Yeah, I should’ve done this:

local exampleRotation = exampleCFrame.Rotation
exampleCFrame = CFrame.new(exampleCFrame.Position * 0.75) * CFrame.Angles(exampleRotation.X, exampleRotation.Y, exampleRotation.Z)

which I see you’ve already tried.

Maybe you don’t want to scale the position? Your script doesn’t scale the position relative to anything which might make it look weird.

1 Like

This seems like a really weird problem to want to solve (XY problem cough cough)

I read the whole thing over and still did not understand what you are trying to do :skull:
Since trying to scale it down to 0,0,0 doesn’t work, or “looks weird”
have you tried lerping an empty cframe to the current cframe? (this would “scale” the rotation as well)

CFrame.new():Lerp(yourCurrentCFrame)
1 Like

This is what it looks like(This is just for learning and for fun, not publishing)

2 Likes

Just to make sure I understand the problem correctly, the camera should sway when walking, and smoothly transition to not swaying when not walking?
(I really don’t see what is wrong in the video…)
In that case it may be helpful to store the time() when the User was last walking, and use that as the t in your lerp

1 Like

Like this? (now is os.clock)

cam.CFrame = CFrame.new():Lerp(cam.CFrame*now, .1)

So I am assuming I understood your problem correctly???
outside of everything initialize

local previousWalkingTime : number = 0

then

local now = time() -- move it outside the if statement
if hum.MoveDirection.Magnitude > 0 then
    previousWalkingTime = time() 
-- do everything else
else
-- do normally
end

so now you can use t, which is the distance of now to when you are last walking
local t = math.abs(time() - previousWalkingTime)
so you would basically tween toward the non walking cframe using t

you put t in the t slot of the lerp
WalkingCFrame:Lerp(nonWalkingCFrame,t)

1 Like

This? It does the same thing as when I started. Also, yes I am trying to do what you are describing

local previousWalkingTime : number = 0

local function update()
	local rot = cam.CFrame:ToObjectSpace(lastCamCF)
	local x,y,z = rot:ToEulerAngles()
	sway = sway:Lerp(CFrame.new(-math.sin(y)*mult, 0, -math.sin(y)*mult), .02)
	
	local offset = cam.CFrame:ToWorldSpace(handOffset)
	
	hand.CFrame = offset * sway
	
	lastCamCF = cam.CFrame
	
	local now = time()
	
	if hum.MoveDirection.Magnitude > 0 then
		previousWalkingTime = time() 
		local vel = humR.AssemblyLinearVelocity
		local clamp = math.min(1, vel.Magnitude / hum.WalkSpeed)

		local xR = math.sin(hum.WalkSpeed*now*.5)/ 40
		local yR = math.abs(math.cos(hum.WalkSpeed*now*.5))/ -20
		
		local x2 = math.sin(hum.WalkSpeed*now*.5) * 1.5
		local y2 = math.abs(math.cos(hum.WalkSpeed*now*.5)) * 2
		
		local zR = math.sin(hum.WalkSpeed*now*.5)/ 20
		local x2R = math.sin(hum.WalkSpeed*now)/ 400
		
		local bobble = CFrame.new(x2*clamp, y2*clamp, 0) * CFrame.Angles(x2R*clamp, 0, zR*clamp)

		cam.CFrame = cam.CFrame:Lerp(cam.CFrame * bobble, .1)
		
		local bobble2 = CFrame.new(0, yR*clamp, xR*clamp)
		
		final = final:Lerp(sway * bobble2, .1)
		
		hand.CFrame = hand.CFrame:ToWorldSpace(final)
	else
		local t = math.abs(time() - previousWalkingTime)
		
		hand.CFrame = offset * sway
		
		local camRot = cam.CFrame.Rotation
		cam.CFrame = cam.CFrame:Lerp(cam.CFrame, t)
	end
end

no, not like that, like this

local previousWalkingTime: number = 0

local function computeCFWhenWalking()
    local vel = humR.AssemblyLinearVelocity
    local clamp = math.min(1, vel.Magnitude / hum.WalkSpeed)

    local xR = math.sin(hum.WalkSpeed * now * 0.5) / 40
    local yR = math.abs(math.cos(hum.WalkSpeed * now * 0.5)) / -20

    local x2 = math.sin(hum.WalkSpeed * now * 0.5) * 1.5
    local y2 = math.abs(math.cos(hum.WalkSpeed * now * 0.5)) * 2

    local zR = math.sin(hum.WalkSpeed * now * 0.5) / 20
    local x2R = math.sin(hum.WalkSpeed * now) / 400

    local bobble = CFrame.new(x2 * clamp, y2 * clamp, 0) 
        * CFrame.Angles(x2R * clamp, 0, zR * clamp)

    local goalCamCF = cam.CFrame:Lerp(cam.CFrame * bobble, 0.1)

    local bobble2 = CFrame.new(0, yR * clamp, xR * clamp)
    final = final:Lerp(sway * bobble2, 0.1)

    local goalHandCF = hand.CFrame:ToWorldSpace(final)
    return goalCamCF, goalHandCF
end

local function computeCFWhenNotWalking()
    local offset = cam.CFrame:ToWorldSpace(handOffset)

    local goalHandCF = offset * sway
    local camRot = cam.CFrame.Rotation
    local goalCamCF = cam.CFrame:Lerp(cam.CFrame, t)
    return goalCamCF, goalHandCF
end

local function update()
    local rot = cam.CFrame:ToObjectSpace(lastCamCF)
    local x, y, z = rot:ToEulerAngles()
    sway = sway:Lerp(CFrame.new(-math.sin(y) * mult, 0, -math.sin(y) * mult), 0.02)

    local offset = cam.CFrame:ToWorldSpace(handOffset)
    hand.CFrame = offset * sway 

    lastCamCF = cam.CFrame

    local now = time()

    if hum.MoveDirection.Magnitude > 0 then
        previousWalkingTime = time()
    else
        -- Standing still, no update to walking time
    end

    local t = math.abs(time() - previousWalkingTime)
    local goalCamCFW, goalHandCFW = computeCFWhenWalking()
    local goalCamCF, goalHandCF = computeCFWhenNotWalking()

    cam.CFrame = goalCamCFW:Lerp(goalCamCF, t)
    hand.CFrame = goalHandCFW:Lerp(goalHandCF, t)
end

do you get the idea? Transition the entire thing?

1 Like

One more question, how can I change how quick the transition is, it’s too quick to tell if it works or not, but it seems like it works.

Depends on calculating t
Original:

local t = math.abs(time() - previousWalkingTime)

new:
local t = math.abs(time() - previousWalkingTime)/10.0
you see the 10, that is the number of seconds it takes if I am doing math correctly

I also recommend doing t = math.clamp(t,0,1) forgot to do that… (so t will always be between 0 and 1)

If this problem was solved, please mark the solution thanks.