Right, so I’m trying to create a camera system of which the camera is fixated to a determined part (a moving part, that is). Easy enough, right? Just a simple workspace.CurrentCamera.CFrame = workspace.Part.CFrame, maybe with the CameraType set to Scriptable. Well, what I’m trying to go for is free camera movement (which I am looking to have restricted to a maximum of 40 to 50 degrees in any direction) that is still relative to said moving part. Setting the CameraSubject will leave the camera to possess free movement from said point, though the camera angle does not bode with the part’s new angle, should it move. I’m rather stumped on this, so any answer that can point me in the right direction would be of great help. If you need more information on what I’m trying to describe, take the ‘Death from Above’ mission from Call of Duty 4: Modern Warfare. It’s directly what I’m trying to achieve and is practically the only part of the whole system I’m lost on.
tl;dr: Need pointers on how I should go about panning a camera freely while staying relative to a moving part’s angle.
Okay let’s name moving object plane and camera stays camera.
Okay plane moves 5 ft. Wouldn’t that mean that the camera would move 5ft too? However since it is free let’s say it goes 2 ft around the plane to keep it moving freely.
Camera = plane + X. (Of course we are adding CFrames here and X is variable maybe moving back in forth or so)
Not on my computer so just trying to make sense of this.
So let’s say there are ten frames with the plane. To make it easy we will say that the camera is in front of the plane and will either zoom in or zoom out on the plane. We want to zoom out 10 feet on frame 10 and the plane moves 20 feet.
Current frame:
Plane: 0 feet
Zoom: 0 feet
Total feet the camera moves: 0 feet
Frame 1:
Plane: 2 feet
Zoom: 1 feet
Camera has moved: 3 feet
Frame 2:
Plane: 4 feet
Zoom: 2 feet
Camera has moved: 6 feet
…
Frame 5:
Plane: 10 feet
Zoom: 5 feet
Camera has moved: 15 feet
…
Frame 10:
Plane: 20 feet
Zoom: 10 feet
Camera has moved: 30 feet
That will give the effect that the camera is zooming out 10 feet even though the plane moved 20 feet while that was happening.
Conclusion:
So when you tween the camera, just add the zoom and the distance the plane is going to move. It of course doesn’t have to be a plane. Not entirely sure if that answers your question, but I felt I would give it a try.
I probably didn’t word it properly, my apologies if that’s the case. I’ve handled on how the camera’s position itself moves along with the moving part, but not how the camera’s angle will always stay relative to the moving part’s angle plus the player’s freedom to move the camera too. It’s sort of hard to explain, but what I’m trying to achieve is shown in the video posted with how the player is moving the camera around, with the camera still staying relative to the plane’s rotating angle.
Perhaps this could work for you. The smoothing is a little buggy, though and the automatic return to center has no smoothing at all. I don’t know how to make a better smoothing system.
local UserInputService = game:GetService("UserInputService")
-- the camera can go this amount of degrees to both directions (negative and positive) from your default CFrame
-- the smoothing reduces this a little bit, and ROUND_CORNERS affects this too.
local FREEDOM_DEGREES = 30
local BIND_NAME = "CameraFollow"
local CAMERA_MOVE_SENSITIVITY = .5
local CAMERA_MOVE_SMOOTHNESS = 0 -- I recommed keeping this between 0 and 1, if it's bigger, the camera won't reach the max angle
local CAMERA_SMOOTH_START = 0 -- the smoothing affects the camera movement when math.abs(angle) >= CAMERA_SMOOTH_START*maxRad
local ROUND_CORNERS = true -- limits the camera movement to a circular area
local AUTOMATIC_CENTER_RETURN = false -- The variables below have no effect when this is false.
local RETURN_TO_CENTER_DELAY = 2
local RETURN_TO_CENTER_TIME = 2
local priority = Enum.RenderPriority.Camera.Value+1
local maxRad = math.rad(FREEDOM_DEGREES)
local minRad = -maxRad
local screenSize = camera.ViewportSize
local xAngle, yAngle = 0, 0
local startToReturnTick
local originalXAngle, originalYAngle -- x and y angles when the returning begins
camera.CameraType = Enum.CameraType.Custom
local function mouseCameraRotate(cf) -- cf is your default CFrame without rotation from player input
-- GetMouseDelta only works when mouse is locked, otherWise coordinates are 0.
-- The mouse is locked when the player holds down the right mouse button.
local mouseDelta = UserInputService:GetMouseDelta()
if mouseDelta ~= Vector2.new() then
local angleCalculationHelperValue = 1/screenSize.Y*math.pi*CAMERA_MOVE_SENSITIVITY*.1
local xAngleToAdd, yAngleToAdd = mouseDelta.Y*angleCalculationHelperValue, mouseDelta.X*angleCalculationHelperValue
local rawXAngle, rawYAngle = xAngle+xAngleToAdd, yAngle+yAngleToAdd
-- additional limiting values to round corners
local roundXMaxRad, roundYMaxRad
if ROUND_CORNERS and rawXAngle ~= 0 and rawYAngle ~= 0 then
local atanXAYA = math.abs(math.atan2(rawXAngle, rawYAngle))
roundXMaxRad, roundYMaxRad = math.abs(math.sin(atanXAYA)*maxRad), math.abs(math.cos(atanXAYA)*maxRad)
end
local xMaxRad, yMaxRad = roundXMaxRad or maxRad, roundYMaxRad or maxRad
local xMinRad, yMinRad = -xMaxRad, -yMaxRad
local smoothCalcXMinus, smoothCalcYMinus = CAMERA_SMOOTH_START*xMaxRad, CAMERA_SMOOTH_START*yMaxRad
-- smoothing the camera movement when angle is big enough
local smoothXAngleToAdd, smoothYAngleToAdd
if math.abs(rawXAngle/xMaxRad) >= CAMERA_SMOOTH_START then
local smoothAddMinus = (math.abs(rawXAngle)-smoothCalcXMinus)/(xMaxRad-smoothCalcXMinus)^CAMERA_MOVE_SMOOTHNESS*xAngleToAdd
smoothXAngleToAdd = xAngleToAdd-smoothAddMinus
end
if math.abs(rawYAngle/yMaxRad) >= CAMERA_SMOOTH_START then
local smoothAddMinus = (math.abs(rawYAngle)-smoothCalcYMinus)/(yMaxRad-smoothCalcYMinus)^CAMERA_MOVE_SMOOTHNESS*yAngleToAdd
smoothYAngleToAdd = yAngleToAdd-smoothAddMinus
end
-- these will be clamped to make sure the angles aren't bigger than max angle values.
local xAngleToClamp, yAngleToClamp = xAngle+(smoothXAngleToAdd or xAngleToAdd), yAngle+(smoothYAngleToAdd or yAngleToAdd)
-- these are the finished angles
xAngle, yAngle = math.clamp(xAngleToClamp, xMinRad, xMaxRad), math.clamp(yAngleToClamp, yMinRad, yMaxRad)
if AUTOMATIC_CENTER_RETURN then
startToReturnTick = tick()+RETURN_TO_CENTER_DELAY
originalXAngle, originalYAngle = xAngle, yAngle
end
else local currentTick = tick()
if startToReturnTick and currentTick >= startToReturnTick then
if currentTick-startToReturnTick >= RETURN_TO_CENTER_TIME then
print("hi")
xAngle, yAngle = 0, 0
else local m = 1-(currentTick-startToReturnTick)/RETURN_TO_CENTER_TIME
xAngle, yAngle = originalXAngle*m, originalYAngle*m
end
end
end
return cf*CFrame.Angles(-xAngle, -yAngle, 0)
end