And then we say this i don’t know why :
lastCameraCF = camera.CFrame
The code works by figuring out how the camera has rotated since the previous (“last”, I prefer the less ambiguous “previous”) frame. It does that by comparing the “current” CF to the previous CF, so it needs to store the “old/previous/last” CF for the next frame. It does that after it’s done processing the current frame, otherwise it would be pointless
When we use local rot = camera.CFrame:ToObjectSpace(lastCameraCF)
doesn’t it mean that rot’s orientation becomes the opposite direction of our camera so why don’t we look at our back with this?
doesn’t it mean that rot’s orientation becomes the opposite direction of our camera
No, rot
becomes lastCameraCF
but relative to camera.CFrame
. In other words, "the transformation from camera.CFrame
to lastCameraCF
". Since lastCameraCF
is the CFrame of the camera at the previous frame, rot
becomes the transformation from the previous frame’s CF to the current frame’s CF. Or “how the camera has moved since the last frame”. Well, it’s really the inverse of that because the original programmer flipped the arguments but oh well. I’m a bit confused RN by the double negatives, but i think that explains why the original programmer had to have a negative swayAMT
. transform
would be a way better variable name, or even better transformFromPrevFrame
.
After the assignment, camera.CFrame == lastCameraCF * rot
holds true. Another statement that illustrates what I mean (a and b are Parts):
print(a.CFrame * a.CFrame:ToObjectSpace(b.CFrame) == b.CFrame) --Always true
so why don’t we look at our back with this?
Not sure what you meant by this or “opposite direction of the camera”, sorry. If you explain that in more detail maybe I can help clear it up, but maybe it’s not needed.
It takes the inverse of the camera’s transformation from the last frame (rot
) (inverse because of the flipped arguments I mentioned earlier) and turns that into Euler angles, which is a way of representing orientation or rotation in the way you’re used to from the properties window. “X, Y, Z” angles or preferably “pitch, yaw, roll” angles which is usually clearer Aircraft principal axes - Wikipedia
In the line below, we rotate an empty CFrame with these variables with every rendered frame.
swayCF = swayCF:Lerp(CFrame.Angles(math.sin(X) * swayAMT, math.sin(Y) * swayAMT, 0), 0.1)
Do you mean swayCF
is “empty”? It’s only empty on the first frame, on subsequent frames it’s whatever it was set to on the previous frame. Do you mean that the CF constructed with CFrame.Angles
is “empty”? Well it’s clearly not since it’s being constructed with non-zero parameters.
It’s not entirely accurate to say that swayCF
gets rotated by these variables. After all, the line doesn’t say something like swayCF *= CFrame.Angles(blablabla)
. It’s more like it gets set to a specific orientation, which would be accurate if it weren’t for the Lerp call which just smooths out the movement which is absolutely necessary because mouse movement is super jittery. Plus it makes it look like the viewmodel has momentum.
Setting the sway to an orientation that follows the rotation (“movement” but for orientation) of the camera causes the viewmodel to “lead” where the camera is pointing.
Looking closely at it, I think it’s a mistake to use math.sin
in this case. The fastest I was able to rotate the camera in an experiment was about 0.6 radians. Call it 1.0 to be generous. Here’s x
and sin(x)
plotted in [-1;1]
:
As you can see, they’re pretty close so having the sin
call in there doesn’t do a lot. It does kind of reduce the output a bit at the extreme ends, so it could be a valid approach to make the sway not go beyond a certain limit even with extreme camera movement. But the effect is so small I don’t think it’s worth the confusion, and it doesn’t work at even more extreme movements:
At inputs outside [-pi/2; pi]
, sin
starts moving the wrong direction! So if you move the camera move than 90 degrees in a frame, the sway goes in the opposite direction?! Not sure if that’s the intentional, artistic choice of the original programmer but I don’t think that’s a good idea. First, it’s confusing to read compared to just math.clamp and takes all this analysis to figure out, second it doesn’t have much effect in the domain that’s usually relevant, so it’s very little gain for a decent amount of technical debt, and third it kinda breaks in edge cases. I’d remove it and just have this instead:
swayCF = swayCF:Lerp(CFrame.Angles(X * swayAMT, Y * swayAMT, 0), 0.1)
In my earlier response I said some things about math.sin
, they’re still true but don’t really apply in this situation. I didn’t fully understand back then what it was doing in the code, sorry if my response caused any confusion.
What is the point of this code ? In the first rendered frame, we will use camera.CFrame:ToObjectSpace()
on a empty CFrame (lastCameraCF)
Again, ToObjectSpace
works on TWO CFrames, not just one. So that LoC doesn’t do it “on a CF”, it does it on a pair of CFs. The camera CFrame is probably not empty, so the computation probably actually does do something. Even if it didn’t do anything to the camera CF, it would still have a purpose because it sets lastCFrame
so something can actually happen on the next frame. To be fair it’s a tiiiny bit confusing that the original programmer set lastCameraCF
to CFrame.new()
at the top of the script, because that’s not a sensible “first previous” CFrame. A version that fixes this could look like
RunS.RenderStepped:Connect(function()
--If this is the first frame then there's no sensible way of computing the sway, so just set the variable for next frame and pretend like there has been no movement since the last frame ("previous = current").
_previousCameraCF = _previousCameraCF or camera.CFrame
local rot = ... everything else like it was
end)
This shows a clear intent for how the sway should be handled in the edge case where there is no “previous” frame. This actually fixes minor a bug too, it’s not just just about coding style but thinking clearly about what the code does so good job spotting that there’s something weird. What happens if the player spawns in with a camera that’s yawed 179 degrees? They’ll get a massive spike of sway on the first frame, causing the viewmodel to jerk a bit to the left or right. It won’t be too extreme or even noticable because it’s just for a single frame and then it quickly falls back to a normal state but hey a bug is a bug Another benefit is that there’s no ugly state variable at the top of the script (I mean you could have it but I wouldn’t). A down side is that it’s still a global variable that might get accidentally set by a different function. There’s loads of ways to “capture” that variable in a block so only the relevant code can see it, but this is already a big of a tangent, sorry xD
BTW the a = a or b
thing might look a bit confusing at first and you could argue that it is, but it’s idiomatic (common) Lua so experienced coders will quickly recognize that it’s a way of providing a default value in situations where a value isn’t already provided.
is it used for not swaying when we don’t move, right ? Because if we don’t move, camera.CFrame
won’t change so X,Y,Z values will be 0.
No, none of this code has anything to do with how the camera translates though space (X, Y, Z coordinates, from walking and stuff), only with how the camera rotates (from mouse movement, or touch or controller or VR or whatever). If you try walking but not moving the mouse you should see no sway at all. That’s why I don’t think X, Y and Z are good variable names in this case. Pitch, yaw and roll would make it clearer that it’s talking about rotation and not translation.
The ToOrientation
call completely ignores the position component of the input CFrame, so if the camera moved a bit to the left or w/e that has no effect on the sway. You could use the same approach to make a “walk sway” though. It’d be a great challenge to see how well you understand all this stuff.
Anyway hope this wall of text helps clear some things up xD And I hope I got everything right Ask away if you have follow up questions