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:
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)
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:
var SkelControl_CCD_IK RightArmIK;
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.
Begin Object Name=WPawnSkeletalMeshComponent
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:
This finds the controller, assigns it a pointer, and now we have our variable fixed. So now, to USE IT!
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
RightArmIK = SkelControl_CCD_IK( mesh.FindSkelControl('RightArmIK') );
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:
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).
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;
Here's the code I used to make this check:
This should go before you set the EffectorLocation, so it actually has any effect.
if((LastX>0 && PlayerInput.aTurn <0) || (LastX<0 && PlayerInput.aTurn>0)) //if we're aiming backwards with the right hand, start from tail
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