0
\$\begingroup\$

I need to patch an animation during LateUpdate.

I'm rotating the "chest" bone according to the mouse delta. This is to mimic aiming a pistol.

During this aiming, the "neck" bone should stay relatively still. As changing the chest bone's location rotation yaw automatically changes the "neck" bone's local yaw rotation, I store the chest bone's rotation BEFORE I apply a new rotation to it. Then I calculate the difference between the old and the new rotation. Then I apply this rotation yaw difference to the neck bone.

The inspector gives me nice values to work with. When I inspect the rotation values by script, I get reprensations that I can not easily work with.

Here is an example: enter image description here

While the Inspector shows "15.4", the localEulerAngle shows "344.6".

If I use "344.6", the neck rotation goes Berzerk of course.

How should this be solved?

For completeness, here is the entire void:

private void pHandle_LateUpdate_Aim()
{
    Vector3 nOldChestRotation = Chest.localRotation.eulerAngles;

    // Fade old input before capturing new, so we don't dull the freshest data.
    float yawBlend = Mathf.Pow(1.0f - Aim_yawInputFalloff, referenceFramerate * Time.deltaTime);
    // A Lerp toward zero is just the same as a multiplication by the blend factor.    
    Chest_yawRate *= yawBlend;
    // Accelerate by mouse movement over the past frame.
    // (May need adjustment for display resolution).
    Chest_yawRate += Input.GetAxis("Mouse X");

    float yawDelta = Chest_yawSpeed * Chest_yawRate * Time.deltaTime;
    float offCenterYaw = Chest_currentYaw - Chest_yawCenter;

    float fDesiredChestYaw = Chest_currentYaw + yawDelta;
    float fHeroRotation = 0f;

    if (fDesiredChestYaw > Chest_rotateRange)
    {
        fHeroRotation = fDesiredChestYaw - Chest_rotateRange;
        //we don't rotate the chest beyond the allowed "Chest_rotateRange", so simply act like the user didn't move the mouse
        Chest_yawRate = 0;
        yawDelta = Chest_yawSpeed * Chest_yawRate * Time.deltaTime;
    }
    else if (fDesiredChestYaw < -Chest_rotateRange)
    {
        fHeroRotation = (Chest_rotateRange + fDesiredChestYaw);
        Chest_yawRate = 0;
        yawDelta = Chest_yawSpeed * Chest_yawRate * Time.deltaTime;
    }

    // If we're moving away from the center, slow down as we approach the edge.
    if (yawDelta * offCenterYaw > 0)
    {
        float extremityYaw = offCenterYaw / Aim_yawMaxRange;
        yawDelta *= 1.0f - extremityYaw * extremityYaw;
    }

    this.transform.Rotate(0, fHeroRotation, 0);

    // Ensure we never overshoot the allowed range.
    Chest_currentYaw = Mathf.Clamp(Chest_currentYaw + yawDelta,
                              Chest_yawCenter - Aim_yawMaxRange,
                              Chest_yawCenter + Aim_yawMaxRange);

    Vector3 nNewChestRotation = new Vector3(Chest.localEulerAngles.x, Chest_currentYaw, Chest.localEulerAngles.z);
    Chest.localRotation = Quaternion.Euler(nNewChestRotation);

    float fChestRotationDifference = nNewChestRotation.y - nOldChestRotation.y;

    //the neck should not rotate with the chest. So I first counter-rotate it against the new chest rotation, then rotate it a little towards the chest rotation
    Neck.transform.localRotation = Quaternion.Euler(0, Aim_NeckYaw - fChestRotationDifference + (fChestRotationDifference * 0.5f), 0);

}
\$\endgroup\$
1
\$\begingroup\$

Changing just a yaw is one of the few times it's reasonably safe to do math directly on Euler angles. Because the yaw is applied at the most-global end of the chain, it interacts in fairly consistent & predictable ways with the other angles. So you just need to take into account the potential for angle wraparound.

Replace:

float fChestRotationDifference = nNewChestRotation.y - nOldChestRotation.y;

With:

float fChestRotationDifference = Mathf.DeltaAngle(nOldChestRotation.y, nNewChestRotation.y);

Or, we can avoid fiddling with angles and just blend our quaternions instead:

// At the top, where you're currently caching nOldChestRotation:
Quaternion oldNeckRotation = Neck.transform.rotation;

// ...do your modifications to the chest rotation...

Quaternion newNeckRotation = Neck.transform.rotation;

Neck.transform.rotation = Quaternion.Lerp(oldNeckRotation, newNeckRotation, neckFollowChest);

Here you can vary the float neckFollowChest from 0 to 1 to control whether you want the neck to keep its original rotation exactly (0), turn with the chest (1), or any blend in-between.


In future, I recommend phrasing your question to ask about what you want to accomplish, rather than what you think the solution might be, so we avoid the X/Y Problem. eg. "How to apply a portion of one object's yaw to another" or "Fixing neck rotation after rotating the chest"describes what you're actually trying to do better than "Converting eulerAngles to 'Inspector rotation'" or "Difference between 2 quaternions" (there's no visible attempt to use quaternions in the code you've shown here, and as you can see above we never need to compute a quaternion difference to solve this, so it's a bit of a red herring in this case).

\$\endgroup\$
  • \$\begingroup\$ Wow, that is indeed elegant! \$\endgroup\$ – tmighty Feb 26 at 1:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.