No announcement yet.

Making a Skeletal Control to move an arm

  • Filter
  • Time
  • Show
Clear All
new posts

    Making a Skeletal Control to move an arm

    So this is pretty cool, for a number of reasons. It's also sort of hard to use and there's not a whole lot of tutorials for it that I know of (bet everyone's been writing these things while I've been of coding other stuff, but still). So ta-daa, tutorial time!

    What we're going to do here is fairly simple: we're going to attach a Skeletal Control to the arm of a standard Unreal Tournament Mesh/AnimTree, and tie this to our code so we can move this arm freely in a 2-dimensional plane. Picture the arms of Sackboy in LittleBigPlanet if you will. This is simply how I've done things, and i'm sure with more modeling skills etc you could make lots of cool things, like a spiffy prehensile tail or a trunk of an elephant or what-not.

    This tutorial uses a lot from the UT demo included in the UDK, so mostly it is simply overriding and stuff. It should be incredibly similar to use it if not inheriting from UT however.

    Anyway, to the ARM-MOVEMENT-MAKING!

    1. Open up the UDK editor. We need to create a Skeletal Control for us to use, so we need to fiddle with the editor a bit. Don't worry, we won't break anything (well, I won't, anyway).

    2. Copy CH_AnimHuman_tree. So we don't screw up the original things, make a copy of the AnimTree we'll be modifying. You'll be working with the copy, so name it something useful, like "CH_AnimHuman_MyTree" or similar. Remember the name and remember the package (or just dig them up later).

    3. Make a new Skeletal Control. Ok, so open up the AnimTree you just created if you haven't already. You should now see a big grey field filled with various Skeletal Controls, Animnodes and, most importantly a blue box name AnimTree containing the names of bones, such a b_LeftAnkle and b_LeftHand.
    Right click in the grey field somewhere where it's not too cluttered, and select New Skeletal Control. For this tutorial, since we're moving an arm, SkelControlLimb might seem like the ideal candidate. However, this only controls 2 bones and an entire arm actually contains a whole lot of bones. So instead, we will create a SkelControl_CCD_IK, which is basically like a tentacle or long string of bones that bruteforces bone position to work. They power the tentacles of the Reavers in Gears of War. You should now have a SkelControl_CCD_IK lying around, in a greenw indow titled "SkelControl_CCD_IK: None". We need to name this, so simply scroll down the properties list (should be at the bottom of the screen) until you find Controller Name. name it something without spaces, you need this name for the code later on. I named mine RightArmIK, as we're controlling Inverse Kinematics in the arm/with the arm/whatever.

    4. Connect the Skeletal Control to a bone on the AnimTree. This is where we connect the SkelControl to our skeleton. Simply drag from the "In" portion of the SkelControl node we created to the bone you want to control. This should be the outermost limb, or the bottom child, of the limb. Mine connected to b_RightWeapon so I have control over the outermost limb of the arm, with the weapon, hand and everything.

    5. Set the properties of the SkelControl. This step is probably the hardest part of the entire tutorial, as you need to make sure all the AngleConstraints work properly and this is a rather enormous pain. You also need to check just how many bones you want in the chain, as this will affect a lot of things. There is a lot of trial and error in this stage, and i have not quite gotten the hang of it myself, so if someone gets a better value for this whole thing, feel free to let me know.
    For our arm-purposes, we will set "Num Bones" in the CCD properties to 5. This goes from the weapon up to the shoulder blade, more or less, giving a fairly good control of the entire right arm.
    AngleConstraints are how much each individual bone may move and, like I said, I am not entirely sure how this works with one bone in relation to another. Trial and error, people.
    Mine are set to:
    [0]= 0.000
    [1]= 0.900
    [2]= 0.200
    [3]= 0.900
    [4]= 0.100
    Max Angle Steps= 2.100 (the sum of the steps)

    Remember to set your Controller Strength appropriately. 0.0 is no control, 1.0 is full control, entirely overriding any animations.

    Finally, set "Ignore at or above LOD" to a high number, like 1000, so you can see it even from a distance.

    6. Save the Package We are more or less done with the editor now, so save the package. Now we're getting to the fun/comfortable part: Coding!

    7. Set up the pawn class to handle your SkelControl. Open up the pawn class you are using (you can just extend UTPawn, or the UT2D pawn or something to keep matters simple if you want).
    We need to add a new variable for our SkelControl, so simply add a new Skelcontrol variable named after the type and a useful name (I prefer the same as the one we used for the controller name in the anim tree)
    For example:
    var SkelControl_CCD_IK RightArmIK;
    There, we now have a SkelControl that we can call from other classes (our PlayerController class, for instance). We do not have it initialized or have anything to initialize, however, so scroll down to the DefaultProperties. We need to change which AnimTreeTemplate our pawn uses, so it finds our shiny new SkelControl. If you're extending UTPawn, then this value is set in WPawnSkeletalMeshComponent, so override that one. To do this, just add this in your DefaultProperties:
    Begin Object Name=WPawnSkeletalMeshComponent
    AnimTreeTemplate=AnimTree 'CH_AnimHuman_MyTree.AT_CH_MyTree'
    End Object
    Where the AnimTree path should be the name of your package and AnimTree you created. If you can't remember it, just right-click it in the content broswer of the editor and choose "copy full name to clipboard" and paste.

    8. Initialize the skeletal control. Ok, so now we have the controller and the AnimTree set up, but we don't have the Controller in the AnimTree actually hooked up to our variable. The best way to do this, if UTPawn does it the best, is to initialize these in PostInitAnimtree. So, simply overriding again, we do this:
    simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
    	RightArmIK = SkelControl_CCD_IK( mesh.FindSkelControl('RightArmIK') );
    This finds the controller, assigns it a pointer, and now we have our variable fixed. So now, to USE IT!

    9. Modifying PlayerController. Since the pawn is controlled by playerController, we're going to modify PlayerController to give us total control of the arm. Once again, i inherited from Unreal tournament, so these steps might be different if done otherwise. This bit is going to be a lot of code, so apologies in advance. The basic thing being done is that in UpdateRotation we use the values of the mouse input or the right stick input on a gamepad to declare a vector position which our arm will then strive to reach.
    So, basic code:
    function UpdateRotation(float DeltaTime)
    	local vector ArmLocation;
    	local MyPawn Utp;
    	ArmLocation.Z += PlayerInput.aLookup*10+100; //giant multiplier so we extend the arm more.
    	ArmLocation.X += PlayerInput.aTurn*10; 
    	Utp=MyPawn(Pawn); //this simply gets our pawn so we can then point to our SkelControl
    	Utp.RightArmIK.EffectorLocation=Pawn.Location + ArmLocation; 
    There, on that last line we set the EffectorLocation, meaning where the limb is trying to be, to the ArmLocation Vector, and the arm should move whenever you move the mouse. Now, if you're running this into UT2D like i did, you might notice that if your character is facing the opposite direction from where you are pointing your arm, it does this sort of weird hook thing when trying to aim backwards, as opposed to stretching out fully. This issort of LBP-ish, so we can keep it, but if you want to get rid of it, keep track of where the character is looking and where the arm is pointing (I stored the "I'm looking this way" direction into a float called LastX) and then set bStartFromTail to false if they're opposites. you might remember bStartFromTail from the properties in the editor, and you can set it in code as well. If true, this means we start from the opposite end of the chain and not the bone we started the chain in (so we would not start from the weapon but around the shoulder).
    Here's the code I used to make this check:
    if((LastX>0 && PlayerInput.aTurn <0) || (LastX<0 && PlayerInput.aTurn>0)) //if we're aiming backwards with the right hand, start from tail
    This should go before you set the EffectorLocation, so it actually has any effect.

    And that's it! I didn't realize I did so many steps to get this to work! Sorry about the lack of pictures, sorry about the mess and, in general, I'm just sorry I guess. This is my first tutorial and I think it is one people might find some use out of.
    Thanks for reading, and hopefully this will keep you from pulling your hair like I did.

    Video of this working hopefully forthcoming soon as well!

    EDIT: oh yeah, durr, if there's any questions, just ask and i'll try and answer to the best of my capabilites.

    UPDATE: So, finally got around to uploading the gameplay video of our project we did, which shows this off. Video can be viewed HERE

    Awesome, I'll try it as soon as I get home.


      Very cool, not the usual tutorial and very much appreciated!


        I like it, it will probably help me in the future...


          Nice tutorial. Would you mind looking at the issue I've got here please. Sounds like this is almost what I'm after but using a UDKSkelControl_LookAT -


            Originally posted by Monophobe View Post
            Nice tutorial. Would you mind looking at the issue I've got here please. Sounds like this is almost what I'm after but using a UDKSkelControl_LookAT -
            Sorry I didn't help you out with that, looks like it sorted itself out however. Came down all feverish and stuff and didn't exactly feel like thinking


              I had no luck following this tutorial. Could you help me find out what's wrong?

              I followed it exactly, but on top of an already existing code base (UT2D heavily modified code), so there might be other code breaking it.

              The only thing I did different from you was the bone, cause in UDK there is no b_RightWeapon, but there is b_IKGun which seems to be the same (I could be wrong).

              I sent a debug message to console to make sure that it Location got set, here's the output when I move the mouse (and nothing happens to the pawn).

              Edit: nevermind, the bone b_RightWeapon was there, I just had to add it to blue box. Now it works, it's just a matter of tuning it so it doesnt behave all crazy.


                Thanks for the tutoria, was actually looking into controlling my pawns arms.


                  How did this tutorial get missed on
                  It should be there. This is one of those things that can take a lot of work to figure out.


                    Hmm. I'm poking around through some of this stuff, and I'm noticing that all of the subclasses of SkelControlBase define their own things, even though most of them share the exact same information. Is there a reason why this is, I wonder?


                      Tip: If you want to see the effects of your IK in the editor, before you start putting the code down (especially useful if you find you need to use a different SkelControl, since they aren't compatible with each other codewise), if you select the SkelControl in the AnimTree editor, there should be a little diamond that you can move around in the Preview window, that will allow you to adjust the various Location attributes in the SkelControl (EffectorLocation in this example, BoneTranslation in another, etc). Then you can adjust the slider in the animtree to see how it will affect your bones.


                        This is fine if you want the arm to reset constantly to its standard position, but what if you want a 1:1 control of the arm via input?

                        Simply intialize ArmLocation and the Pawn as variables of your PlayerController, as such:

                        class MyPlayerController extends UTPlayerController;
                        var vector ArmLocation;
                        var MyPawn Utp;
                        and delete the local calls in UpdateRotation.


                          Originally posted by Wyldhunt View Post
                          How did this tutorial get missed on
                          It should be there. This is one of those things that can take a lot of work to figure out.
                          If the original author is ok with it, I will put it on the website in quick time. It's a great tutorial, on a topic I been trying to figure out myself


                            Originally posted by Sir. Polaris View Post
                            If the original author is ok with it, I will put it on the website in quick time. It's a great tutorial, on a topic I been trying to figure out myself
                            I PMed them, but they didn't respond. I'm not sure if they still frequent the boards.


                              Yea, I did as well a well a while back. It took a week or so for him to respond