CFrame help with camera system!

Hello, developers!
Before I start this topic, keep in mind that I am fairly new to CFrame and I don’t entirely understand how it works.

I have made this simple camera system:

local TargetCFrame = CFrame.new(Vector3.new(0,10,0))

local Camera = workspace.Camera

repeat Camera.CameraType = Enum.CameraType.Scriptable wait() until Camera.CameraType == Enum.CameraType.Scriptable

local UserInputService = game:GetService("UserInputService")

local Mouse = game:GetService("Players").LocalPlayer:GetMouse()

local Rotation = 0

local Directions = {
	["W"] = CFrame.new(Vector3.new(0,0,-1));
	["A"] = CFrame.new(Vector3.new(-1,0,0));
	["S"] = CFrame.new(Vector3.new(0,0,1));
	["D"] = CFrame.new(Vector3.new(1,0,0));
	["Q"] = CFrame.new(Vector3.new(0,1,0));
	["E"] = CFrame.new(Vector3.new(0,-1,0));
}

local CurrentKeys = {}

Mouse.KeyDown:Connect(function(KeyCode)
	KeyCode = KeyCode:upper()
	if not table.find(CurrentKeys, KeyCode) then
		table.insert(CurrentKeys, KeyCode)
	end
end)

Mouse.KeyUp:Connect(function(KeyCode)
	KeyCode = KeyCode:upper()
	if table.find(CurrentKeys, KeyCode) then
		table.remove(CurrentKeys, table.find(CurrentKeys, KeyCode))
	end
end)

game:GetService("RunService").RenderStepped:Connect(function()
	game:GetService("TweenService"):Create(Camera, TweenInfo.new(.5, Enum.EasingStyle.Quint), {CFrame = TargetCFrame* CFrame.Angles(0,math.rad(Rotation),0)}):Play()
	
	for _, KeyCode in pairs(CurrentKeys) do
		if Directions[KeyCode] then
			TargetCFrame *= Directions[KeyCode]
		end
	end
	
	local LeftX = Camera.ViewportSize.X/5
	
	local RightX = (Camera.ViewportSize.X/5) * 4
	
	local MouseX = Mouse.X
	
	if LeftX > MouseX then
		Rotation += 1
	elseif MouseX > RightX then
		Rotation -= 1
	end
end)

However, when the script adds or subtracts CFrame depending on the keys being pressed, it is “global”… it’s moving along a global x/y/z axis. I assumed that multiplying one CFrame by another would create a local x/y/z axis.

Here’s a paint drawing to better express what I am trying to say here:
image

How can I adjust CFrame based on a current CFrame?

2 Likes

You are not wrong here, it’s rather an issue with the TargetCFrame variable you got. This effect of moving on a local axis is only applicable if the previous CFrame has a rotation.

local newCFrame = previousCFrame * movementCFrame

However in your script you have decided to seperate the positionCFrame and the orientation CFrame and consequently the effects are not as they seem.

To resolve this just add the rotation before the movementCFrame operation in order to convert the object space direction to world space direction.
(Hence it is known as CFrame:ToWorldSpace(cf) from documentation)

	for _, KeyCode in pairs(CurrentKeys) do
		if Directions[KeyCode] then
			TargetCFrame *= CFrame.Angles(0,math.rad(Rotation),0)*Directions[KeyCode]
		end
	end

I really like the current code and the usage of tables making it neat rather than a lot of if statements, really refreshing after hanging out in #help-and-feedback:scripting-support a long time.

1 Like

Thank you for this!

While your code didn’t quite work, the principle of what you’re saying got through to me. :sweat_smile:

local TargetCFrame = CFrame.new(Vector3.new(0,10,0))

local Camera = workspace.Camera

repeat Camera.CameraType = Enum.CameraType.Scriptable wait() until Camera.CameraType == Enum.CameraType.Scriptable

local UserInputService = game:GetService("UserInputService")

local Mouse = game:GetService("Players").LocalPlayer:GetMouse()

local Directions = {
	["W"] = CFrame.new(Vector3.new(0,0,-.5));
	["A"] = CFrame.new(Vector3.new(-.5,0,0));
	["S"] = CFrame.new(Vector3.new(0,0,.5));
	["D"] = CFrame.new(Vector3.new(.5,0,0));
	["Q"] = CFrame.new(Vector3.new(0,.5,0));
	["E"] = CFrame.new(Vector3.new(0,-.5,0));
}

local CurrentKeys = {}

Mouse.KeyDown:Connect(function(KeyCode)
	KeyCode = KeyCode:upper()
	if not table.find(CurrentKeys, KeyCode) then
		table.insert(CurrentKeys, KeyCode)
	end
end)

Mouse.KeyUp:Connect(function(KeyCode)
	KeyCode = KeyCode:upper()
	if table.find(CurrentKeys, KeyCode) then
		table.remove(CurrentKeys, table.find(CurrentKeys, KeyCode))
	end
end)

game:GetService("RunService").RenderStepped:Connect(function()
	game:GetService("TweenService"):Create(Camera, TweenInfo.new(.5, Enum.EasingStyle.Quint), {CFrame = TargetCFrame}):Play()
	
	for _, KeyCode in pairs(CurrentKeys) do
		if Directions[KeyCode] then
			TargetCFrame *= Directions[KeyCode]
		end
	end
	
	local LeftX = Camera.ViewportSize.X/5
	
	local RightX = (Camera.ViewportSize.X/5) * 4
	
	local MouseX = Mouse.X
	
	if LeftX > MouseX then
		TargetCFrame *= CFrame.Angles(0,math.rad(1),0)
	elseif MouseX > RightX then
		TargetCFrame *= CFrame.Angles(0,math.rad(-1),0)
	end
end)

I just removed the rotation variable and multiplied the target CFrame directly.

The problem with your code is that…

-- every time a frame is rendered....
	for _, KeyCode in pairs(CurrentKeys) do
 -- loop through the keys that are being pressed
		if Directions[KeyCode] then
			TargetCFrame *= CFrame.Angles(0,math.rad(Rotation),0)*Directions[KeyCode]
-- multiply the CFrame by the rotation and the direction
		end
	end

It was increasing the rotation infinitely… not good!

Whenever I want a headache I just go to scripting support and look at all the code with hundreds of lines of if statements. I was being more lazy than normal with this code and leaving variables undefined etc but I appreciate the compliment!

1 Like