Search Unity

Trouble with 2D inverse kinematics

Discussion in '2D' started by Runetass, Sep 27, 2014.

  1. Runetass

    Runetass

    Joined:
    Jun 4, 2013
    Posts:
    16
    Hey.

    I'm working on setting up a re-usable character rig with IKs. I'm currently trying to use SimpleCCD, a (brand new, it seems, thus no documentation) script from Unity (showcased here, found the link here).

    I did also try Sprites and Bones, but switched back to SimpleCCD since I didn't find a way to restrict the angles of the IK, which leads to my current problem.

    The angle limits seem to be limited themselves, the way it's currently set up. The arm I'm limiting gets thrown to an opposite angle beyond a certain point, well within the range I've set up. Granted it would not bug on me, it still would be limited, in the sense that you can't seem to move where the 0 angle is calculated from. That creates a sort of obligatory dead-zone for animation. It's hard to explain, so I'll try to show:



    Here's the scripts - one of them is for the editor only.

    https://dl.dropboxusercontent.com/u/58757568/SimpleCCD.cs
    https://dl.dropboxusercontent.com/u/58757568/SimpleCCDEditor.cs

    Or to view them here:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. [ExecuteInEditMode]
    5. public class SimpleCCD : MonoBehaviour
    6. {
    7.     public int iterations = 5;
    8.    
    9.     [Range(0.01f, 1)]
    10.     public float damping = 1;
    11.  
    12.     public Transform target;
    13.     public Transform endTransform;
    14.    
    15.     public Node[] angleLimits = new Node[0];
    16.  
    17.     Dictionary<Transform, Node> nodeCache;
    18.     [System.Serializable]
    19.     public class Node
    20.     {
    21.         public Transform Transform;
    22.         public float min;
    23.         public float max;
    24.     }
    25.  
    26.     void OnValidate()
    27.     {
    28.         // min & max has to be between 0 ... 360
    29.         foreach (var node in angleLimits)
    30.         {
    31.             node.min = Mathf.Clamp (node.min, 0, 360);
    32.             node.max = Mathf.Clamp (node.max, 0, 360);
    33.         }
    34.     }
    35.  
    36.     void Start()
    37.     {
    38.         // Cache optimization
    39.         nodeCache = new Dictionary<Transform, Node>(angleLimits.Length);
    40.         foreach (var node in angleLimits)
    41.             if (!nodeCache.ContainsKey(node.Transform))
    42.                 nodeCache.Add(node.Transform, node);
    43.     }
    44.  
    45.     void LateUpdate()
    46.     {
    47.         if (!Application.isPlaying)
    48.             Start();
    49.  
    50.         if (target == null || endTransform == null)
    51.             return;
    52.  
    53.         int i = 0;
    54.  
    55.         while (i < iterations)
    56.         {
    57.             CalculateIK ();
    58.             i++;
    59.         }
    60.  
    61.         endTransform.rotation = target.rotation;
    62.     }
    63.  
    64.     void CalculateIK()
    65.     {      
    66.         Transform node = endTransform.parent;
    67.  
    68.         while (true)
    69.         {
    70.             RotateTowardsTarget (node);
    71.  
    72.             if (node == transform)
    73.                 break;
    74.  
    75.             node = node.parent;
    76.         }
    77.     }
    78.  
    79.     void RotateTowardsTarget(Transform transform)
    80.     {      
    81.         Vector2 toTarget = target.position - transform.position;
    82.         Vector2 toEnd = endTransform.position - transform.position;
    83.  
    84.         // Calculate how much we should rotate to get to the target
    85.         float angle = SignedAngle(toEnd, toTarget);
    86.  
    87.         // Flip sign if character is turned around
    88.         angle *= Mathf.Sign(transform.root.localScale.x);
    89.  
    90.         // "Slows" down the IK solving
    91.         angle *= damping;
    92.  
    93.         // Wanted angle for rotation
    94.         angle = -(angle - transform.eulerAngles.z);
    95.  
    96.         // Take care of angle limits
    97.         if (nodeCache.ContainsKey(transform))
    98.         {
    99.             // Clamp angle in local space
    100.             var node = nodeCache[transform];
    101.             float parentRotation = transform.parent ? transform.parent.eulerAngles.z : 0;
    102.             angle -= parentRotation;
    103.             angle = ClampAngle(angle, node.min, node.max);
    104.             angle += parentRotation;
    105.         }
    106.  
    107.         transform.rotation = Quaternion.Euler(0, 0, angle);
    108.     }
    109.  
    110.     public static float SignedAngle (Vector3 a, Vector3 b)
    111.     {
    112.         float angle = Vector3.Angle (a, b);
    113.         float sign = Mathf.Sign (Vector3.Dot (Vector3.back, Vector3.Cross (a, b)));
    114.  
    115.         return angle * sign;
    116.     }
    117.  
    118.     float ClampAngle (float angle, float min, float max)
    119.     {
    120.         angle = Mathf.Abs((angle % 360) + 360) % 360;
    121.         return Mathf.Clamp(angle, min, max);
    122.     }
    123. }
    124.  

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [InitializeOnLoad]
    5. public class SimpleCCDEditor
    6. {
    7.     static SimpleCCDEditor ()
    8.     {
    9.         SceneView.onSceneGUIDelegate += OnScene;
    10.     }
    11.  
    12.     // Scales scene view gizmo, feel free to change ;)
    13.     const float gizmoSize = 0.5f;
    14.  
    15.     static void OnScene(SceneView sceneview)
    16.     {
    17.         var targets = GameObject.FindObjectsOfType<SimpleCCD>();
    18.  
    19.         foreach (var target in targets)
    20.         {
    21.             foreach (var node in target.angleLimits)
    22.             {
    23.                 if (node.Transform == null)
    24.                     continue;
    25.  
    26.                 Transform transform = node.Transform;
    27.                 Vector3 position = transform.position;
    28.  
    29.                 float handleSize = HandleUtility.GetHandleSize(position);
    30.                 float discSize = handleSize * gizmoSize;
    31.  
    32.  
    33.                 float parentRotation = transform.parent ? transform.parent.eulerAngles.z : 0;
    34.                 Vector3 min = Quaternion.Euler(0, 0, node.min + parentRotation)*Vector3.down;
    35.                 Vector3 max = Quaternion.Euler(0, 0, node.max + parentRotation)*Vector3.down;
    36.  
    37.                 Handles.color = new Color(0, 1, 0, 0.1f);
    38.                 Handles.DrawWireDisc(position, Vector3.back, discSize);
    39.                 Handles.DrawSolidArc(position, Vector3.forward, min, node.max - node.min, discSize);
    40.  
    41.                 Handles.color = Color.green;
    42.                 Handles.DrawLine(position, position + min * discSize);
    43.                 Handles.DrawLine(position, position + max*discSize);
    44.  
    45.                 Vector3 toChild = FindChildNode(transform, target.endTransform).position - position;
    46.                 Handles.DrawLine(position, position + toChild);
    47.             }
    48.         }
    49.     }
    50.  
    51.     static Transform FindChildNode (Transform parent, Transform endTransform)
    52.     {
    53.         if (endTransform.parent != parent)
    54.             return FindChildNode(parent, endTransform.parent); ;
    55.  
    56.         return endTransform;
    57.     }
    58. }
    59.  

    Would be super grateful for any help, or any suggestions for other alternatives!
     
  2. mogwhy

    mogwhy

    Joined:
    Nov 20, 2014
    Posts:
    36
    did you find a solution? I m also stuck here.
     
  3. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I'm interested in this too.
     
  4. mogwhy

    mogwhy

    Joined:
    Nov 20, 2014
    Posts:
    36
    just buy puppet 2d . don't waste time with this
     
  5. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    But Puppet2D does not have the ability to restrict the angles of the IK.
     
  6. mogwhy

    mogwhy

    Joined:
    Nov 20, 2014
    Posts:
    36
    yeah, i know. that is on my find-out-how list.
    as soon as you give up on simpleccd and give the puppet guy the money
    AND you find out how to do it I would be happy if you post it here. :D
     
  7. playemgames

    playemgames

    Joined:
    Apr 30, 2009
    Posts:
    438
  8. WarpZone

    WarpZone

    Joined:
    Oct 29, 2007
    Posts:
    326
    Upon import:

    "Some scripts have compilation errors which may prevent obsolete API usages to get updated. Obsolete API updating will continue automatically after these errors get fixed."
     
  9. playemgames

    playemgames

    Joined:
    Apr 30, 2009
    Posts:
    438
    If you got the latest off Github that should be it.
     
  10. WarpZone

    WarpZone

    Joined:
    Oct 29, 2007
    Posts:
    326
    So I click this link you posted: https://github.com/playemgames/UnitySpritesAndBones
    Then I click Clone or Download
    Then I click Download Zip
    Then I unzip the folder
    Then I copy Assets/SpritesAndBones into my project's Assets folder
    Then I tab over to Unity, which already has my project open, and it says "Some scripts have compilation errors which may prevent obsolete API usages to get updated. Obsolete API updating will continue automatically after these errors get fixed."

    Did I do it right? If so, Unity 5.3.5f Personal can't convert the latest off Github.