How do I replicate Real Arm movements to Fake Arms (ViewModel)?

Its not worth it to make a viewmodel. it will take increase the lag, and decrease the performance, since it will capture EVERY SINGLE MOVEMENT

Just a note: OP has mentioned that they don’t want to use LocalTransparencyModifier several times now. Please remain on-topic and focus on trying to help OP. Repeating previous suggestions and telling them not to pursue their use case is counterproductive. :sweat_smile:

View models are inexpensive and many first person games currently up make use of view models. A few CFrame sets will not cause performance issues. If a view model causes lag, you aren’t doing it properly.

Please focus on trying to resolve the issue in the OP instead. :slightly_smiling_face:

1 Like

https://developer.roblox.com/en-us/api-reference/class/AlignOrientation

AlignOrientation does use attachments, one on the real arm, one on the ViewModel arm, for each arm. It’s my understanding these can be used in addition to the Motor6D you already have on the real arms. Not an expert, but if you haven’t looked into this option maybe it can work for you.

You mean RealArmCFrame * Offset and make it position to the right place?

As a starting point for the idea of how to do this, yes. Though you may want more or other combinations of CFrame operation strung together.

I imagine that each Renderstep frame, you want to copy the RealArmCFrame (rotation and orientation), but you want the position component of the ViewModel Cframe to be Offset from the camera … or something.

And then I’m just adding that you can continue adding or multiplying CFrame operations as needed to arrive at whatever position and orientation you need, if you need more. See the Understanding CFrames article and the graphic just above CFrame + or - Vector3.

What is not efficient about it? If you want the same animations, this is the most efficient way to do it.

Well, there are couple reasons. Mainly because of the arm doesn’t follow on where you look at.
Plus, fake arms are not long enough, even though you can resize it.

but you want it to, because you want them to use the same cframe, correct?

Not exactly the same CFrame, because fake arm’s CFrame are set to the Camera’s offset on every RenderStepped, I’m just wondering how do I replicate real arm’s movement to fake arms.

I think it low-key works with Motor6D.Transform! But the orientation is wrong, how do I address that?

This is most likely an issue in regards to the way that the view model is constructed. Ensure all your parts are in correct orientation and the Motor6Ds are rotated the right way not with code but the actual model used.

To ensure you have correct orientation, define the front face of the view model - this is the outwards facing surfaces of each part. Make sure that each part faces front with the left surfaces facing the left and the same for the right.

As for Motor6Ds, you can use a character rig editor for that, such as the one by DaMrNelson. You’ll need an actual R6 reference to see how the Motor6Ds are oriented to use for your view model.

If you are unsure of how to do this or someone doesn’t resolve this before I do, I can make a crude repro tomorrow regarding orientations. Otherwise, it looks to me like everything’s gold now.

image
I checked and they are facing front though, do I have to do something on scripting about it?

I whipped up a repro in a couple of minutes in the concept of the view model you’re currently attempting to achieve. I had to make a few respective adjustments from what I suggested to accomodate for R6 rigs. Namely, the following was done:

  • Changed HumanoidRootPart to Torso, as all character limbs are held in the Torso for R6 rigs

  • I used the transform method rather than my originally suggested AnimationPlayed method

In addition to all this, I went ahead and created a rough rig that has correct orientations for the limbs and Motor6Ds. The success of the repro leads me to believe that what you have right now is an incorrect orientation of the Motor6Ds. Observe what my repro does:

One of the only necks is that the Torso doesn’t move as well but that can easily be fixed with a couple of changes here and there. I only accounted for arm movement. You may need to add an extra limb for the torso for accurate movement and use the HumanoidRootPart as a real root.

Here’s what it looks like in first person when touched up:

Code:
local LocalPlayer = game:GetService("Players").LocalPlayer
local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local RenderStepped = game:GetService("RunService").RenderStepped
local ViewModel = game:GetService("ReplicatedStorage").ViewModel:Clone()
local ViewModelRoot = ViewModel.PrimaryPart

ViewModel.Parent = workspace

RenderStepped:Connect(function ()
	ViewModelRoot.CFrame = workspace.CurrentCamera.CFrame * CFrame.new(0, -1.5, 0)
	-- Hardcoded logic test
	ViewModel.Torso["Left Shoulder"].Transform = Character.Torso["Left Shoulder"].Transform
	ViewModel.Torso["Right Shoulder"].Transform = Character.Torso["Right Shoulder"].Transform
end)

Repro file: ViewModelRepro.rbxl (19.4 KB)

Just a warning that collisions may interfere due to the way Humanoids hack them together. I’ve applied CollisionGroups but I’m not sure if that resolves the problem. You’ll need to experiment a bit to find something that fits.

44 Likes

You can used RunService.Stepped for this purpose. When the player zooms in (or if all players are first person you can simply set this) you’ll want to set the LocalTransparencyModifier of the arms to 0 on RenderStepped (not Stepped!). This property is used by the camera script to hide the player’s arms.

Next you’ll want to bind to Stepped (not RenderStepped!) This event is used by animations to create movement. Finally you’ll want to set the Transform property of the Humanoid’s arm joints within the event bind to anything you desire. You can simply translate the base of the arms to somewhere around the camera. This property is not replicated to other clients at all so it is entirely local.

This will create your arm viewmodel without any hacky solutions with new rigs or parts and it will be fully compatible with all Humanoid assets since it is part of the same Humanoid.

2 Likes

Woah! That repro is nearly exactly same as mine! :open_mouth:

Does the name matters? I changed the name from Torso to Motor6D and the collision problem in first person seems not to be happening anymore.

How do you create the rigs? With plugin? For me, I just simply build three parts and connect them with Motor6D, then position them with the correct CFrame, and that’s probably wrong.

Also, I turned on all parts’ Massless will make it looks more smooth, thanks anyways! :wink:

Edit:
Is there any ways to position it to the exactly same position as the real arm?
I modified the CFrame a little bit.

1 Like

Animations are weirdly hard-coded but at the same time aren’t really. There are some conventions that are required, such as where a motor is located and what it’s named, but other than that I don’t think the part name matters. I’m not fully sure myself.

I create rigs by hand and rotate motors with the help of a weld editor of some sort.

As far as moving the hands go, that is also unknown to me. It’d require playing around with CFrames some or separating what others see of the character (as well as the player themselves) from what the client sees.

One last option that can be considered is to modify the actual character parts when in first person to reflect this behaviour, but I’m fairly skeptical when it comes to that.

Is it possible to copy the rig from the Character to the ViewModel such that they have the same position?

Yeah, though that might be a little hacky and at that point it’d be more worth attempting to move the actual rig instead of resorting to any cloning. I don’t have any experience with this so I can try working away at a repro.

Hexcede’s post is worth referring to for attempting something of this nature.

1 Like

I don’t really understand about this, can you further explain a bit more with examples if possible?
What’s the difference between RenderStepped and Stepped?

RenderStepped is run when the game is rendered, while Stepped is run before physics (thanks @ScriptingSupport for just teaching me that just now haha). Stepped is used by animations and it’s the only time the Transform property of Motor6Ds can be used.

This is what I meant (there’s purposely room for improvement but I think I explained what I am doing well enough in the comments):

RunService.RenderStepped:Connect(function()
	for _, part in ipairs(character:GetDescendants()) do -- Definitely don't use GetDescendants in here if you have a lot of instances (e.g. 40 or more) in the player's character since it'll cause a lot of lag
		if part:IsA("BasePart") then -- Not all of these will be parts so we need to check
			part.LocalTransparencyModifier = 0 -- Read the documentation for more information on this property. It basically is used to hide the character's parts when they zoom in without actually using .Transparency. Setting it to 0 makes the part's "actual" transparency equal to its Transparency property which is 0, so the part is fully visible.
		end
	end
end)
RunService.Stepped:Connect(function()
	local camera = workspace.CurrentCamera
	local cameraCFrame = camera.CFrame
	for _, motor in ipairs(baseArmMotors) do
		local cameraOffset = motor.Part0 * camera.CFrame:Inverse() -- An offset to move/rotate the viewmodel by. We calculate it by converting the camera's position to the actual position of the root part. That means the parts will display in their "real" location but we can easily adjust their location by applying a CFrame to this value.
		local offset = motor.C0 * motor.C1:Inverse() -- This is the formula used by joints to get an offset from Part0 to display Part1. We will make the camera act as if it were the Part0 of the weld by applying the offset to the camera's CFrame instead of the Part0's CFrame.
		local newLocation = camera.CFrame * cameraOffset * offset -- We apply the offset to the desired cframe which is the camera CFrame offset by another cframe. This cameraOffset CFrame can be used to move the model forward/backward, left/right.
		local oldLocation = motor.Part0.CFrame * offset -- This is the offset produced by the Motor, not including movements from animations
		motor.Transform = motor.Transform*oldLocation:Inverse()*newLocation -- We subtract the old location from the transform to "0" the viewed position of the arms. Now they're centered on the origin with the offsets from their animations still applied. That means we can add the new location which reapplies the weld offsets with the camera CFrame and now the arms are centered around the camera, offset to their proper positions away from the camera, and they include the playing animation's offsets.
	end
end)
6 Likes

For baseArmMotors ,is that the Motor6D inside the ViewModel?