For now, I pretty much just simply offset the Left Shoulder, Right Shoulder and ToolGrip Motor. ToolGrip is a custom Motor that connects the tool between tool’s handle and player’s character torso. It is visible in the ViewModel but not a descendant of the ViewModel, it’s a child of the player’s character.
There’s an AimPart inside the tool, What I want to achieve is the AimPart will be positioned in the center of the Camera, here’s the pseudo steps:
Calculate the offset between AimPart and the Center of Camera in CFrame
Let’s say the offset is (-1,0,0), then multiply it to current CFrame (Like I did above)
Then also multiply this offset to Left Shoulder and Right Shoulder such that they will be positioned correctly.
Currently what I did (Code above) does not put the tool in the center the screen, it didn’t even utilize the AimPart inside the tool and here’s the result:
Normal:
ADS (Red Part is the AimPart)
I have all the pseudo steps but I don’t really know how to perform this in code
The way I would go about doing this is setting the primary part of the model to the aim part, the lerping it to the camera, then setting the primary part back to it.
You can use several camera functions to do this. First of all, you want to calculate the depth of the aim part from the camera. You can use WorldToViewportPoint for this. It returns a Vector3 and the Z component is the depth from the camera.
Next you want to determine the distance in pixels. The screen position is returned with the X and Y components (correlating to the X and Y pixel position on screen). You’ll subtract the target X Y from the center X Y (ScreenSize/2) to get the distance in pixels.
Now you’ll use ScreenPointToRay to generate a new 3D world position at the original depth. This will return a Ray. You’ll then use CFrame.new(ray.Origin + ray.Direction, ray.Origin + ray.Direction * 2) to get the ending CFrame of the ray.
Finally, you’ll want to find the 3D offset between the original position and the new position. And then you can offset the entire ViewModel by this amount. This should perfectly center the part on the screen and keep it at the exact depth.
Fun fact: These camera functions work on the server if you create new cameras
Right now I’m using toObjectSpace to caculate the Vector3 position in studs between the Aim Part and the character’s head.
-- inside run service renderstepped
local offset = char:FindFirstChildOfClass("Tool").ADS.CFrame:toObjectSpace(char.Head.CFrame).Position
local offsetX = offset.X
local offsetY = offset.Y
local offsetZ = offset.Z
print(offset)
-- inside a debounce such that C0 will not multiply 60 times a second
char.Torso.ToolGrip.C0 = char.Torso.ToolGrip.C0 * CFrame.new(offsetX,offsetY,offsetZ)
ViewModelTorso["Left Shoulder"].C0 = ViewModelTorso["Left Shoulder"].C0 * CFrame.new(offsetZ,offsetY,-offsetX)
ViewModelTorso["Right Shoulder"].C0 = ViewModelTorso["Right Shoulder"].C0 *CFrame.new(offsetY,offsetZ,-offsetX)
For some reasons, the Left Shoulder and Right Shoulder is not same as ToolGrip Motor, I have to manually change the XYZ values and make some of them negative, at least now it works: https://gyazo.com/5ef3ae485e15cdb6ec8eae9feae6bac4
However, since it calculate the offset between the character’s head and the Aim Part, the camera pretty much does not face towards the Aim Part. So now I follow the above steps do find the Center of the Camera and replace using character head with it?
Well what you’re trying to do is move the aim part to the center of the screen. So to center it to the screen you’d need to center the pixels on the screen. Instead of the character’s head position you would replace it with the ray’s end point. This will perfectly center the aim part on the screen.
local worldPoint = char:FindFirstChildOfClass("Tool").ADS.Position
local vector, inViewport = cam:WorldToViewportPoint(worldPoint)
local viewportPoint = Vector2.new(vector.X, vector.Y)
local depth = vector.Z
Sorry my bad the pixel difference isn’t necessary. You just need to use ScreenPointToRay at the screen’s center position and the depth you got previously. (The screen center in pixels is ScreenGui.AbsoluteSize/2)
local ray = cam:ScreenPointToRay(mouse.X, mouse.Y, 1)
local offset = char:FindFirstChildOfClass("Tool").ADS.CFrame:toObjectSpace(CFrame.new(ray.Origin, ray.Origin + ray.Direction)).Position
I’ve already achieved the Screen Center position in Vector3, but seems that it’s still not reaching the position, do I have to do something with this?
Edit: Solved it!
This took me sometime to discover about the XYZ values, when I multiply C0’s value to offset it, it will position incorrectly if you put in XYZ format, probably due to the orientation of different parts, here’s my final code:
local ray = cam:ScreenPointToRay(mouse.X, mouse.Y, 1)
local offset = char:FindFirstChildOfClass("Tool").ADS.CFrame:toObjectSpace(CFrame.new(ray.Origin, ray.Origin + ray.Direction)).Position
local offsetX = offset.X
local offsetY = offset.Y
local offsetZ = offset.Z
char.Torso.ToolGrip.C0 = char.Torso.ToolGrip.C0 * CFrame.new(offsetX,offsetY,offsetZ)
ViewModelTorso["Left Shoulder"].C0 = ViewModelTorso["Left Shoulder"].C0 * CFrame.new(offsetZ,offsetY,-offsetX)
ViewModelTorso["Right Shoulder"].C0 = ViewModelTorso["Right Shoulder"].C0 *CFrame.new(-offsetZ,offsetY,offsetX)
I changed the formula from C0 = C0 * offset into C0 = offeset, where offset is the Vector3 value caculated by using ToObjectSpace (With the method you provided and suggested above) and I ran it inside a RunService RenderStepped.
Can you explain the difference between both formula? I find something really confusing, whenever I change the offset from the old formula (C0 = C0 * offset) into something like (C0 = C0 * 0.1,0,0), it will keep multiplying and eventually the tool will be out of the screen since it’s ran inside a RenderStepped and I keep multiplying 0.1 by every step, but how come this doesn’t happen when I multiply the offset?
When I use the new formula (C0 = offset), this happens. The C0 keep resetting. I checked there’s no code to reset the C0 back to it’s initial CFrame (0,0,0). When I implement a debounce which pause them from running every step, this stopped happening.
For unknown reasons, whenever I use the formula and I change the offset value to something that’s not offset (Such as 0.1,0,0), it will offset the arms and it went completely off.
The C0 property offsets Part1 from Part0. The C1 property subtracts from that offset. So the position of Part1 is equal to this: Part0.CFrame * C0 * C1:Inverse()
When doing C0 = C0 * x you constantly add that offset on top of itself meaning the offset will jump to infinity. This is because C0 is not reset.
Multiplying a CFrame acts like adding except it’s relative to the target’s direction. (-Z is forwards, Positive Y is upwards, and positive X is left)