Motor6D Transform property isn't changing to mouse position

  1. What do you want to achieve? Keep it simple and clear!

I want the players right arm to point towards the mouse. I am using RemoteEvents to pass the mouse.Hit.Position to the server and then change the Right Shoulder’s Transform property to where the mouse is positioned.

  1. What is the issue? Include screenshots / videos if possible!

It isn’t working. The player’s arm is not moving on the client nor the server. I checked if the event is firing and the function is working, and it says it is.


  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I tried using a wait() function to stop the RemoteEvent from constantly firing, but of course that didn’t work since I’m using RunService.Stepped. I couldn’t find any posts about this.

Local Script:

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

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

local tool = script.Parent

local steppedConn
tool.Equipped:Connect(function()
	steppedConn = RunService.Stepped:Connect(function()
		local pointRemote = game.ReplicatedStorage:WaitForChild("pointRemote")
		local char = plr.Character or plr.CharacterAdded:Wait()
		local mouse = mouse.Hit
		pointRemote:FireServer(char, mouse)
		print("Event fired")
	end)
end)
tool.Unequipped:Connect(function()
	steppedConn:Disconnect()
end)

Server Script:

local a  = Instance.new("RemoteEvent", game.ReplicatedStorage)
a.Name = "pointRemote"

local YAW_MIN = math.rad(-70)
local YAW_MAX = math.rad(70)
local PITCH_MIN = math.rad(-30)
local PITCH_MAX = math.rad(30)

a.OnServerEvent:Connect(function(player, char, mouse)
	local rsh = char:WaitForChild("Torso"):WaitForChild("Right Shoulder")
	
	local jointWorld = rsh.Part0.CFrame * rsh.C0
	
	local dirLocal = jointWorld:PointToObjectSpace(mouse.Position)
	
	local yaw = math.atan2(-dirLocal.Z, dirLocal.X)
	local pitch = math.asin(dirLocal.Unit.Y)
	
	local yawClamped = math.clamp(yaw, YAW_MIN, YAW_MAX)
	local pitchClamped = math.clamp(pitch, PITCH_MIN, PITCH_MAX)
	
	rsh.Transform = CFrame.fromOrientation(0, yawClamped, pitchClamped + math.pi/2)
	print("Right Shoulder Transform property changed!")
end)

I think the problem is the limitations of using RemoteEvents. The mouse is always moving, so it will always be firing the server.

Hi again :slight_smile:

From Motor6D | Documentation - Roblox Creator Hub :

NotReplicated

This item is not replicated across Roblox’s server/client boundary.

But, from Animator | Documentation - Roblox Creator Hub :

If an Animator is a descendant of a Humanoid or AnimationController in a Player’s Character then animations started on that Player’s client will be replicated to the server and other clients.

So

  1. Server sets Transform for itself (not replicated to any clients though)
  2. Client’s Animator overrides Transform to be “arm straight in tool holding position”
  3. Client’s transform is replicated to the server, overwriting (1)

Solutions

  1. Use C1 instead of Transform on the server because that is replicated. You’ll need to tweak the math a little, though, and reset it when you unequip. Also animations will still play on the arm (not a huge problem when holding a tool because the arm’s pretty still).

  2. Using the server as a middleman, tell every client about every other client’s mouse positions and do the Transform update on the client, but do it for every character in workspace! Clients can calculate their own angles and just send the angles to reduce load (or the server could do it). You can throttle the data or even lerp between angles so its not so terrible performance-wise.

  3. If it’s just a non-critical visual effect, don’t bother going to the server at all. The default Tool animation sticks the arm straight out anyways—most players won’t care that other people’s arms don’t move as finely as theirs do.

I personally recommend 3 :slight_smile:

1 Like

You’ll need to tweak the math a little, though, and reset it when you unequip.

Do you mean to change this:

local YAW_MIN = math.rad(-70)
local YAW_MAX = math.rad(70)
local PITCH_MIN = math.rad(-30)
local PITCH_MAX = math.rad(30)

…or change the code that actually calculates the angles?

Also, you don’t have to provide an answer for this because I feel like I am asking for too much, but do you know how to actually reset the Right Shoulder’s Motor6D? Or at least the default values?

Nah just swap around the arguments in fromOrientation and maybe also switch it to fromAngles. You’ll also need to multiply by the original C1 to get the offset right.

You’d also need to know the original C1 so you can set it back to that when the tool is unequipped. If you go with setting the C1 on the server, that also means you need another remote that resets the C1.

Edit: I can’t get the angles to work out exactly but originalC1 * CFrame.Angles(-yawClamped, 0, -pitchClamped seems to be close.

1 Like

I’m getting really confused here. :sweat_smile:

Could you just send your code?

Well, here’s a client-only example (I figured out the math)

It gets pretty annoying pretty quick if you do it on the server though… Which is why I suggest option 3 :slight_smile:

local YAW_MIN = math.rad(-70)
local YAW_MAX = math.rad(70)
local PITCH_MIN = math.rad(-30)
local PITCH_MAX = math.rad(30)

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local tool = script.Parent

local conn, c1

tool.Equipped:Connect(function()
	local rsh = tool.Parent.Torso["Right Shoulder"]
	c1 = rsh.C1
	
	if conn then conn:Disconnect() end
	
	conn = game:GetService("RunService").Stepped:Connect(function()
		local jointWorld = rsh.Part0.CFrame * rsh.C0

		local dirLocal = jointWorld:PointToObjectSpace(mouse.Hit.Position)

		local yaw = math.atan2(-dirLocal.Z, dirLocal.X)
		local pitch = math.asin(dirLocal.Unit.Y)

		local yawClamped = math.clamp(yaw, YAW_MIN, YAW_MAX)
		local pitchClamped = math.clamp(pitch, PITCH_MIN, PITCH_MAX)

		rsh.C1 = c1 * CFrame.Angles(0, 0, -pitchClamped) * CFrame.Angles(-yawClamped, 0, 0)
		print("Right Shoulder Transform property changed!")
	end)
end)

tool.Unequipped:Connect(function()
	if conn then
		conn:Disconnect()
		conn = nil
	end
	if c1 then
		local rsh = tool.Parent.Torso["Right Shoulder"]
		rsh.C1 = c1
	end
end)

Edit: also you can do local yaw = math.asin(-dirLocal.Z) instead of atan2 if you want smoother movement when the mouse is behind you

1 Like

I actually managed to get it working on the server. Thanks!

@nicemike40 I don’t want to use RunService.Stepped since because it’s firing the RemoteEvent at every frame, and it’s gonna fire too many times.

Are you able to instead execute the function every second using a wait()? What would I use instead of RunService.Stepped?
(I don’t know if I sound dumb but I think you get what I’m saying)

You could keep using stepped and just do a check to see when the last time you called it was

local lastCalled = 0
RunService.Stepped:Connect(function(time)
    if time - lastCalled > 1 then -- one sec
        -- do thing
        lastCalled = time
    end
end)

But a nicer way would be option (2) — do the update on your client every frame and send your angles to the server occasionally, who then forwards that info to other clients to update your character on their screen

1 Like

Alright, thanks! I really appreciate all the help on this, dude. :grin:

1 Like