Character Torso Bending

I’m working to create a gun/character system somewhat similar to ArmA 3.
Something important to note about ArmA 3 is how when you look down, your characters torso bends forward to point his weapon around.

I want to recreate this. How could I even go about with making my characters torso bend depending on the cameras look angle?

I should note that the system will be based on R15

2 Likes

Hint


Use Motor6D.Transform(). However I do not know exactly how it works.

You can use Motor6D to make this work.
You will need to work with C0 and C1 to make it work.
You can click the Motor6D above to read more on how it works.

With R15, the UpperTorso’s Waist joint will allow you to rotate the body around to your liking.
You can adjust the C0 to be able to rotate it around to the desired rotation.
But how?
Well, somehow we need to rotate the humanoid to the camera’s look vector Y position.
We will have to start off the code with a variable, which will function as the camera’s lookvector Y.
local cameralookvectorY = workspace.CurrentCamera.CFrame.LookVector.Y
Now we have a variable which will show what our character wants to rotate to.
Now, we need to use math.asin to be able to find our desired radians. We can do this by creating a variable that represents math.asin(), with x being cameralookvectorY. This is what it should look like now.
local cameralookvectorY = workspace.CurrentCamera.CFrame.LookVector.Y
local radian = math.asin(cameralookvectorY)
Now that we have the radian, we are now able to rotate the waist. We can use CFrame.fromEulerAnglesYXZ() to rotate the waist, with rx being the radian. We will now also have another variable representing the C0 of the waist. This is an example of what it should look like in a localscript:
local c0 = game.Players.LocalPlayer.Character:WaitForChild("UpperTorso"):WaitForChild("Waist").C0
function updatewaist()
local cameralookvectorY = workspace.CurrentCamera.CFrame.lookVector.Y
local radian = math.asin(cameralookvectorY)
game.Players.LocalPlayer.Character.UpperTorso.Waist.C0 = c0*CFrame.fromEulerAnglesYXZ(radian,0,0)
end
Hope I helped!

9 Likes

Transform is a property, not a function. Transform also does not replicate for performance reasons and it has to be set every frame due to overriding behaviour by anything that internally uses Transform (e.g. animations).

1 Like

Like this?


I used this code I got from a tutorial on making FPS (I modified it a little):

local player = game:GetService("Players").LocalPlayer

local camera = workspace.CurrentCamera

player.Character:WaitForChild("Head",10)

local cam = Instance.new("Part")

cam.Name = "Cam"

cam.Parent = player

cam.Transparency = 0

cam.CanCollide = false

cam.Anchored = true

local neckC0 = CFrame.new(0, 0.8, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)

local waistC0 = CFrame.new(0, 0.2, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)

game:GetService("RunService").RenderStepped:Connect(function()

cam.CFrame = camera.CFrame

script.TiltEvent:FireServer(math.asin(cam.CFrame.LookVector.Y))

local neck = player.Character.Head.Neck

local waist = player.Character.UpperTorso.Waist

neck.C0 = neckC0 * CFrame.fromEulerAnglesYXZ(math.asin(cam.CFrame.LookVector.Y)*1, 0, 0)

waist.C0 = waistC0 * CFrame.fromEulerAnglesYXZ(math.asin(cam.CFrame.LookVector.Y)*1, 0, 0)

end)

Here’s the link to the tutorial by @EgoMoose:

6 Likes

Make sure to mark one answer as the solution when it helps you find what you are looking for.

1 Like

Would constantly firing the tilt event be practical?

No, it wouldn’t, neither is updating the joints on the server in order to replicate to all clients. Great that this concern was made. Even in the thread above where the code was taken from, the author made an amendment to address the issue, however i’m not too fond of updating the joints on the server, since the server has to update the joint itself and then replicate the changes to all of the clients.

The server should just be for validation/verification of data, while all of the clients are just in charge of visuals. The idea is to have the client firing the remote every few seconds (like 0.1 seconds) if there’s a charge in the angle and have the server fire the event to all of the clients to interpolate to the desired angle. Here’s some code:

-- server

RemoteEvent.OnServerEvent:Connect(function(player, angle)
    RemoteEvent:FireAllClients(player, angle)
end)

-- client

RemoteEvent.OnClientEvent:Connect(function(player, angle)
    local character = player.Character
    if character then
        local neck = character.Head.Neck
        local waist = character.UpperTorso.Waist

        neck.C0 = neck.C0:lerp(neckC0 * CFrame.fromEulerAnglesYXZ(angle, 0, 0), 0.05)
        waist.C0 = waist.C0:lerp(waistC0 * CFrame.fromEulerAnglesYXZ(angle, 0, 0), 0.05)
    end
end)

local last = nil

while true do
    local current_angle = math.asin(cam.CFrame.LookVector.Y)
    if not last or math.abs(current_angle - last) > 0.005 then
        RemoteEvent:FireServer(current_angle)
        last = current_angle
    end
    task.wait(0.1)
end
3 Likes