I am embarking on trying to make an IK Chain solver in Unityscript. I don't really need help yet, but I wanted to ask, has anyone done this and would you be willing to share your script? But I do have one question about doing this. Of course this is going to rely heavily on reading and writing rotation values. I am wondering, would doing this in quaternions offer any performance benefit over using EulerAngles? Because I have no idea how a quaternion works and am inclined to use euler angles. But if euler angles cause a performance hit I'll figure out quaternions.
I've taken a look at that locomotion system and it's an incredible piece of work, but still way over my head. I'm working on procedural animations right now, too (see scripting thread "Can you read this book?") but on a more basic level. IK would work much better for me, but I still don't understand how eulers work well enough yet to write an IK script.
This seems to be a question that comes up once in a while, so I made an entry for it at UnityAnswers: http://answers.unity3d.com/questions/3646/how-to-do-inverse-kinematics-ik-in-unity/3648#3648
Quaternions are a bit faster, but the main reason for using them is that you get smoother interpolation of angles. With euler angles, sometimes you will get weird rotations if you try to interpolate between two sets of angles. I've read quite a few research papers related to animation, and in the recent ten years or so they all use quaternions rather than euler angles because it has just proven to work better.
I looked in the locomotion system but can't really follow it, and I need to understand it thoroughly in this application. I am trying to implement a 2 bone solve based on the law of cosines, read about here http://en.wikipedia.org/wiki/Cosine_formula specifically I am going off the of the y = arcos function seen in the applications section http://en.wikipedia.org/wiki/Cosine_formula#Applications But I CANNOT get this to work. It seems so simple but it just won't do it. Here is the code so far. Code (csharp): private var rightFoot : Transform; private var rightKnee : Transform; private var rightHip : Transform; var rightFootIK : Transform; function OnDrawGizmos() { rightFoot = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee/RightFoot"); rightKnee = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee"); rightHip = transform.Find("greyParametric/CenterRoot/RightHip"); var hipToKnee : float = Vector3.Distance(rightHip.position, rightKnee.position); var kneeToFoot : float = Vector3.Distance(rightHip.position, rightKnee.position); var hipToFoot : float = Mathf.Clamp(Vector3.Distance(rightFootIK.position, rightHip.position), 0, hipToKnee + kneeToFoot); //total leng length var IKAngleFromHip : float = Mathf.Atan2(rightHip.position.x - rightFootIK.position.x, rightHip.position.y - rightFootIK.position.y); hipAngle = Mathf.Acos((hipToKnee * hipToKnee + hipToFoot * hipToFoot - kneeToFoot * kneeToFoot) / (2 * hipToKnee * hipToFoot)); kneeAngle = Mathf.Acos((kneeToFoot * kneeToFoot + hipToKnee * hipToKnee - hipToFoot * hipToFoot) / (2 * kneeToFoot * hipToKnee)); rightKnee.eulerAngles.z = ((kneeAngle * -1 ) + IKAngleFromHip) * Mathf.Rad2Deg; rightHip.eulerAngles.z = ((hipAngle * -1 ) + IKAngleFromHip) * Mathf.Rad2Deg + 180; } How that works is, you have to set rightFoot, rightKnee and rightHip to your foot, knee and hip joints respectively. Through the transform.Find functions Then you make a seperate cube and drag it onto the rightFootIK transform slot. The leg then follows the rightFootIK cube. This HALF-way works. You can fully extend the leg, and it will aim at the cube correctly. As you move the cube closer to the hip, the leg will retract and fold up but it retracts faster than you move the cube! And I cannot figure this out, seriously have been staring at this for liek 12 hours. I cannot figure out why it doesn't work. I'm thinking unity might be producing a slightly different acos value then what'd be expected.
In the script IK2JointAnalytic.cs there is this function: Code (csharp): public override void Solve(Transform[] bones, Vector3 target) The bones array should contain the hip, knee, and ankle transforms, and the target is where the ankle should be. The function will then modify the transforms using IK. Sorry, I don't have time to look into your code. No, that is not the case. Mathf.Acos is based on the Math.Acos function in Mono. The error must be somewhere else.
Do you know what method IK2Joinanalytic is using to calculate the inverse kinematics? I cannot tell what its doing.
The interesting part that finds the position of the knee is the code below. I can't remember all the details, but it's basic trigonometry. The attached image may be helpful if you want to investigate further. Code (csharp): public Vector3 findKnee(Vector3 pHip, Vector3 pAnkle, float fThigh, float fShin, Vector3 vKneeDir) { Vector3 vB = pAnkle-pHip; float LB = vB.magnitude; // ... Edge case handing here ... float aa = (LB*LB+fThigh*fThigh-fShin*fShin)/2/LB; float bb = Mathf.Sqrt(fThigh*fThigh-aa*aa); Vector3 vF = Vector3.Cross(vB,Vector3.Cross(vKneeDir,vB)); return pHip+(aa*vB.normalized)+(bb*vF.normalized); }
yeah... I am study and try to make my version of full body IK. (still in the research stage). Maybe we can share the idea and code someday later http://www.youtube.com/watch?v=UNrrd_XhPMA http://www.youtube.com/watch?v=S-zAk6VqL-E They are using approximation of inverse sine cardinal function for IK. Code (csharp): function asinc(x0:float):float { var x :float = 6*(1-x0); var x1:float = x; var a :float = x; x*=x1; a += x /20.0; x*=x1; a += x* 2.0 /525.0; x*=x1; a += x* 13.0 /37800.0; x*=x1; a += x* 4957.0 /145530000.0; x*=x1; a += x* 58007.0 /16216200000.0; x*=x1; a += x* 1748431.0 /4469590125000.0; x*=x1; a += x* 4058681.0 /92100645000000.0; x*=x1; a += x* 5313239803.0 /1046241656460000000.0; x*=x1; a += x* 2601229460539.0/4365681093774000000000.0; // x^10 return sqrt(a); } a, the length of arc from base to target. b, the distance of base to target
Thanks for your guys help. runevision, I was trying to use your IK solver as a public class by going IKSolver.IK2JointAnalytic.Solve(rightLeg,rightFootIK); but that doesn't work. I'm trying to call it from javascript. Is it possible to call c# functions from js? Also about the code float aa = (LB*LB+fThigh*fThigh-fShin*fShin)/2/LB; float bb = Mathf.Sqrt(fThigh*fThigh-aa*aa); Vector3 vF = Vector3.Cross(vB,Vector3.Cross(vKneeDir,vB)); return pHip+(aa*vB.normalized)+(bb*vF.normalized) I can see the float bb = is using the Pythagorean theorem. But what is float aa = doing? Is there a name for whats going on there? and this right here Vector3 vF = Vector3.Cross(vB,Vector3.Cross(vKneeDir,vB)); I assume vKneeDir would point in the same direction as vF on the diagram? Is that correct? Or would it point down the thigh? But doing that set of cross products seems like it just cancels itself out? What is that doing exaclty? I still kind of want to get an IK solver that I can fully comprehend running and so I've been plugging at the code and I managed to get a 2 bone analytic solver working through a different trig function, just a simple acos and asin call to get angles of a right right triangle. Code (csharp): private var rightFoot : Transform; private var rightKnee : Transform; private var rightHip : Transform; var rightFootIK : Transform; function OnDrawGizmos() { rightFoot = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee/RightFoot"); rightKnee = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee"); rightHip = transform.Find("greyParametric/CenterRoot/RightHip"); var hipToKnee : float = Vector3.Distance(rightHip.position, rightKnee.position); var kneeToFoot : float = Vector3.Distance(rightFoot.position, rightKnee.position); var hipToFoot : float = Mathf.Clamp(Vector3.Distance(rightFootIK.position, rightHip.position), 0, hipToKnee + kneeToFoot); //total leg length var IKAngleFromHipZ : float = Mathf.Atan2(rightHip.position.x - rightFootIK.position.x, rightHip.position.y - rightFootIK.position.y) * Mathf.Rad2Deg; var IKAngleFromHipX : float = Mathf.Atan2(rightHip.position.x - rightFootIK.position.x, rightHip.position.y - rightFootIK.position.y) * Mathf.Rad2Deg; hipAngle = Mathf.Acos((hipToFoot/2)/hipToKnee) * Mathf.Rad2Deg; kneeAngle = Mathf.Asin((hipToFoot/2)/kneeToFoot) * Mathf.Rad2Deg; rightKnee.localEulerAngles.z = 90 - kneeAngle + hipAngle; rightHip.localEulerAngles.z = (180 - hipAngle) + IKAngleFromHipZ; } That only works if both the shin and the thigh are the same length, and is essentially a 2D solver. And I'm having a helluva a time trying to figure out how to get it into a 3D solver. I think I have a way figured out but, I can't figure out how to do one thing. I need to essentially parent a joint to a transform in code but not actually parent it in the hiearchy. I know you can do this with fixed joints but I would like to keep rigidbodies off my skeleton. Does anyone know how to parent a transform to another transform, all in script? Without actually making it a parent in the hiearchy? So like you would rotate the parent transform in it's local angles, and it would add that rotation to another transform as if it were a child.
The function takes an array of Transforms, not just one Transform. You can call C# functions from JavaScript if you make sure the C# script is compiled before the JavaScript script that is calling it. See this for more info on controlling the order of compilation: http://unity3d.com/support/documentation/ScriptReference/index.Script_compilation_28Advanced29.html I don't know 100% because I based the script on formulas I found elsewhere, but it seems to be derived from the law of cosines. vKneeDir should be the general direction the knee should be pointing in. vF is just a corrected version of vKneeDir that is forced to be perpendicular to vB, in case vKneeDir was not perpendicular to vB.
I put IKSolver and IK2JointAnalytic into a folder called "Plugins" but when I try IKSolver.Solve([rightHip,rightKnee,rightFoot],rightFootIK); it still says BCE0020: An instance of type 'IKSolver' is required to access non static member 'Solve'. I don't understand why that would be required. In the locomotion example project those scripts are just in "\Assets\Locomotion System", which I assume places them in the 4th compile group. But they still work.
It is because Solve is not a static function. That is because they are called from other c# scripts, not from Javascript.
Ohh. I see. So I just tried to edit your script into being a static function and somehow managed to completely break unity. It froze, then crashed. And now when I open it, it's unresponsive and says 'IsD3D9DeviceLost() || g_d3dInsideScene' :? I rebooted and now it says FMOD failed to initialize I guess I gotta reinstall .. Help. I reinstalled unity and it still opens with an error!?! It's not saying anything in the console now, just giving an error bleep and freezing... ... ok I found it was just something in 1 scene... I wonder what caused that
yeah... law of cosines is very useful and simple for finding the joins angle of 2-segmented IK Code (csharp): function IK_2bone(a0:float, a1:float, mag:float):float[] { if (abs(a0-a1) > mag) { return [0.0, 180.0]; } if (abs(a0+a1) < mag) { return [0.0, 0.0]; } var x0 = CosLaw(mag, a0, a1); var x1 = CosLaw(a0, a1, mag); var t0 = (x0 -PI/2.0)*r2d; var t1 = (x1 +PI/2.0)*r2d; return [t0, t1]; } input: a0, length of parent segment a1, length of child segment mag, the distance between "base" "target" point output: t0, the angle at base join. t1, the angle between segments (to find the opposite angle of side c from 3 given length) Code (csharp): function CosLaw(a:float, b:float, c:float):float { return asin((a*a+b*b-c*c)/(2*a*b)); } remark: this is my code style, I always overload the math function for easy viewing Code (csharp): private var asin = Mathf().Asin; private var PI = Mathf().PI; private var r2d = Mathf().Rad2Deg; private var abs = Mathf().Abs;
check this out. I leant from there many many more related information out there too. http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/index.htm
that works awesomely apple_motion One question though, have you put any thought into getting that method to work in 3D? Thats currently where I was at using my implementation, and also now yours. How do you get X and Y rotation values onto the hipjoint so that it points at the IK target no matter where it is. My current thought was, the X and Y rotation values can't be applied directly to the hipjoint. Because it's not the hipJoint that has to point at the IK target, but rather the non-existing vector between the hip joint and the foot joint that has to point at the IK target. So there needs to be some kind of imaginary transform at the hip, that aims at the IK target, and then the real hip transform needs to be parented to this imaginary transform. But that needs to be done in code... and I cannot figure out how to parent one objects rotations to another purely in code. Any ideas?
Not yet for this implementation, since I jumped to working on the Bézier Curve version, but as you see on the script, it is 3D ready, most of variables are using vector3. On the following portion of code, simply change the "atan2" to some others find 3D angle method that should make it work on 3D too Code (csharp): //---- parent joins t[0].z += atan2(target_vec.y, target_vec.x)*r2d; if(join[0].parent) { t[0].z -= join[0].parent.eulerAngles.z; } yeah... as of the above code, after find the 3D angle, minus it with the parent join angle This is the full script for your reference. (remark, this script should apply on the "child" bone) Code (csharp): var reference:GameObject; var bone_0 :float = 1; var bone_1 :float = 1; var join_bias :float = 0; private var target:GameObject; function IK_2bone(a0:float, a1:float, mag:float):float[] { if (abs(a0-a1) > mag) { return [0.0, 180.0]; } if (abs(a0+a1) < mag) { return [0.0, 0.0]; } //var a = a0+a1; var b = mag; var x0 = CosLaw( b, a0, a1); var x1 = CosLaw(a0, a1, b); var t0 = (x0 -PI/2.0)*r2d; var t1 = (x1 +PI/2.0)*r2d; return [t0, t1]; } function FixedUpdate () { if(reference target) { target.transform.position = reference.transform.position; } //----- setup joins var L:int = 2; var join = new Array(); join[L-1] = this.transform; var i:int; for(i=L-1;i>0;i--) { join[i-1] = join[i].parent; } //----- setup target if (!target) { target = new GameObject(); target.name = "_target"; target.transform.position = Vector3.zero; target.transform.parent = this.transform; target.transform.localPosition = Vector3(bone_1, 0, 0); } //----- calc vector var base_pos = join[0].transform.position; var target_pos = target.transform.position; var target_vec = (target_pos - base_pos).normalized; var target_mag = (target_pos - base_pos).magnitude; //---- init var t = new Vector3[L]; for(i=0;i<L;i++) { t[i] = Vector3.zero; } //----- body joins var sector_angle = new float[L]; sector_angle = IK_2bone(bone_0, bone_1, target_mag); var dotL = Vector3.Dot(target_vec,join[L-1].transform.up); for(i=0;i<L;i++) { t[i].z -= sector_angle[i]*sign(dotL +join_bias); } //---- parent joins t[0].z += atan2(target_vec.y, target_vec.x)*r2d; if(join[0].parent) { t[0].z -= join[0].parent.eulerAngles.z; } //---- assign target.transform.parent = null; for(i=0;i<L;i++) { join[i].Rotate(t[i]-join[i].localEulerAngles); } target.transform.parent = this.transform; _pline(target_pos, base_pos, 6); } //-------------------- helper ------------------------------ function OnDrawGizmos() { if(!target) return; Gizmos.color = Color(1,1,0); Gizmos.DrawCube (target.transform.position, Vector3.one*0.05); } private static var TP_str: String = "..."; function OnGUI() { GUI.Label(Rect(0,0,320,240), TP_str); } function CosLaw(a:float, b:float, c:float):float { return asin((a*a+b*b-c*c)/(2*a*b)); } function _ray(p:Vector3, v:Vector3, c:Color) { Debug().DrawRay(p,v,c); } function _pline(p1:Vector3, p2:Vector3, c:int) { var b = c % 2; c /=2; var g = c % 2; c /=2; var r = c % 2; _line(p1, p2, Color(r, g, b)); } private var _log = Debug().Log; private var _line = Debug().DrawLine; private var atan2 = Mathf().Atan2; private var asin = Mathf().Asin; private var PI = Mathf().PI; private var r2d = Mathf().Rad2Deg; private var sqrt = Mathf().Sqrt; private var sign = Mathf().Sign; private var abs = Mathf().Abs; (once again, most of the math and debug function are overloaded )
I modified the IKSimple script from the locomotion tutorial so that you could drop it on a root node and add an ik handle. 1) Create a hierarchy of joints 2) Create a GameObject (iKHandle) 3) Drop script onto root joint for IK 4) Assign iKHandle to the script 5) Run and move the iKHandle or the Root Joint I don't have nearly as much control as I would like. It would be great if someone could add code for weighting of the "Joints". I've also modified the TubeRenderer script to work with this so that you can easily see what is going on. Great for creating wires/tubes, etc. Simply drop the script onto the root joint along with the other script. Hopefully this is helpful. Again, it would be great if someone could modify this to support bone weights Enjoy!
This is all incredibly helpful to me, and I'm sure alot of others too, thanks all of you. slgooding, from looking at this script, if I understand correctly, is the method used in iksimplechain a CCD method? Or runevision, since I guess this is your script and you'd know better, is it a CCD method? Did you end up using a CCD IK method on the locomotion system implementation? I was thinking about trying to do this myself but worried about speed. Does the CCD method take a whole lot more computation that it'd be something to worry about in performance terms? Although I can't get this to work reliably on a whole skeleton, it'll properly solve for a bit until moved out of range, then the knee will lock and the foot will start to go wonky. applemotion, now it makes sense why I was struggling so hard... for some reason I thought I needed to apply the X and Y rotations AFTER the Z rotation... :roll: anyways. that bezier solver looks quite good. Anyone know what kind of math goes into doing a pole vector constraint? Or a rotate plane? I'm trying to figure out how to get control over the direction the knee points.
To be honest I don't understand a lot of the underlying code. I simply modified the original ik script from the locomotion tutorial. Wish I could be of more help, and I should probably read into it as I would like to add weighting to the joints. There are definitely some performance issues that need to be considered with this script and it seems to be "jittery" when moving the ik handle along certain axis. This may just be a matter of a simple modification to that code. However this doesn't seem to be the case if you transform the root joint though ( and it make work all the same if you treat that as the ik handle ). The max iterations can be reduced to improve performance. Depending on the scenario you can drop it significantly (down to 15 or something around that). The iterations is basically determines how many times it can loop before it finds the optimal joint rotations and positions based on the previous positions of each joint.
did you mean the unwanted flipping after the join angle is zero (or the bones are in the straight line) ? If that is the case, simply set the "join_bias" other than 0 will solve the problem. This make the join will only allowed to rotate in one direction not both. Code (csharp): var dotL = Vector3.Dot(target_vec,join[L-1].transform.up); for(i=0;i<L;i++) { t[i].z -= sector_angle[i]*sign(dotL +join_bias); } (remark: as mentioned, that is tested on 2D situation, you may need to modify it for 3D, however, the concept should be very similar
I ran into one odd thing when converting it to 3D. Just want to make sure I'm thinking about this correctly. Let me try to illustrate this in ascii Code (csharp): H |\ | \ | \ | K | / | / |/ F So that is your triangle for solving IK, H = Hip, K = Knee, F = Foot. I have it set up so rotations around the Z axis is forward and back on the leg, X axis is left and right. Now the thing is, when doing the X axis rotation, you cannot directly rotate the hip joint on the X axis. Because the X axis rotation needs to be performed on a joint that is aimed at the foot. But the hip joint is aimed at the knee, so it would orient it wrong. When doing the x axis rotation it is the vector from the hip to the foot that has to be rotated on the x axis, not the vector from the hip to the knee. But the actual hip joint is aimed at the knee, so you gotta do something about that. I was thinking that you could apply the X axis rotation BEFORE the z axis rotation, because before the Z axis rotation the hip is aimed at the foot. But this doesn't seem possible in anyway. Whether you apply the z axis or x axis rotation first in code makes no difference. So right now I am using an additional quaternion at the hip joint, that is aimed at the foot, then I rotate that quaternion on the X axis, the actual hip joints rotation is multiplied by the additional hip quaternions rotation (so it acts like a child of the additional hip quaternion). And the only rotation applied to the actual hip joint is on the Z axis. Then the X and Y axis rotations on the hip are done through the additional hip quaternion that remains aimed at the foot. This is working.... I can't think of how you would do this any other way, you have to have an additional quaternion at the hip joint. Unless I'm wrong here? Am I? Thats why I typed all that out. But anyways. My hip joint DOES need to rotate on all axis, I can't remove rotation from one of the axis. I just need to get some kind of control on all the axis. I need to figure a way to get the additional hip quaternion to aim at both the foot, and another IK target in order to get the knee to point the proper direction, and be able to control where the knee points. Essentialy a pole vector is what Maya calls them. Any ideas on how to do that? Here is the code I have appended onto yours, I'm currently doing this all in OnDrawGizmos just cause it is easier to test, but will move it to Update when finished. Now I have it set up so that rightFoot, rightKnee and rightHip transforms are set through the find function. And also as I said before, Z rotation rotates the leg forward and back, X rotation rotates it left and right, Y rotation rotates where the knee aims. Code (csharp): #pragma strict //http://www.thesixtyone.com/ochremusic/song/Vegas/j8kRl3cfwm1/ private var asin = Mathf().Asin; private var PI = Mathf().PI; private var r2d = Mathf().Rad2Deg; private var abs = Mathf().Abs; function CosLaw(a:float, b:float, c:float):float { return asin((a*a+b*b-c*c)/(2*a*b)); } function IK_2bone(a0:float, a1:float, mag:float):float[] { if (abs(a0-a1) > mag) { return [0.0, 180.0]; } if (abs(a0+a1) < mag) { return [0.0, 0.0]; } var x0 = CosLaw(mag, a0, a1); var x1 = CosLaw(a0, a1, mag); var t0 = (x0 -PI/2.0)*r2d; var t1 = (x1 +PI/2.0)*r2d; return [t0, t1]; } private var rightFoot : Transform; private var rightKnee : Transform; private var rightHip : Transform; var rightFootIK : Transform; function OnDrawGizmos() { rightFoot = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee/RightFoot"); rightKnee = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee"); rightHip = transform.Find("greyParametric/CenterRoot/RightHip"); var hipToKneeQuat : Quaternion; var hipToFootQuat : Quaternion; var hipToKnee : float = Vector3.Distance(rightHip.position, rightKnee.position); var kneeToFoot : float = Vector3.Distance(rightKnee.position, rightFoot.position); //var hipToFoot : float = Mathf.Clamp(Vector3.Distance(rightFootIK.position, rightHip.position), 0, hipToKnee + kneeToFoot); //total leg length clamped var hipToFoot : float = Vector3.Distance(rightFootIK.position, rightHip.position); //total leg length var IKAngleFromHipZ : float = Mathf.Atan2(rightFootIK.position.y - rightHip.position.y, rightFootIK.position.x - rightHip.position.x) * Mathf.Rad2Deg; var IKAngleFromHipX : float = Mathf.Atan2(rightHip.position.y - rightFootIK.position.y, rightHip.position.z - rightFootIK.position.z) * Mathf.Rad2Deg; var legAngles : float[] = IK_2bone(hipToKnee, kneeToFoot, hipToFoot); hipToFootQuat.eulerAngles.y = 180; //where the knee aims hipToFootQuat.eulerAngles.z = 90 - IKAngleFromHipZ; hipToFootQuat.eulerAngles.x = IKAngleFromHipX - 90; rightKnee.localEulerAngles.z = legAngles[1]; hipToKneeQuat.eulerAngles = Vector3( 0, 0, legAngles[0]); rightHip.rotation = hipToFootQuat * hipToKneeQuat; //quaternion multiplication will make hipToKneeQuat a child to the rotations of hipToFootQuat } If anyone has any ideas on how to get this knee constrained, ideally like a pole vector in maya, I'd like to hear. Also apple_motion, you said that you were aiming for a full body IK solution? I am actually aiming for the same thing as well. I'm curious, what are you intending to use it for? I myself am attempting to create a parametric kung fu animation system for a sidescroller.
In fact, we are doing the same thing but for different purpose my goal is build a full physics based ballet simulator.
I was thinking about this alot yesterday, but don't you think a CCD solution would be most ideal for full body IK? I'm pretty sure thats what the autodesk humanIK middleware uses. It's a CCD solver that will solve the entire skeleton.
I GOT THE POLE VECTOR WORKING thanks to the quaternion look at function put a transform for rightKneeIK, and the knee will aim at it! Even at extremes, or above and below the waist. It works just like a maya pole vector Code (csharp): #pragma strict private var rightFoot : Transform; private var rightKnee : Transform; private var rightHip : Transform; var rightFootIK : Transform; var rightKneeIK : Transform; private var asin = Mathf().Asin; private var PI = Mathf().PI; private var r2d = Mathf().Rad2Deg; private var abs = Mathf().Abs; function CosLaw(a:float, b:float, c:float):float { return asin((a*a+b*b-c*c)/(2*a*b)); } function IK_2bone(a0:float, a1:float, mag:float):float[] { if (abs(a0-a1) > mag) { return [0.0, 180.0]; } if (abs(a0+a1) < mag) { return [0.0, 0.0]; } var x0 = CosLaw(mag, a0, a1); var x1 = CosLaw(a0, a1, mag); var t0 = (x0 -PI/2.0)*r2d; var t1 = (x1 +PI/2.0)*r2d; return [t0, t1]; } function OnDrawGizmos() { rightFoot = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee/RightFoot"); rightKnee = transform.Find("greyParametric/CenterRoot/RightHip/RightKnee"); rightHip = transform.Find("greyParametric/CenterRoot/RightHip"); var hipToKneeQuat : Quaternion; var hipToFootQuat : Quaternion; var hipToKnee : float = Vector3.Distance(rightHip.position, rightKnee.position); var kneeToFoot : float = Vector3.Distance(rightKnee.position, rightFoot.position); var hipToFoot : float = Vector3.Distance(rightFootIK.position, rightHip.position); //total leg length var legAngles : float[] = IK_2bone(hipToKnee, kneeToFoot, hipToFoot); hipToFootQuat.SetLookRotation(rightFootIK.position - rightHip.position, rightKneeIK.position - rightHip.position); rightKnee.localEulerAngles.z = legAngles[1]; hipToKneeQuat.eulerAngles = Vector3(0, -270, legAngles[0] + 90);//will probably need to adjust depending on your skeleton rightHip.rotation = hipToFootQuat * hipToKneeQuat; //quaternion multiplication will make hipToKneeQuat a child to the rotations of hipToFootQuat } [/img]
well done ! your script is better than my mine so that, I could copy your version later. (hope you don't mine )
I use LookAt function too that help my version go 3D ! as expected, only few lines of code need to change Code (csharp): join[0].rotation = lookAt_Quat(target_vec, Vector3.right); join[1].Rotate(t[1]-join[1].localEulerAngles); join[0].Rotate(t[0]); Code (csharp): function lookAt_Quat(v1:Vector3, v0:Vector3):Quaternion // v0 -> v1 { v0 = v0.normalized; v1 = v1.normalized; var n = cross(v0, v1); n = n.normalized; var t:float = acos(dot(v0, v1))/2.0; return Quaternion(sin(t)*n.x, sin(t)*n.y, sin(t)*n.z, cos(t)); } (Remark: The up vector from the output of the above function is not same as the unity build-in lookAt.) Of course, I can use build-in lookAt too, but I need to do additional 90 degree rotation, since my setup is not use "forward" as lookAt position Code (csharp): var qR = Quaternion(0, -sin(PI/2), 0, sin(PI/2)); var q0:Quaternion; q0.SetLookRotation(target_vec, Vector3.up); join[0].rotation = q0*qR;
apple_motion, have you seen this? http://www.youtube.com/watch?v=X5Z7ZJ39zAA http://grail.cs.washington.edu/projects/styleik/ seems to be the ultimate full body IK solution but potentially very diffucult and costly to implement... hm it could probably be done though I am going to continue to try to come up with something off of an analytical solution first though. Trying to figure out how to position the root hip joint, and how much to rotate the root hip joint, so that if you move the footIK the character looks like hes still standing in balance.
no, that is too new to me. I still study from the basic concept, especially on the robotics. (since, I have electronic engineering background, maybe one day I could make a robot not just a simulator ) 16 lectures of "Introduction to Robotics" from Stanford University, http://www.youtube.com/watch?v=0yD3uBshJB0 these courses are very informative, full of interesting, and up-today material, except the mathematic model of robotic is little hard to me. but that is the most important part of the lecture. I shall make it
I took the law of cosines script and condensed it as short as possible, with the least amount of calls and variables and made it so that you can drop it on anything. If you use maya, the script is set up to work with joints oriented to XYZ with second axis world orientation +Y, you set this via the 'Orient Joint Options' menu. Otherwise you will need to edit a couple of the variable to make it orient properly Code (csharp): var Root : Transform; var Mid : Transform; var End : Transform; var MidIK : Transform; var EndIK : Transform; function LateUpdate() { var RootToMid : float = Vector3.Distance(Root.position, Mid.position); var MidToEnd : float = Vector3.Distance(Mid.position, End.position); var RootToEnd : float = Vector3.Distance(EndIK.position, Root.position); //total leg length var RootToMidQuat : Quaternion; var RootToEndQuat : Quaternion; RootToEndQuat.SetLookRotation(EndIK.position - Root.position, MidIK.position - Root.position); if (Mathf.Abs(RootToMid-MidToEnd) > RootToEnd) { Mid.localEulerAngles.z = 180; RootToMidQuat.eulerAngles = Vector3(0, -270, 0); //-270 determines direction Knee will face } else if (Mathf.Abs(RootToMid+MidToEnd) < RootToEnd) { Mid.localEulerAngles.z = 0; RootToMidQuat.eulerAngles = Vector3(0, -270, 0); } else { //uses pythagorean theroem to figure out angles Mid.localEulerAngles.z = Mathf.Asin((RootToMid*RootToMid+MidToEnd*MidToEnd-RootToEnd*RootToEnd)/(2*RootToMid*MidToEnd))*Mathf.Rad2Deg + 90; RootToMidQuat.eulerAngles = Vector3(0, -270, Mathf.Asin((RootToEnd*RootToEnd+RootToMid*RootToMid-MidToEnd*MidToEnd)/(2*RootToEnd*RootToMid))*Mathf.Rad2Deg - 90); } Root.rotation = RootToEndQuat * RootToMidQuat; //quaternion multiplication will make RootToMidQuat a child to the rotations of RootToEndQuat End.rotation = EndIK.rotation; } I got a question about this though. If I take that lateUpdate function and also make it a OnDrawGizmos function to see the IK work in the viewport without playing, will this cause any sort of clash or take up unneeded cpu cycles once exported to a web app or standalone?
Hey I just found this thread, I've been trying (without success) to get a simple IK chain going for a while but this thread saved me! I attached a little example scene with a rigged up set of legs, rigged witth eem's script. Thanks! Pete
Cool thread some useful stuff here. I've started implementing an fairly recent IK method called FABRIK which seems promising in comparison with CCD, Jacobian methods etc. You can read the paper here - www.andreasaristidou.com/publications/FABRIK.pdf ..and video here http://www.andreasaristidou.com/FABRIK.html I've done the easy bit in converting the main algorithm but need to figure out an elegant way of implementing multi end effectors and constraints. Here's the package, just move the orange sphere around in the editor view while unity is running. http://dl.dropbox.com/u/8521226/FABRIK.unitypackage
The script that techmage posted works great for my project, except that my character's knee bends backwards, and I can't seem to figure out why. Any folks out there smarter than I know how I can fix this? Edit: Solved this, had to make some adjustments to the rig's bone rotations. Working well now.
I've converted the FABRIK main algorithm too. And couldn't understand how the constraints are done. It´s a bit confusing on the article. Have you managed to do constraints? BTW, Your link is broken!
Cool stuff. Does anyone know how to make this work when the joints are oriented to the world? Also, wanted to make a correction to what techmage says regarding joint orientation for his script: it should actually read "joints oriented to XYZ with second axis world orientation Z +". To do this in Maya: 1. Select the root joint 2. Edit > Select Hierarchy 3. Display > Transform Display > Local Rotation Axes (this is optional, it just shows you the joint pivots) 4. Animation Mode 5. Skeleton > Orient Joints (Option Box) 6. Orient Joint Options: - Edit > Reset Settings - Primary Axis = X - Secondary Axis = Y - Secondary Axis World Orientation = Z +
I know it's an old thread but it's save me a lot of time. So, here a Csharp version of the script Code (csharp): using UnityEngine; using System.Collections; public class Iktest : MonoBehaviour { public Transform Root; public Transform Mid; public Transform End; public Transform MidIK; public Transform EndIK; Quaternion RootToMidQuat = Quaternion.identity; Quaternion RootToEndQuat = Quaternion.identity; void LateUpdate () { float RootToMid = Vector3.Distance(Root.position, Mid.position); float MidToEnd = Vector3.Distance(Mid.position, End.position); float RootToEnd = Vector3.Distance(EndIK.position, Root.position); //total leg length RootToEndQuat.SetLookRotation(EndIK.position - Root.position, MidIK.position - Root.position); if (Mathf.Abs(RootToMid-MidToEnd) > RootToEnd) { Mid.localEulerAngles = new Vector3(Mid.localEulerAngles.x, Mid.localEulerAngles.y, 180); RootToMidQuat.eulerAngles = new Vector3(0, -270, 0); //-270 determines direction Knee will face } else if (Mathf.Abs(RootToMid+MidToEnd) < RootToEnd) { Mid.localEulerAngles = new Vector3(Mid.localEulerAngles.x, Mid.localEulerAngles.y, 0); RootToMidQuat.eulerAngles = new Vector3(0, -270, 0); } else { //uses pythagorean theroem to figure out angles float modz = Mathf.Asin((RootToMid*RootToMid+MidToEnd*MidToEnd-RootToEnd*RootToEnd)/(2*RootToMid*MidToEnd))*Mathf.Rad2Deg + 90; Mid.localEulerAngles = new Vector3(Mid.localEulerAngles.x, Mid.localEulerAngles.y, modz); RootToMidQuat.eulerAngles = new Vector3(0, -270, Mathf.Asin((RootToEnd*RootToEnd+RootToMid*RootToMid-MidToEnd*MidToEnd)/(2*RootToEnd*RootToMid))*Mathf.Rad2Deg - 90); } Root.rotation = RootToEndQuat * RootToMidQuat; //quaternion multiplication will make RootToMidQuat a child to the rotations of RootToEndQuat End.rotation = EndIK.rotation; } }
Used to build my own foot placement system in a 'Gravity like Mario-Galaxy' project. The main perso is a multi parts robot. Perfect...
Sorry for resurrecting an old thread, but could you please tell how to make it work for arms? As it is, it works for the left arm, but the right arm acts as if something's inversed / flipped / mirrored
Hey Deyama, I delved back into this script a little while ago and had some painful issues with flipping and stuff. If you can, I would definitely check out this plugin - https://www.assetstore.unity3d.com/en/#!/content/14290 The dev is great and its super easy to get cool IK stuff happening. It'll save you a bunch of time (and headaches). P.
Yeah, it surely would. Apart from it being a bit on the expensive side for me at the moment. But I probably will try it some day in the future. For now I figured out that it was that "-270" in that flips the thing. So need to change this for left and right arms or probably come up with some formula that automates the change. And there's still some similar issue with the elbow target
Hey nice one! I guess you could just put a little checkbox to flip that value when needed in the script. Yeah Final ik is a bit pricey. I guess it's because this kind of ik is only a small part of the plugins capabilities. P.