Any ideas for a system like this?

Hey! So I’ve been considering several ideas in order to improve the functionality of my game, so that it might take a bit more skill rather than just clicking and pressing a button to throw the ball. (bowling game.)

Okay so, I got this really nice bowling game quite a long time ago, it’s pretty old, but it included a pretty nice bowling mechanic in there, nothing I’ve ever seen before, and I’ve played quite a lot of bowling games in my time xd.

Basically, the camera would face the lane, and once you set ur ball power or what-not, the camera started going towards the lane, and it would face your mouse. Basically, the ball would launch at the center of the camera. But what made it tricky was that as the camera moved towards the lane, it would shake very lightly, therefor making the ball go way off line if you just aimed at the beginning, but didn’t straighten it out as it went through. I feel implementing this mechanic would definitely make the game a bit more skillful, however, I couldn’t find the right technique to actually code this.

I’ve tried messing around with lerp and stuff, but the effects I get on the camera are just way too intense… any ideas or sample code for something like this would be highly appreciated! Thanks for reading my long, wordy post. :stuck_out_tongue:

2 Likes

If it were me, I’d write a custom camera script which sets the camera’s CFrame every frame (by binding to RenderStepped). Every frame, I’d add a slight change to the shake rotational velocity and apply the rotational velocity. Both of these operations would obviously be proportional to the time between frames. Each frame the camera’s new orientation would be its previous position plus its linear velocity, with a rotation of its previous rotation combined with any rotation from user input and the shake rotational velocity.

What game was it that had this mechanic, I’m curious as I used to play a few bowling games growing up.

Bowling Evolution by Sadetta Oy. Pretty nostalgic game c:

Interesting. Well thanks for the information, not sure how I might go about implementing it, but I’ll try :stuck_out_tongue:

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.

5 Likes

Thank you very much for that in-depth walkthrough of this system, Watermelon! I could only imagine how long that took you to write up, I highly appreciate it. I’ll definitely attempt to mess around and try to create a system based on the wonderful samples you’ve provided. :smiley:

2 Likes

Something I did forget to mention when multiplying xdivide by your mouse.X value is that you have to give it an offset. Currently moving the mouse to the left will keep the camera in the center. This is because 0 * 10 will return 0. Subtracting it will make the value negative meaning it will move in the other direction. Just subtract half of the relative value from your calculation to center the whole range. The range should be something like -0.5 to 0.5 rather than 0 to 1. There still if 1 unit difference between the two ranges!

local x = (xdivide * mouse.X) - 0.5
1 Like

I see. Will do!