I want the character’s torso to bend up or down based on the position of the mouse, similar to this post.
There is plenty of code out there that deals with this, however, all of it is for R15 characters.
I’ve looked around online as well, and nothing has worked so far, so I’m asking for help with basically zero knowledge on the subject.
Its because moving the torso (like on the R15 rig) is impossible on the R6 Rig, as there is no waist joint.
The closest effect you’ll get with an R6 Rig is moving the head (and arms if youre making a gun game)
Yeah it’s more difficult for R6 like @Chark_Proto said.
However it’s still possible,
The issue is changing the rotation Torso motor changes the rotation of the legs.
However, to counteract this I have added an inverse rotation so it works.
The script I adapted is from @CleverSource in this post, a lot of things needed to be changed as well such as the CFrame order with rotation * OriginalC0 in order to apply the rotation in the correct direction.
Edit: Whoops forgot the .Rotation and + .Position which made the head move around
Code:
local RunService = game:GetService("RunService")
local Player = game.Players.LocalPlayer
local PlayerMouse = Player:GetMouse()
local Camera = workspace.CurrentCamera
local Character = Player.Character or Player.CharacterAdded:Wait()
--Old R15 code
local Head = Character:WaitForChild("Head")
--local Neck = Head:WaitForChild("Neck")
--local Torso = Character:WaitForChild("UpperTorso")
--local Waist = Torso:WaitForChild("Waist")
--New R6 stuff
local Humanoid = Character:WaitForChild("Humanoid")
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Torso = Character:WaitForChild("Torso")
local Neck = Torso:WaitForChild("Neck")
local Waist = HumanoidRootPart:WaitForChild("RootJoint")
local RHip = Torso:WaitForChild("Right Hip")
local LHip = Torso:WaitForChild("Left Hip")
local LHipOriginC0 = LHip.C0
local RHipOriginC0 = RHip.C0
local NeckOriginC0 = Neck.C0
local WaistOriginC0 = Waist.C0
Neck.MaxVelocity = 1/3
RunService.RenderStepped:Connect(function()
local CameraCFrame = Camera.CoordinateFrame
if Character:FindFirstChild("Torso") and Character:FindFirstChild("Head") then
local TorsoLookVector = Torso.CFrame.lookVector
local HeadPosition = Head.CFrame.p
if Neck and Waist then
if Camera.CameraSubject:IsDescendantOf(Character) or Camera.CameraSubject:IsDescendantOf(Player) then
local Point = PlayerMouse.Hit.p
local Distance = (Head.CFrame.p - Point).magnitude
local Difference = Head.CFrame.Y - Point.Y
local goalNeckCFrame = CFrame.Angles(-(math.atan(Difference / Distance) * 0.5), (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 1, 0)
Neck.C0 = Neck.C0:lerp(goalNeckCFrame*NeckOriginC0, 0.5 / 2).Rotation + NeckOriginC0.Position
local xAxisWaistRotation = -(math.atan(Difference / Distance) * 0.5)
local yAxisWaistRotation = (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 0.5
local rotationWaistCFrame = CFrame.Angles(xAxisWaistRotation, yAxisWaistRotation, 0)
local goalWaistCFrame = rotationWaistCFrame*WaistOriginC0
Waist.C0 = Waist.C0:lerp(goalWaistCFrame, 0.5 / 2).Rotation + WaistOriginC0.Position
local currentLegCounterCFrame = Waist.C0*WaistOriginC0:Inverse()
local legsCounterCFrame = currentLegCounterCFrame:Inverse()
RHip.C0 = legsCounterCFrame*RHipOriginC0
LHip.C0 = legsCounterCFrame*LHipOriginC0
end
end
end
end)
This is great, however, I’m wondering how I would be able to replicate this change to the server without exhausting a remote event?
Thanks,
To replicate this change you can just send the mouse position over and then fire all clients perhaps in a loop every 1 or 0.2 seconds
while task.wait(0.2) do
someRemote:Fire(mouse.Hit.Position)
To replicate you can run the code on the client for each players
Instead of using .LocalPlayer use the player from game.Players.PlayerAdded then run the code for every .CharacterAdded.
Instead of using the local mouse use a dictionary containing the mouse position per player that is received from the server.
Initially since there is no mouse position you can default to Vector3.new(0,0,0) as a fail safe or a position in front of the character.
local playerMousePositionDictionary = {}
remote.OnClientEvent(function(playerThatFired, mousePosition)
playerMousePositionDictionary [playerThatFired] = mousePosition
end)
--In the renderstep code for each player that joins
game.PlayerAdded:Connect(function(player)
local Point = playerMousePositionDictionary[player] or Vector3.new(0,0,0)
end))
Update on this, having
game.Players.PlayerAdded:Connect(function(player)
Point = playerMousePositionDictionary[player] or Vector3.new(0,0,0)
end)
in runservice still continues to return nil for Point
.
The code so far is structured very similar to how egomoose did it in this post which I had mentioned before.
My method of replication that im thinking of is a bit different then EgoMoose.
The main idea im going for is having the local player change the C0 of the other players for replication.
A reference for this is what I did for my R6 IKPF system with player added and character added.
EgoMooses method is to change it directly on server which also works.
I can show it when I get the time im outside rn.
I tried having a look at your code for your R6 IKPF system, it’s a lot and it’s quite overwhelming.
Could you give the main gist of how I could implement the changes egomoose’s way?
So I’ve managed to stitch the code together to work, but the fact that it fires the remote event every heartbeat kinda worries me in terms of game performance.
I’m hoping you’ll actually reply with how you would do it with your own method.
Server
-- Game services
local RunService = game:GetService("RunService")
local Point
local playerMousePositionDictionary = {}
script.Parent:WaitForChild("RemoteEvent").OnServerEvent:Connect(function(playerThatFired, mousePosition)
playerMousePositionDictionary [playerThatFired] = mousePosition
end)
script.Parent:WaitForChild("SetUp").OnServerEvent:Connect(function(Player, CamCFrame, CamSubject)
local Character = Player.Character or Player.CharacterAdded:Wait()
--Old R15 code
local Head = Character:WaitForChild("Head")
--local Neck = Head:WaitForChild("Neck")
--local Torso = Character:WaitForChild("UpperTorso")
--local Waist = Torso:WaitForChild("Waist")
--New R6 stuff
local Humanoid = Character:WaitForChild("Humanoid")
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Torso = Character:WaitForChild("Torso")
local Neck = Torso:WaitForChild("Neck")
local Waist = HumanoidRootPart:WaitForChild("RootJoint")
local RHip = Torso:WaitForChild("Right Hip")
local LHip = Torso:WaitForChild("Left Hip")
local LHipOriginC0 = LHip.C0
local RHipOriginC0 = RHip.C0
local NeckOriginC0 = Neck.C0
local WaistOriginC0 = Waist.C0
RunService.Heartbeat:Connect(function(dt)
--game.Players.PlayerAdded:Connect(function(player)
Point = playerMousePositionDictionary[Player] or Vector3.new(0,0,0)
--end)
local CameraCFrame = CamCFrame
if Character:FindFirstChild("Torso") and Character:FindFirstChild("Head") then
local TorsoLookVector = Torso.CFrame.lookVector
local HeadPosition = Head.CFrame.p
if Neck and Waist then
if CamSubject:IsDescendantOf(Character) or CamSubject:IsDescendantOf(Player) then
local Distance = (Head.CFrame.p - Point).magnitude
--print(Point)
local Difference = Head.CFrame.Y - Point.Y
local goalNeckCFrame = CFrame.Angles(-(math.atan(Difference / Distance) * 0.5), (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 1, 0)
Neck.C0 = Neck.C0:lerp(goalNeckCFrame*NeckOriginC0, 0.5 / 2).Rotation + NeckOriginC0.Position
local xAxisWaistRotation = -(math.atan(Difference / Distance) * 0.5) -- Change 0.5 to change amount of rotation
local yAxisWaistRotation = (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 0.5
local rotationWaistCFrame = CFrame.Angles(xAxisWaistRotation, yAxisWaistRotation, 0)
local goalWaistCFrame = rotationWaistCFrame*WaistOriginC0
Waist.C0 = Waist.C0:lerp(goalWaistCFrame, 0.5 / 2).Rotation + WaistOriginC0.Position
local currentLegCounterCFrame = Waist.C0*WaistOriginC0:Inverse()
local legsCounterCFrame = currentLegCounterCFrame:Inverse()
RHip.C0 = legsCounterCFrame*RHipOriginC0
LHip.C0 = legsCounterCFrame*LHipOriginC0
end
end
end
end)
end)
Local
local RunService = game:GetService("RunService")
local Camera = workspace.CurrentCamera
local mouse = game.Players.LocalPlayer:GetMouse()
script:WaitForChild("SetUp"):FireServer(Camera.CFrame, Camera.CameraSubject)
RunService.Heartbeat:Connect(function(dt)
script:WaitForChild("RemoteEvent"):FireServer(mouse.Hit.Position)
end)
Here is my method which does it on the client. Which sends the data at a rate of every 0.2 seconds.
The advantage of this method is that all the C0 stuff happens on the client and hence is not burdened by the server.
Cons is that the server does not replicate so hitboxes and stuff might be a little off.
However another issue I found is that the interpolation done by the lerping is not as smooth since there is a delay in mouse.Position of 0.2 seconds perhaps delta time or the lerp alpha number should be changed.
--StarterPlayerScripts
--Only animates the C0 for other players keep another local script to animate the local player character
--Handle Replication Data
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RE = ReplicatedStorage:WaitForChild("RemoteEvent")
local PlayersMouseData = {}
local RE2 = ReplicatedStorage:WaitForChild("OnClient")
RE2.OnClientEvent:Connect(function(player, mousePosData)
--Make sure to clean this up to prevent memory leak with OnPlayerRemoved set data to nil
PlayersMouseData[player] = mousePosData
end)
--Use Replication Data
local Players = game:GetService("Players")
local localPlayer = Players.LocalPlayer
local function initRunService(somePlayer)
local RunService = game:GetService("RunService")
local Character = somePlayer.Character or somePlayer.CharacterAdded:Wait()
local Head = Character:WaitForChild("Head")
local Humanoid = Character:WaitForChild("Humanoid")
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Torso = Character:WaitForChild("Torso")
local Neck = Torso:WaitForChild("Neck")
local Waist = HumanoidRootPart:WaitForChild("RootJoint")
local RHip = Torso:WaitForChild("Right Hip")
local LHip = Torso:WaitForChild("Left Hip")
local LHipOriginC0 = LHip.C0
local RHipOriginC0 = RHip.C0
local NeckOriginC0 = Neck.C0
local WaistOriginC0 = Waist.C0
Neck.MaxVelocity = 1/3
local connection
connection = RunService.RenderStepped:Connect(function(deltaTime)
if Character.Parent == nil then
--clean up function
connection:Disconnect()
end
if Character:FindFirstChild("Torso") and Character:FindFirstChild("Head") then
local TorsoLookVector = Torso.CFrame.lookVector
local HeadPosition = Head.CFrame.p
if Neck and Waist then
local Point = PlayersMouseData[somePlayer] or Vector3.new(0,0,0)
assert(Point, "What its missing")
local Distance = (Head.CFrame.p - Point).magnitude
local Difference = Head.CFrame.Y - Point.Y
local goalNeckCFrame = CFrame.Angles(-(math.atan(Difference / Distance) * 0.5), (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 1, 0)
Neck.C0 = Neck.C0:lerp(goalNeckCFrame*NeckOriginC0, 0.5 / 2).Rotation + NeckOriginC0.Position
local xAxisWaistRotation = -(math.atan(Difference / Distance) * 0.5)
local yAxisWaistRotation = (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 0.5
local rotationWaistCFrame = CFrame.Angles(xAxisWaistRotation, yAxisWaistRotation, 0)
local goalWaistCFrame = rotationWaistCFrame*WaistOriginC0
Waist.C0 = Waist.C0:lerp(goalWaistCFrame, 0.5 / 2).Rotation + WaistOriginC0.Position
local currentLegCounterCFrame = Waist.C0*WaistOriginC0:Inverse()
local legsCounterCFrame = currentLegCounterCFrame:Inverse()
RHip.C0 = legsCounterCFrame*RHipOriginC0
LHip.C0 = legsCounterCFrame*LHipOriginC0
end
end
end)
end
local playerAdded = function(player)
if localPlayer == player then
return
end
player.CharacterAdded:Connect(function(character)
initRunService(player)
end)
end
Players.PlayerAdded:Connect(playerAdded)
for i, v in pairs(Players:GetPlayers()) do
playerAdded(v)
end
--Begin transmitting data
local PlayerMouse = localPlayer:GetMouse()
while true do
RE:FireServer(PlayerMouse.Hit.Position)
print("Fire serverrr", RE)
task.wait(0.2)
end
Server Script:
local Players = game:GetService("Players")
local function fireAllClientsExcept(remote, exceptionPlayer, ...)
for _, player in pairs(Players:GetPlayers()) do
if player == exceptionPlayer then
continue
end
remote:FireClient(player, ...)
end
end
--local RE = Instance.new("RemoteEvent", game.ReplicatedStorage)
--local RE2 = Instance.new("RemoteEvent")
--RE2.Name = "OnClient"
--RE2.Parent = game.ReplicatedStorage
local RE = game.ReplicatedStorage.RemoteEvent
local RE2 = game.ReplicatedStorage.OnClient
RE.OnServerEvent:Connect(function(player, mousePosition)
fireAllClientsExcept(RE2, player, player, mousePosition)
print("Onserver event")
end)
looks really good, but how can I make it so the torso isn’t turning with the mouse, but instead, the camera?
Thanks (With credits to CleverSource as well for the initial script),
To make it turn with the camera you can replace the look at point which is the mouse position obtained from mouse.Hit in the original line of code:
And replace that with this do this instead:
local camera = workspace.CurrentCamera
local cameraCF = camera.CFrame
local cameraPoint = (HumanoidRootPart.CFrame*CFrame.new(0,1,0)).Position + cameraCF.LookVector*5 --Position new look at point at around the original head location and move it where it is looking in front by 5 studs.
--You can adjust the 5
Sorry, I’m a major beginner, where do I replace that code? And what do I replace it with?
i wonder do you use local script or just script?
Very good code! I have somewhat cleaned it up but in camelCase and I don’t just want to leech of this project so, I’ll post what I did here.
It is starterCharacterScript so therefore it gets cleaned up when character gets reset. No need for Disconnecting or stuff like that.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local localPlayer = Players.LocalPlayer
local mouse = localPlayer:GetMouse()
local camera = workspace.CurrentCamera
local character = script.Parent
local head = character:WaitForChild("Head")
local rootPart = character:WaitForChild("HumanoidRootPart")
local torso = character:WaitForChild("Torso")
local neckMotor = torso:WaitForChild("Neck")
local waistMotor = rootPart:WaitForChild("RootJoint")
local rightHipMotor = torso:WaitForChild("Right Hip")
local leftHipMotor = torso:WaitForChild("Left Hip")
local neckMotorOrigin0 = neckMotor.C0
local waistMotorOrigin0 = waistMotor.C0
local leftHipMotorOrigin0 = leftHipMotor.C0
local rightHipMotorOrigin0 = rightHipMotor.C0
neckMotor.MaxVelocity = 1/3
local function updateCharacterMouseFollow(deltaTime)
local cameraCFrame = camera.CoordinateFrame
if torso and head and neckMotor and waistMotor then
-- //You could add check if its Parented to nil, because thats what happens when its destroyed,
-- //Or you could keep it like this so the code doesn't error, and will work with other yet parented motors.
-- //https://devforum.roblox.com/t/frame-rate-independent-lerp/352375/23
local lerpAlpha = 1 - math.exp(-6 * deltaTime)
local torsoLookVector = torso.CFrame.lookVector
local headPosition = head.CFrame.p
local mouseHitPosition = mouse.Hit.p
local mouseHitHeadDist = (head.CFrame.p - mouseHitPosition).magnitude
local YDifference = head.CFrame.Y - mouseHitPosition.Y
local YDiffmouseHeadDistUnit = (headPosition - mouseHitPosition).Unit
local XWaistRotation = -(math.atan(YDifference / mouseHitHeadDist) * 0.5)
local YWaistRotation = (YDiffmouseHeadDistUnit:Cross(torsoLookVector)).Y * 0.5
local rotationWaistCFrame = CFrame.Angles(XWaistRotation, YWaistRotation, 0)
local goalWaistCFrame = rotationWaistCFrame * waistMotorOrigin0
local currentLegCounterCFrame = waistMotor.C0 * waistMotorOrigin0:Inverse()
local legsCounterCFrame = currentLegCounterCFrame:Inverse() * CFrame.Angles(0, math.rad(180), 0)
-- //I had issue with legs being rotated in opposite direction therefore I added ^. It fixes it for me.
local goalNeckCFrame = CFrame.Angles(
-(math.atan(YDifference / mouseHitHeadDist) * 0.5),
YDiffmouseHeadDistUnit:Cross(torsoLookVector).Y * 1,
0
)
neckMotor.C0 = neckMotor.C0:lerp(goalNeckCFrame * neckMotorOrigin0, lerpAlpha).Rotation + neckMotorOrigin0.Position
waistMotor.C0 = waistMotor.C0:lerp(goalWaistCFrame, lerpAlpha).Rotation + waistMotorOrigin0.Position
rightHipMotor.C0 = legsCounterCFrame * leftHipMotorOrigin0
leftHipMotor.C0 = legsCounterCFrame * rightHipMotorOrigin0
end
end
RunService.RenderStepped:Connect(updateCharacterMouseFollow)
Hey, even though this is quite old; How can I manage to change it so instead of it getting the Mouse location, it just gets where the player is looking at, like how you described to @unsinfulways
Just a follow up, if you or someone could help me change it so instead of it getting the players mouse, it gets where the player looks like in the main script. It’ll be better if possible.
just replace the mouse parts with camera.lookvector