Camera orbiting a position jerking

I want a player’s camera to orbit around a position. Rotating around the Y axis works fine, but rotating around either the Z or X axis causes abrupt and brief jerks to happen twice per orbit.

Code with jerking that rotates around X:

local Services = {
    Run = game:GetService("RunService"),
    Workspace = game:GetService("Workspace"),
}

local Configuration =  {}
Configuration.Target = Services.Workspace.Part
Configuration.OrbitRadius = 10
Configuration.FramesPerOrbit = 360

local Camera = Services.Workspace.Camera

local SpinAngle = 0

local function UpdateCamera()
    SpinAngle = (SpinAngle + ((2 * math.pi) / Configuration.FramesPerOrbit)) % (2 * math.pi)
    local Offset = CFrame.new(0, math.sin(SpinAngle) * Configuration.OrbitRadius, math.cos(SpinAngle) * Configuration.OrbitRadius)
    local Position = Offset:ToWorldSpace(Configuration.Target.CFrame).Position
    Camera.CFrame = CFrame.new(Position, Configuration.Target.Position)
end

Camera.CameraType = Enum.CameraType.Scriptable
Camera.Focus = Configuration.Target.CFrame
Services.Run.Stepped:Connect(UpdateCamera)
Code without jerking that rotates properly around the Y axis:
local Services = {
    Run = game:GetService("RunService"),
    Workspace = game:GetService("Workspace"),
}

local Configuration =  {}
Configuration.Target = Services.Workspace.Part
Configuration.OrbitRadius = 10
Configuration.FramesPerOrbit = 360

local Camera = Services.Workspace.Camera

local SpinAngle = 0

local function UpdateCamera()
    SpinAngle = (SpinAngle + ((2 * math.pi) / Configuration.FramesPerOrbit)) % (2 * math.pi)
    local Offset = CFrame.new(math.sin(SpinAngle) * Configuration.OrbitRadius, 0, math.cos(SpinAngle) * Configuration.OrbitRadius)
    local Position = Offset:ToWorldSpace(Configuration.Target.CFrame).Position
    Camera.CFrame = CFrame.new(Position, Configuration.Target.Position)
end

Camera.CameraType = Enum.CameraType.Scriptable
Camera.Focus = Configuration.Target.CFrame
Services.Run.Stepped:Connect(UpdateCamera)

I should be able to just solve this as a 2D problem, where I can replace X and Y with any two axis. Why can’t I?

Instead of doing CFrame.new(vector3, vector3), you could do CFrame.new(CenterVector3) * RotatedCFrame * CFrame.new(0,0,-Radius)

I can’t check rn, but I think RotatedCFrame for you would be CFrame.Angles(0,0,SpinAngle)

it’s because CFrame.new(vecotr3, vecotr3) glitches when looking down or up

1 Like

Here is a wrapper function that I use for constructing CFrames:

function makeCframe(position, lookVector, upVector)
	if (position == nil) then
		position = Vector3.new(0, 0, 0)
	end
	if (lookVector == nil or lookVector.Magnitude == 0) then
		lookVector = Vector3.new(0, 0, -1)
	end
	if (upVector == nil or upVector.Magnitude == 0) then
		upVector = Vector3.new(0, 1, 0)
	end
	lookVector = lookVector.Unit
	upVector = upVector.Unit
	if ((lookVector - upVector).Magnitude <= 1e-5 or (lookVector + upVector).Magnitude <= 1e-5) then
		upVector = Vector3.new(0, 1, 0)
		if ((lookVector - upVector).Magnitude <= 1e-5 or (lookVector + upVector).Magnitude <= 1e-5) then
			upVector = Vector3.new(0, 0, 1)
		end
	end
	local right = lookVector:Cross(upVector).Unit
	local up = right:Cross(lookVector).Unit
	return CFrame.fromMatrix(position, right, up)
end

When you use the CFrame constructor that takes two Vector3s as parameters, it does something like this, but with the ‘UpVector’ defaulting to (0,1,0). When you are trying to construct a CFrame with the forward vector almost parallel to the up vector it can cause some weird rotations since the right vector would be undefined. What I would recommend is calculate some vector besides that to be the up vector in the case that you are rotating around the x or z axis.

When I made an orbit simulator I used something like this:

-- r is the satellite's position vector
-- v is the satellite's velocity vector
-- this CFrame oriented the spaceship so that the planet is directly below the ship
local cf1 = makeCFrame(r, r:Cross(v):Cross(r), r)
-- this one made it so the planet appears below, but the ship is still pointing along it's velocity vector
local cf2 = makeCFrame(r, v, r)
-- this one oriented the ship so that it faced the planet, with it's 'upvector' pointing in the direction of it's velocity
local cf3 = makeCFrame(r, -r, v)

I’ll try to upload videos of them but the website’s being really difficult right now.

cf1:

cf2:

cf3:

4 Likes