Help rotating arm correctly

Hi everyone,

I’d like to make the player’s arm rotate to face their mouse, but it seems to be rotating from the middle of the torso and not the top like I’d like.

image

I’m pretty unfamiliar with welds, so I don’t know how to offset the origin.

Here’s the LocalScript:

wait(2)

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local plr = Players.LocalPlayer
local char = plr.Character
local mouse = plr:GetMouse()

local armOffset = char.Torso.CFrame:Inverse() * char["Right Arm"].CFrame

local armWeld = Instance.new("Weld")
armWeld.Part0 = char.Torso
armWeld.Part1 = char["Right Arm"]
armWeld.Parent = char

RunService.Heartbeat:Connect(function()
	local cframe = CFrame.new(char.Torso.Position, mouse.Hit.Position) * CFrame.Angles(math.pi/2, 0, 0)
	armWeld.C0 = armOffset * char.Torso.CFrame:toObjectSpace(cframe)
end)

Also, I’d like to have player arm rotation to be visible for all players. Is there any way to do that?

Thank you!

1 Like

First of all: Rotate Head to Camera Direction! - Roblox (Motor6D CFrame and Trigonometry) - YouTube

Secondly, player rigs have arms and legs are already connected with motor6D’s, you can edit the C0 and C1 values instead of having to create welds. I hope you can use this video and apply it on the arm!

Unfortunately this positions the arm in the middle of the torso and it pivots around that.

then offset it where it needs to be.

When I add the offset, the arm still pivots around the middle of the torso.

Could be because

  • not enough offset
  • offset in the wrong direction
    try debugging by printing the pivot position of the arm and varying the offset to see if the offset works…

I tried this offset:

	local armOffset = CFrame.new(1,0,0)
	
	local cframe = CFrame.new(torso, mouse) * CFrame.Angles(math.pi/2, 0, 0) * armOffset

It works decent when I point forwards:
image

However, not as much when I point backwards.
image

Also, how do I find the pivot?

In the backwards setting, it looks like the pivot changed - is this intentional?

No, it all seems to be rotating around the center of the player’s torso.

It doesn’t look like this in the front setting though…?
In the front setting, I’ll assume that this is the correct pivot position?

To offset the origin of the arm, you will need to adjust the armWeld.C0 value. The C0 property represents the position and orientation of the arm relative to the torso.

To rotate the arm correctly, you can use the CFrame.new function to create a new coordinate frame based on the position of the torso and the position of the mouse hit. You can then use the * CFrame.Angles expression to apply additional rotations to the arm.

To make the arm rotation visible to all players, you will need to make sure that the changes to the armWeld.C0 value are replicated to the server. One way to do this is to use the Changed event on the armWeld object to trigger a function that updates the value on the server.

Here is an example of how you could modify your script to achieve this.

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local plr = Players.LocalPlayer
local char = plr.Character
local mouse = plr:GetMouse()

local armOffset = char.Torso.CFrame:Inverse() * char["Right Arm"].CFrame

local armWeld = Instance.new("Weld")
armWeld.Part0 = char.Torso
armWeld.Part1 = char["Right Arm"]
armWeld.Parent = char

-- Function to update the armWeld.C0 value on the server
local function updateArmWeld(cframe)
	-- Send a message to the server with the new armWeld.C0 value
	local message = {
		Type = "UpdateArmWeld",
		Value = cframe,
	}
	game:GetService("ReplicatedStorage").UpdateArmWeld:FireServer(message)
end

-- Event that triggers when the armWeld.C0 value changes
armWeld.Changed:Connect(function(property)
	if property == "C0" then
		updateArmWeld(armWeld.C0)
	end
end)

RunService.Heartbeat:Connect(function()
	local cframe = CFrame.new(char.Torso.Position, mouse.Hit.Position) * CFrame.Angles(math.pi/2, 0, 0)
	armWeld.C0 = armOffset * char.Torso.CFrame:toObjectSpace(cframe)
end)

This script will send a message to the server every time the armWeld.C0 value is changed, which will allow the server to replicate the changes to all clients. You will need to set up a corresponding event on the server to handle these messages and update the armWeld.C0 value on the server.

It still is not the correct pivot position, it’s just that the offset is putting it in the right position. Here’s a video.

In the video, you can see that the arm is rotating around the center of the player’s torso.

I see what the issue is!
The offset you have applied is constant, meaning that if you move your mouse to make it face backwards, the offset you have applied will, effectively, work in reverse. Kind of like a mirror.

Ah!

So, if I understand correctly, I have to find the shoulder weld and update it in a ServerScriptService script after the event is fired?

Here is a detailed explanation of the script.

First, we declare some local variables to store references to the Players service, the RunService service, the local player, the local player’s character, and the mouse object:

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local plr = Players.LocalPlayer
local char = plr.Character
local mouse = plr:GetMouse()

Next, we calculate the armOffset variable, which represents the position and orientation of the right arm relative to the torso:

local armOffset = char.Torso.CFrame:Inverse() * char["Right Arm"].CFrame

This allows us to apply the correct rotation to the arm when it is attached to the torso using the weld object.

Next, we create a new Weld object and set its Part0 and Part1 properties to the torso and the right arm, respectively. We also set the Parent property of the weld object to the character:

local armWeld = Instance.new("Weld")
armWeld.Part0 = char.Torso
armWeld.Part1 = char["Right Arm"]
armWeld.Parent = char

This will attach the right arm to the torso using the weld object.

Next, we define a function called updateArmWeld that takes a cframe argument and sends a message to the server with the new armWeld.C0 value:

-- Function to update the armWeld.C0 value on the server
local function updateArmWeld(cframe)
	-- Send a message to the server with the new armWeld.C0 value
	local message = {
		Type = "UpdateArmWeld",
		Value = cframe,
	}
	game:GetService("ReplicatedStorage").UpdateArmWeld:FireServer(message)
end

This function will be called every time the armWeld.C0 value is changed, which will allow us to replicate the changes to all clients.

Next, we use the Changed event on the armWeld object to trigger a function that updates the value on the server. The Changed event fires every time a property of the armWeld object is changed. In this case, we are interested in the C0 property, which represents the position and orientation of the right arm relative to the torso:

-- Event that triggers when the armWeld.C0 value changes
armWeld.Changed:Connect(function(property)
	if property == "C0" then
		updateArmWeld(armWeld.C0)
	end
end)

Finally, we use the RunService.Heartbeat event to update the armWeld.C0 value every frame. This will rotate the arm to follow the mouse cursor:

RunService.Heartbeat:Connect(function()
	local cframe = CFrame.new(char.Torso.Position, mouse.Hit.Position) * CFrame.Angles(math.pi/2, 0, 0)
	armWeld.C0 = armOffset * char.Torso.CFrame:toObjectSpace

I understand the localscript. It’s just I can’t modify a locally created weld with a Server Script to replicate to all clients. :sweat_smile:

I understand give me one moment, I’m in studio doing something

In order to replicate the changes made to the armWeld object to all clients, you will need to use a RemoteEvent object to send a message from the server to the clients.

Here is an example of how you could modify the script to achieve this:

First, create a RemoteEvent object in the ReplicatedStorage folder of your game, and name it UpdateArmWeld

local updateArmWeldEvent = Instance.new("RemoteEvent")
updateArmWeldEvent.Name = "UpdateArmWeld"
updateArmWeldEvent.Parent = game:GetService("ReplicatedStorage")

Next, modify the updateArmWeld function to trigger the UpdateArmWeld RemoteEvent on the server when the armWeld.C0 value is changed

-- Function to update the armWeld.C0 value on the server
local function updateArmWeld(cframe)
	-- Send a message to the server with the new armWeld.C0 value
	local message = {
		Type = "UpdateArmWeld",
		Value = cframe,
	}
	updateArmWeldEvent:FireServer(message)
end

Finally, you will need to create a Script object in the ServerScriptService folder of your game, and name it UpdateArmWeldServer . This script will be responsible for receiving the message from the client and updating the armWeld.C0 value on the server.

Here is the code for the UpdateArmWeldServer script:

local updateArmWeldEvent = game:GetService("ReplicatedStorage").UpdateArmWeld

updateArmWeldEvent.OnServerEvent:Connect(function(player, message)
	local character = player.Character
	local armWeld = character:FindFirstChild("ArmWeld")
	if armWeld then
		armWeld.C0 = message.Value
	end
end)

This script will listen for the UpdateArmWeld RemoteEvent , and when it is triggered, it will update the armWeld.C0 value on the server. This will ensure that the changes made to the armWeld object on the client are replicated to all clients.

This, indeed, mirrors the arm to all clients… however the arm still rotates from the middle of the torso. Here’s a video.

That’s correct! The updateArmWeld function and UpdateArmWeldServer script will ensure that the changes made to the armWeld object on the client are replicated to all clients. However, if you want to make the arm rotate from the shoulder rather than the middle of the torso, you will need to modify the armWeld.C0 value to reflect this.

One way to do this would be to set the armWeld.C0 value to a new CFrame value that is offset from the default CFrame value. The CFrame value represents the position and orientation of an object in 3D space, and you can use it to specify the position and orientation of the armWeld object relative to the shoulder of the character.

Here is an example of how you could modify the updateArmWeld function to set the armWeld.C0 value to a new CFrame value that is offset from the default CFrame value.

-- Function to update the armWeld.C0 value on the client
local function updateArmWeld(cframe)
	-- Set the armWeld.C0 value to a new CFrame value that is offset from the default CFrame value
	local offset = CFrame.new(0, 0.5, 0) -- Offset the armWeld by 0.5 units in the y-axis
	armWeld.C0 = cframe * offset
	
	-- Send a message to the server with the new armWeld.C0 value
	local message = {
		Type = "UpdateArmWeld",
		Value = armWeld.C0,
	}
	updateArmWeldEvent:FireServer(message)
end

This will offset the armWeld object by 0.5 units in the y-axis, which will make the arm rotate from the shoulder rather than the middle of the torso. You can adjust the offset value to change the position of the armWeld object relative to the shoulder.

I hope this helps! Let me know if you have any more questions.