WARNING
This is just my approach to how I would program the camera system to your idea using RenderStepped. It is a rahter LONG comment as I explain it step by step, so you shouldn’t get too overwhelmed by it. Here is a detailed overview on how this would work.
I have put some good thought into coming up with a system for this.
NOTE: This has not been tested but is fine as a theory to give you an idea of the system.
now then… where do i start
You will need to use position the lane to make sure the camera is in the center at all times. Use the X and Z values and assign them to a variable using CFrame, try to place it in the center of the lane and on top of the lane (It may even be Y and Z depending on how your set up your system). Then use Y (or X) to move along the lane.
local Camera = workspace.CurrentCamera
local finalCameraPositionCFrame = CFrame.new(LanePart.CFrame.p) * CFrame.new(0, 5, 10)
Camera.CFrame = finalCameraPositionCFrame
(or LanePart.Position, both will return a Vector3)
I am using CFrame.new(0, 5, 10) to give an estimate of where I want my camera to be positioned. You may want to change these around to suit your system.
Grabbing CFrame will grab the rotation as well which we don’t need so make sure you only use the position of the ball!
Yay it moves the camera with the ball while still staying in the center of the lane! Wait, where is the mouse movement thing?
The second step is your mouse to move the camera slightly and to offset the camera a bit. While still keeping inside the renderstepped event, take your final CFrame position that you have calculated to find where your camera position will be, then multiply that with a rotational CFrame using “CFrame.fromEulerAnglesXYZ”.
In a nutshell:
finalCameraPositionCFrame * CFrame.fromEulerAnglesXYZ(0, 0, 0)
I still dont have any rotation. How do we get a values for that?
The way I would do it is to grab the screen resolution by using a ScreenGui and doing a bit of math like so:
local ScreenRes = ScreenGui.AbsoluteSize
This will return Vector2 of the player’s screen size for us to use. I would place this inside the RenderStep as some PC players may resize their client and their values will change.
Whats the screen resolution for?
We need to divide the screen resolution by 1 so we can multiply that later with a bit of math. This will keep the values ranging from 0 to 1.
local xdivide = 1 / ScreenRes.X
local ydivide = 1 / ScreenRes.Y
Now if we grab the mouse position on the screen (it should return the values in relitive to your screen) and multiply our xdivide and ydivide by the position that we created eariler. This will then tell us how far appart the mouse is from the screen using a range from 0 to 1. If it is half way on the screen on the horizontal axis, the value of X will be 0.5 for example.
local mouse = game:GetPlayers("Players").LocalPlayer:GetMouse()
local x = xdivide * mouse.X
local y = ydivide * mouse.Y
Why do I need this again?
We need to assign how far we want the camera to rotate around in degrees. Since we have a value of where our mouse is on screen, I will assign a maximum rotation of 10 degrees and multiply both together to get a value to put in that fromEulerAnglesXYZ thing we had earlier.
local maxDeg = 10
x = x * maxDeg
y = y * maxDeg
You can make another varaible of maxDeg for a different angle if you want X and Y to be independant.
So I put this into the fromEulerAnglesXYZ thing right?
Yes, lets just put this all together.
game:GetService("RunService").RenderStepped:Connect(function()
local Camera = workspace.CurrentCamera
local finalCameraPositionCFrame = CFrame.new(LanePart.CFrame.p) * CFrame.new(0, 5, 10)
local ScreenRes = ScreenGui.AbsoluteSize
local xdivide = 1 / ScreenRes.X
local ydivide = 1 / ScreenRes.Y
local mouse = game:GetPlayers("Players").LocalPlayer:GetMouse()
local x = xdivide * mouse.X
local y = ydivide * mouse.Y
local maxDeg = 10
x = x * maxDeg
y = y * maxDeg
Camera.CFrame = finalCameraPositionCFrame * CFrame.fromEulerAnglesXYZ(math.rad(y), math.rad(x), 0)
end)
Note: fromEulerAnglesXYZ require radians for rotation, math.rad() converts degrees into radians.
You may want to change y and x rotation around a bit so you can get them aligned.
I wanted to move the camera sideways instead of rotating with the mouse
If you wanted the camera to move side to side rather than rotating a bit, you can change “maxDeg” to the value you want in studs and change “CFrame.fromEulerAnglesXYZ()” to CFrame.new() if you want to poisition them instead.
How do I put this in with the ball movement though?
To move the ball around after it has been thrown, you can still reference your values using the variables “x” and “y” from the small algorithm we made earlier on and placing that on some BodyMover to move your ball around.
Thats cool and all but where is the shaking bit?
more math? maybe?
math.noise() will probably be a good place for having random generated values for your shaking system. You can probably find better ways other than math.noise() to implement the shaking part. But since this comment is already long and tedious, il leave it up to you to figure that out!
Camera.CFrame = finalCameraPositionCFrame
* CFrame.new(shakeY, shakeX, 0)
* CFrame.fromEulerAnglesXYZ(math.rad(y), math.rad(x), 0)
shakeX and shakeY would probably be where you want your values to go for your shaking algorithm.
thats it? cool!
Hope this stupidly long comment helped you in some way! If you have any questions about this or want me to explain this further just let me know, I would be glad to help you further if needed.