First of all, I do not recommend SetPrimaryPartCFrame because the function is known to have issues since forever and it isn’t really the best option. I suggest you instead create your view model as follows:
Construct your View Model including a root, Humanoid (pseudo humanoid) and the arms. The root can be a HumanoidRootPart and a Head, or one or the other. I recommend a pseudo HumanoidRootPart.
Weld all the respective parts to your model’s root using Motor6D EXPLICITLY.
Have your code set the CFrame of the root part to the Camera’s CFrame plus some. Your view model’s arms will automatically move around as well.
If you have a head, compensate for the half that is in front of the Z plane. If you have a HumanoidRootPart, put it slightly below the camera.
For anything that the character does, replicate it to the root. For example, you can fetch the animation the character is playing and put it back on the view model.[1]
[1] Crude untested code
local realHumanoid = Character.Humanoid
local pseudoHumanoid = viewModel.RenderHumanoid
-- https://developer.roblox.com/en-us/api-reference/event/Humanoid/AnimationPlayed
realHumanoid.AnimationPlayed:Connect(function (track)
local viewAnim = pseudoHumanoid:LoadAnimation(track.Animation)
local viewAnimStop do
viewAnimStop = track.Stopped:Connect(function ()
viewAnim:Stop()
viewAnimStop:Disconnect()
end)
end
viewAnim:Play()
end)
The above code is just an example and I don’t quite recommend it. Typed it on mobile and didn’t test it. There could easily be many better ways to handle this, but off the fly this is what I could think of. The code mainly serves to bring light to Humanoid.AnimationPlayed which you could potentially use to your advantage in this scenario.
Another option can be to set the Transform of the limb motors in the view model to match those in the character right after your CFrame set operation in RenderStepped.
The animation didn’t play? I named the Fake arms into Left Arm and Right Arm, which is suppose to work?
I tried to print AnimationPlayed on both humanoids, and they both works and print the PlayedAnimation though.
I forgot to mention, but I already did this in the first place:
char:WaitForChild("Humanoid").AnimationPlayed:Connect(function(track)
print(track)
local ViewAnim = VM.Humanoid:LoadAnimation(track.Animation)
local viewAnimStop do
viewAnimStop = track.Stopped:Connect(function ()
ViewAnim:Stop()
viewAnimStop:Disconnect()
end)
end
ViewAnim:Play()
end)
Plus, how do I accurately position the fake arm? Do I attempt different CFrame values?
This might be a better way to perform it, can you explain a little bit more with examples if possible? Thanks!
For animations to work, you need to have Motor6Ds and part names explicitly the same. In your case, LMR needs to become Left Shoulder, for example. Animations are hard-coded internally to associate a CFrame to a Motor6D name. I believe hierarchy also matters, which means the motors need to be under a Torso part rather than in each arm. Don’t know the accuracy of that though.
In terms of positioning the fake arms, you don’t - at least not with code. If your pseudo limbs are welded to your view model root, you ONLY change the CFrame of the root. The welds will automatically take care of accurately moving the limbs around as needed.
In terms of the Transform method, animations internally use the Transform property of Motor6Ds to function. I have no clue if it’d work but it’d cut out the need to use the animation workaround and anything associated if so (ergo ignore first paragraph). In your RenderStepped function, you simply need to set the Transform of a pseudo limb’s Motor6D to that of the real limb’s.
My Pseudo arms are welded to the “RootPart” by Motor6D, but the RootPart’s CFrame is set to (workspace.CurrentCamera.CFrame * CFrame.new(0,0,-3)
It should not?
Yeah, don’t touch Z (depth) values. You should instead look towards Y (height) values. 1-2 down should give you the result you want. Play around with the numbers a bit until you find something you like.
I could easily be missing something, but if you are still looking for other options, have your tried AlignOrientation with Responsiveness = 200?
Otherwise replicate arm CFrame plus a position offset would still seem to be the most straightforward and responsive approach. It’s not clear to me that you have in fact tried combining multiple CFrame operations together. Again, it’s likely I’m missing something.
If you just want the visual representation to appear on the client camera, another method could be the Viewport Portal approach in a ScreenGui where you see all or just specified parts of the avatar in the Viewport.