• Runtimes
  • Unity - Combing animation & manual bone rotation

Related Discussions
...

I'm trying to play an animation and simultaneously override the rotation on a few bones. When there is no animation, the manual rotation works, but when an animation is playing it appears to override my manual changes.

I'm doing my manual changes in LateUpdate like so:

toolArmBone.rotation = toolRotation;
toolArmBone.UpdateWorldTransform(flipToolX, flipToolY);

Not sure what else I need to call in order to get the mesh positions to update. Any feedback would be appreciated, thanks!

You should do it in SkeletonAnimation#UpdateSkeleton, after the animation state is applied but before base.UpdateSkeleton is called. Or, you can modify the SkeletonData (anywhere), which all animations are relative to.

Thanks - I actually had to put my UpdateWorldTransforms after base.UpdateSkeleton (not in the middle), but it is indeed working now!

a month later

Just found this thread. Rotating a bone doesn't seem like something I should need to extend SkeletonAnimation for? Even doing so I can't really see how you rotate a bone from an external script. Unity is a composition-based programming model and it feels like there should be a way to do this without inheritance (e.g. in SmoothMoves it is very simple to do in LateUpdate from any external script)

8 days later

In Spine you manipulate bone's local SRT, then call updateWorldTransform to compute the world SRT. Currently SkeletonAnimation Update calls UpdateSkeleton which poses the bone's local SRT with an animaiton then calls updateWorldTransform. updateWorldTransform needs to happen so that Update can configure the mesh using the bone's world SRT.

There may be a better way in Unity to order the work that needs to be done. As you said, it would be nice to adjust a skeleton without using inheritance. Any ideas?

In SmoothMoves if it doesn't work the way you want, too bad. In Spine you have the runtime source. 🙂

a month later

An easy workaround would be to modify the SkeletonAnimation component and simply use an event/delegate system. Something like:

public class SkeletonAnimation : SkeletonComponent {
        public delegate void OverrideAction();
        public event OverrideAction OverrideBones;
...

    override public void UpdateSkeleton (float deltaTime) {
            // Apply the animation.
            state.Update(deltaTime * timeScale);
            state.Apply(skeleton);

            // Override the bones
            if(OverrideBones != null) OverrideBones();
...

}

And then in your Unity scripts you would just have to subscribe/unsubscribe to the OverrideBones event when needed:

//subscribe to the OverrideBones event
mySkeletonAnimation.OverrideBones += MethodYouWannaCallToRotateYourBonesManually;

//unsubscribe when the override is not necessary
mySkeletonAnimation.OverrideBones -= MethodYouWannaCallToRotateYourBonesManually;

I guess that fits better with how components are used than inheritance. I added an UpdateBones delegate. I also updated how the AnimationState delegates, no more nasty event object.

So what I need to do in my script to make it works?

public SkeletonAnimation skeleton;

skeleton.UpdateBones += methodOverride;

void methodOverride ()
{
Spine.Bone arm = skeleton.skeleton.FindBone("arm");
				arm.Rotation = 10f;
}

This is ok?

13 days later

I am doing exactly as shown in that goblin example, and when I change animation it overides my manual bone manipulation. Any tips? Been trying to figure this out for a good while.

3 months later

Just in case someone want to follow bones to the mouse position, here is the code

   private SkeletonAnimation SkeletonAnim;
   private Bone HeadBone;
   private Bone GunBone;
   void Start () {
      GroundCheck = transform.Find("GroundCheck");
      SkeletonAnim = GetComponent<SkeletonAnimation>();
      if(SkeletonAnim) {
         // get bones
         HeadBone = SkeletonAnim.skeleton.FindBone("head");
         GunBone = SkeletonAnim.skeleton.FindBone("gun");

     SkeletonAnim.UpdateBones += UpdateBones;
  }
   }
   void UpdateBones(SkeletonAnimation skeletonAnimation) {

  // could be public
  const float LowerRotationBound = -60.0f;   
  const float UpperRotationBound = 60.0f;

  // temp variables
  float tempRot;
  Vector3 tempVec;

  // head bone rotation
  tempVec = Camera.main.WorldToScreenPoint(new Vector3(HeadBone.WorldX+transform.position.x, HeadBone.WorldY+transform.position.y, 0));
  tempVec = Input.mousePosition - tempVec;
  tempRot = Mathf.Atan2(tempVec.y, tempVec.x*transform.localScale.x) * Mathf.Rad2Deg;
  HeadBone.Rotation = Mathf.Clamp(tempRot, LowerRotationBound, UpperRotationBound);

  // gun bone rotation
  tempVec = Camera.main.WorldToScreenPoint(new Vector3(GunBone.WorldX+transform.position.x, GunBone.WorldY+transform.position.y, 0));
  tempVec = Input.mousePosition - tempVec;
  tempRot = Mathf.Atan2(tempVec.y, tempVec.x*transform.localScale.x) * Mathf.Rad2Deg;
  GunBone.Rotation = Mathf.Clamp(tempRot, LowerRotationBound, UpperRotationBound)-GunBone.parent.WorldRotation;
   }

The transform.localScale.x Multiplication is because I am using that Flip to setup the facing of the character when moving left and right:

   void Flip() {
      
// change facing flag and scale transform FacingRight = !FacingRight; Vector3 scale = transform.localScale; scale.x*=-1; transform.localScale = scale; }

Hope that helps someone!

BR, Marc