Issue w/ 3rd Person Arms

I did want to compose it, so the arm would be rotated upward, otherwise since I’m applying this to the shoulder motor the arm would be pointing down + the rotation, which wouldn’t be what I was aiming for.

Admittedly I’m behind on the terminology, could you elaborate on what you meant by…?

Also I’m still unsure if it being close to Vector3.new(0,1,0) is the full issue, as I’m using the same offsets before and after Aim Down Sights and the bug only occurs before.

I meant building up a CFrame from a function like CFrame.Angles() or CFrame.fromOrientation, and then adding the translation to it by adding a Vector3 (if you want to add a world/parent space translation) or by a post-multiply with a pure translational CFrame (like one made via CFrame.new(positionVector)) if you want a local-space translation.

I don’t understand why you’d want the shoulder rotation in world space multiplied by a 90 degree world-X-axis rotation (isn’t that meant to be a 90-degree bend in shoulder-part local space?). What I think you’re describing would only make sense to me if it was all in local space of the shoulder part (i.e. adding 2 joint angles).

I found myself having to start off re-writing the desired C0 in worldspace, then having to translate it back into objectspace before I could apply it to the motor directly, as I had found applying these rotations in the past in objectspace directly had problematic results, mainly incorrect rotations. Applying the 90 degree fix before or after the translation back into object space to apply it to the motor did not make a difference.

(Edited to clarify the process)

What is the purpose of the boolean input to the Righty function?

To tell the function either to calculate the arms or reset them.

I’ve working on setting up a testing bed to reproduce this, however I’m missing a couple pieces of information. Could you give me a hand?

  1. What is the initial position of rig as stored in ReplicatedStorage?
  2. Is there anything special about the endpart’s position that I should know about? Where is it in relation to the rig when you were producing the bug? It looks like it is the endpart’s position that determines if the weapon is shot from the hip or not.
  1. I keep both rigs backed up in ReplicatedStorage for Motor6D references.
  • Male HumanoidRootPart Position | -35, 3.677, -75.485
  • Female HumanoidRootPart Position | -35, 3.689, -80.709
  1. endpart is either the LeftHand in the gun model, or the Middle (main part/ RightHand reference) in the gun model. Middle is offset via Motor6D from the character’s head, and a rotation is applied based on where you’re aiming.

Here are the two offsets for the rest position and position when the gun is being fired

m.gunOffset = CFrame.new(0.35, -0.88, -0.85)
m.gunRest = CFrame.new(0.25, -1.2, -0.5) * CFrame.Angles(math.rad(20), math.rad(20),math.rad(-10))

When ADS is true, the following is applied to the offset:

local off = Root.gunOffset
if _G.ads then
 off = CFrame.new(off.X * 0.8,off.Y * 0.7,off.Z * 0.6)
end
Root.Services.TS:Create(gunoffval, TweenInfo.new(0.2),{Value = off}):Play()

Is there any more information important in the backup rigs? You said they are used for Motor6D references, are there important starting values?

Also, what does the following line of code do:

Root.Services.TS:Create(gunoffval, TweenInfo.new(0.2),{Value = off}):Play()

Nah, they’re more like StarterCharacter backups, I just read the C0s off of them as well.

That line calls TweenService:Create() to tween the offset value from where it is to where I want it to be. A function on RenderStepped then applies the offset (with a rotation based on where you’re aiming) to the main Motor6D in Middle

The names gunOffset and off make me think that this CFrame is added onto the gun’s current position to result in the ADS position. Is this the case?

off = CFrame.new(off.X * 0.8,off.Y * 0.7,off.Z * 0.6)

In this particular case it modifiies the current offset, which is either gunOffset or gunRest, in other cases it’s one of the two, however ADS triggers gunOffset to be used so in no case is the value modified when gunRest is being used instead.

I’ve created a script to test the function. Does everything in here look correct? Unfortunately it does not reproduce the bug. It may also be that the actual error doesn’t lay here in the ArmCFrame module, but possibly in the code used to animate between positions. How did you determine that the bug is in this code?

local ServerScriptService = game:GetService 'ServerStorage'
local ReplicatedStorage = game:GetService 'ReplicatedStorage'

local ArmCFrame = require(script.Parent.ArmCFrame)

local rootPartCFrame= {
	Male = CFrame.new(-35, 3.677, -75.485);
	Female = CFrame.new(-35, 3.689, -80.709);
}

local gender = 'Male'
local character = ReplicatedStorage.Rigs[gender]:Clone()
character.PrimaryPart = character.HumanoidRootPart
character:SetPrimaryPartCFrame(rootPartCFrame[gender])
character.Parent = workspace
local endPart = ServerScriptService.endPart:Clone()
endPart.Parent = workspace

local function setMotors(off, fire)
	if fire then
		off = CFrame.new(off.X * 0.8, off.Y * 0.7, off.Z * 0.6)
	end
	endPart.CFrame = off + character.Head.Position
	ArmCFrame.Righty(true, character, endPart, gender)
end

wait(5) -- so I can see what is going on

while true do
	-- Test entering resting
	print 'resting'
	setMotors(ArmCFrame.gunRest, false)
	wait(1)

	-- Test resting shot
	print 'resting fire'
	setMotors(ArmCFrame.gunRest, true)
	wait(1)

	-- Test going back to resting after a shot
	print 'resting'
	setMotors(ArmCFrame.gunRest, false)
	wait(1)

	-- Test transition to ADS while not shooting
	print 'ADS'
	setMotors(ArmCFrame.gunOffset, false)
	wait(1)

	-- Test ADS shot
	print 'ADS fire'
	setMotors(ArmCFrame.gunOffset, true)
	wait(1)

	-- Test going back to ADS after a shot
	print 'ADS'
	setMotors(ArmCFrame.gunOffset, false)
	wait(1)

	-- Test going back to resting while not shooting
	print 'resting'
	setMotors(ArmCFrame.gunRest, false)
	wait(1)

	-- Test going to ADS while shooting
	print 'resting fire'
	setMotors(ArmCFrame.gunRest, true)
	wait(1)
	print 'ADS'
	setMotors(ArmCFrame.gunOffset, false)
	wait(1)

	-- Test going to resting while shooting
	print 'ADS fire'
	setMotors(ArmCFrame.gunOffset, true)
	wait(1)

	print '\nRESTARTING\n'
end

Because the tweening/positioning of the main weld works fine, it’s the arms themselves that are having the issue.

Oh, and in the sprinting case, the bug only occurs with pistols, works fine when sprinting with any other gun, which the gun uses the rest offset

Maybe could I get you replace the Righty function with this function and give us the output when it works and when it doesn’t? I need to run off to class but will be back in 3 hours.

m.Righty = function(bool, char, endpart, gender)

    local upperTorso = char:FindFirstChild("UpperTorso")
    local upperC0 = game.ReplicatedStorage.Rigs[gender or "Male"].RightUpperArm.RightShoulder.C0
    local lowerC0 = game.ReplicatedStorage.Rigs[gender or "Male"].RightLowerArm.RightElbow.C0
    local shoulderMotor = char.RightUpperArm.RightShoulder
    local elbowMotor = char.RightLowerArm.RightElbow

print(([[Righty input: {
	reset: %s,
	endPart.Position: %s,
	gender: %s,
	upperC0: %s,
	lowerC0: %s,
}]]):format(
	tostring(bool), 
	tostring(endPart),
	tostring(gender),
	tostring(upperC0),
	tostring(lowerC0)
))

    if upperTorso then
        if bool then
            local shoulderCF = upperTorso.CFrame * upperC0
            local plane, shoulderAngle, elbowAngle = solveIK(shoulderCF, endpart.Position, a, b)

            shoulderMotor.C0 = upperTorso.CFrame:toObjectSpace(plane) * cfAngles(shoulderAngle, 0, 0)
            elbowMotor.C0 = lowerC0 * cfAngles(elbowAngle, 0, 0)
        else
            shoulderMotor.C0 = upperC0
            elbowMotor.C0 = lowerC0
        end
    end

print(([[Righty output: {
	shoulderMotor.C0: %s,
	elbowMotor.C0: %s,
}]]):format(
	tostring(shoulderMotor.C0),
	tostring(elbowMotor.C0)
))

end

I have this running on RenderStepped so this is gonna be a bit difficult to fetch, but I’ll try.

Edit:
Prints flew by so I hope I snagged the right ones (had to deliberately cause an error in the case where the arms are turned off)

Bug does not occur: https://i.gyazo.com/bf57ffb910cf31f3cd48d2249ad3d1a4.png
Bug does occur: https://i.gyazo.com/90cf922556cca4af205884c2b473e341.png

Okay, I’m back! And I have some bad news: I plugged in motor positions and neither set of outputs caused the issue we saw in the videos. Here are two pictures with the two different outputs hardcoded into the function (just ignore the input printed in the output window, it doesn’t matter since I hardcoded the motor values). That was a lot of typing large decimals!

Bad Motors:

Good Motors:

So here is where we are at. Either we got the wrong input/output set that caused the issue, or the issue doesn’t lay in this code. What I’d recommend doing is recording the input/output and have a script print it in a structured format so you could copy and paste it into another script which allows you to replay those values, pause, slow down, and replay. I’d keep it to a bare minimum, no tweening, just a simple R15 model, just a raw setting of motors and stepping through a set of data. Once you identify the troublesome input/output sets, it should be easy to dissect what part of the code is causing those strange values. On the other hand, if the bug doesn’t occur in the replay, then something else is causing the issue.

Hehe, once you find the troublesome input/output set, I’d be nice to have it in a copy/paste-able format! Sorry, this’ll probably take it bit… we are at the pinnacle of debugging here!

Write a couple of lines of code to detect the glitch, and spit out the data only then. Or bind a keyboard key to and event that will print out the values you need, and just punch the key when you see the problem, and when you don’t (for comparison).

In general, for debugging something like this, you should isolate the as little code as is needed to produce the problem, and publish it to a test place for forum members to look at, rather than pastebin snippets or screenshots. Strip out everything that is not required, or dead code, down to the simplest publishable repro case.