How to rig and use an excavator arm correctly?


I’m currently working on an excavator. My idea was to create the animations in 3Ds Max. This way i want to make sure, all cables and hydraulics are already animated, and don’t need a lookat script for the hydraulics and can use rigged/animated cables in unity.

The first problem i had was to keep the current animation state, of the shovel, small arm and big arm.
I created a Direct Blend Tree, so i could use 3 floats to determine and keep states at where they are.
2769978--200246--blend.PNG
But they blended so weird, that i had an weird explosion effect included a scale, when i blended at least 2 of them.
2769978--200245--scaledExploded.PNG

When i export the FBX from 3Ds Max, it take and key everything i select. So my problem seems to be in the FBX, since all is keyed there.

Do i have to go into the animation clips, and delete everything i don’t need in that particular animation?
Or is there an easier workflow? May i have to split all animated pieces in 3Ds Max and put them back/link together in unity?

Or am i completely wrong and need to make it completly different?

Please help me.

Hi,

i would use the base layer for the chains to rotate when moving. The second layer for the big arm, the third for the small arm and a fourth for the shovel.
Each of the layers is additive! and contains a 1D blendtree.
Example for the big arm: You need two animations. One where the big arm is rotated completely down and one where he is rotated up. Both animations contain no movement, just two Frames with the arm at the specific position.
With this setup you can blend between the positions of the arms.

That is what i would try.

1 Like

Hi,
I’m currently trying it your way. How about the Layer Weight? Do it everywhere have to be 1? At 0 i got no effect on blending parameters. But on 1 It is overdoing it all. Also the Hydraulics are not working properly.


Thats not how i animated it. What do i miss?

Yes the weight should be 1.
Make sure, that BigArmClose and Open only moves the bigarm and no other animation moves the big arm.

If that doesn´t work you can try a mask for each layer which enables just the spezific arm.

Thanks again,

when i start the scene the arm automaticaly rises. So somehow it seems the thresholds are wrong.
Nothing else is driving the Big Arm.

With a threashold betwen 0 and 2, but capping blend between 0 and 1 it seems to work, not looking 100% but looks good so far.

I stumbled appon another possibility without a blend tree but using the speed multiplier with your multilayer setup.
2770133--200257--speedmulti.PNG
But there is a coding issue i have.

//BIG ARM
if(Input.GetKey(KeyCode.E)&& !Input.GetKey(KeyCode.D))
{
anim.SetFloat("BigArmSpeed",1f);
}
elseif(!Input.GetKey(KeyCode.E)&&Input.GetKey(KeyCode.D))
{
anim.SetFloat("BigArmSpeed",-1f);
}
else
{
anim.SetFloat("BigArmSpeed",0f);
}

If i hold down a key to long, another number still rises in the background. So when i want to get back, i have to hold the same amount i pressed earlier and the it start to go back. Anyone knows why?

You could set an event at the end and at the beginning of the animation which calls a method to set BigArmSpeed to zero.
Or post a picture of the layer for big arm

Sadly it’s not helping since the button press overrides it.

The BigArm Layer just contains the singel open animation. So with my piece of code i would just use the speed multiplier to change directions and speed of cause. Basicly it works, but again, i still seem to modifiy any value in the background i don’t know of. Maybe i try a bool now.

Edit: i guess that wont work since on the 1 st frame and last frame i would have to set it to false, but while sitting on it, i guess i can’t do anything anymore.

Then prevent it from getting overriden. Use one bool for the last direction (nextIsUp) And one (changeDir) which is set in the method to indicate that the next input must be in opposit direction ignoring everithing else:

if(Input.GetKey(KeyCode.E)&& !Input.GetKey(KeyCode.D) && (!changeDir || !nextIsUp))
{
changeDir = false;
nextIsUp = true;

if(Input.GetKey(KeyCode.E)&& !Input.GetKey(KeyCode.D) && (!changeDir || nextIsUp))
{
changeDir = false;
nextIsUp = false;

void OnEventStartEndAnimation()
{
changeDir = true;
anim.SetFloat(“BigArmSpeed”,0f);

This is a real good setup. Nice!
I would add the big arm could have three additional states, 1- directly forward, 2- pivoted left and 3- pivoted right since the entire rig swivels at the base of the big arm.

Thanks @theANMATOR2b
Yes i considered the big arm not as the one with the engine etc. So for rotating the tower we would need an other layer between base and big arm.

1 Like

Sorry for the late reply, i’ll add the coding and will tell if that’s working.

So far so good, the Big Arm works in both directions now. But if i try the same with the small arm and the shovel, only one direction works like this, the other stucks in the end. No idea why.

Edit 1: Never mind, it resets on restart, since the animations from the FBX are read-only. So it doesn’t save the events in the animation window. I’ll try to use the Events in the FBX directly.

Edit 2: In the Animation Clip Event, same problem: Big Arm Works now, Small Arm and Shovel only to one site. But once they reached the highest point, they don’t reset like the Big Arm. I’ll look into the code again, maybe it’s a typo. But i don’t think so.

Edit 3: Seems i have no coding errors. It just doesn’t work. I try reimport the model. Maybe something weird happen with it.

Edit 4: I can’t find the Problem, even after reimporting it doesn’t work. Only BigArm works, Small Arm and Shovel don’t… or at least do the Up direction, but not down/ or maybe 1 frame down.

Time for some code?

//-------------------------------------------------BIGARM-----------------------------------------------------------------
if(Input.GetKey(KeyCode.E)&& !Input.GetKey(KeyCode.D)&&(!changeBigArmDirection || !nextBigArmIsUp))
{
changeBigArmDirection=false;
nextBigArmIsUp=true;
anim.SetFloat("BigArmSpeed",1f);
}
if(!Input.GetKey(KeyCode.E)&&Input.GetKey(KeyCode.D)&&(!changeBigArmDirection || nextBigArmIsUp))
{
changeBigArmDirection=false;
nextBigArmIsUp=false;
anim.SetFloat("BigArmSpeed",-1f);
}
else
{
//anim.SetFloat("BigArmSpeed",0);
}



//-------------------------------------------------------SMALLARM-------------------------------------------------------------
if(Input.GetKey(KeyCode.W)&& !Input.GetKey(KeyCode.S)&&(!changeSmallArmDirection || !nextSmallArmIsUp))
{
changeSmallArmDirection=false;
nextSmallArmIsUp=true;
anim.SetFloat("SmallArmSpeed",1f);
}
if(!Input.GetKey(KeyCode.W)&&Input.GetKey(KeyCode.S)&&(!changeSmallArmDirection || nextSmallArmIsUp))
{
changeSmallArmDirection=false;
nextSmallArmIsUp=false;
anim.SetFloat("SmallArmSpeed",-1f);
}
else
{
//anim.SetFloat("SmallArmSpeed",0);
}





//----------------------------------------------------------SHOVEL-----------------------------------------------------------------
if(Input.GetKey(KeyCode.Q)&& !Input.GetKey(KeyCode.A)&&(!changeShovelDirection || !nextShovelIsUp))
{
changeShovelDirection=false;
nextShovelIsUp=true;
anim.SetFloat("ShovelSpeed",1f);
}
if(Input.GetKey(KeyCode.A)&& !Input.GetKey(KeyCode.Q)&&(!changeShovelDirection || nextShovelIsUp))
{
changeShovelDirection=false;
nextShovelIsUp=false;
anim.SetFloat("ShovelSpeed",-1f);
}
else
{

//anim.SetFloat("ShovelSpeed",0);
}

The reset Script has:

  publicvoidSetBigArmSpeed()
{
Debug.Log("bigarmspeedresetted");
excav.changeBigArmDirection=true;
excav.anim.SetFloat("BigArmSpeed",0f);
}

publicvoidSetSmallArmSpeed()
{
Debug.Log("smallarmspeedresetted");
excav.changeSmallArmDirection=true;
excav.anim.SetFloat("SmallArmSpeed",0f);
}

publicvoidSetShovelSpeed()
{
Debug.Log("shovelspeedresetted");
excav.changeShovelDirection=true;
excav.anim.SetFloat("ShovelSpeed",0f);
}

From your explanation it sounds like the smaller arm and bucket are not registering they have completed the up motions - so they are not in the up position (ready to move down).
Whatever that translates to in code - I would guess that is the problem.
Can you show your mecanim state machine?

So - as most know - I don’t code - but looking at your code this is what I see - maybe this might help.
I see there is a known up position and when a button is pressed the arm/bucket moves at a speed of 1 to ??
I don’t see in your code the second position for the arm/bucket. So how does it know when to stop - where to stop?
Then when the arm/bucket is up the code says change the speed to -1 when another button is pushed but it doesn’t say where to go to. There is no down position mentioned - only up=false, direction=false.

I’m sure I am missing something - and not reading it totally correctly - cause I’m an art guy. But maybe just looking and writing what I see may help. Maybe not. :eyes:

Hey, first of all thanks for your comment.

When i check the bools i can see they notifiy they have to return in the other direction, once they hit the last animation frame.

So when i press up for example it sees, ah user input was up, so nextIsUp = true; If i press key for down nextIsUp=false.
So when it hit’s the first or last frame of the animation, i change the directions to true, so depending on nextUp true or false, it will handle the next input. Irrelevant to the direction change, because it’s true at that point.

Once it’s up it sees ok last input was up, and change dir = true, so i can only input down. He registers that for a millisecond when i press, but stops moving instandly, no idea why.

Statemaschine just looks as simple as that for all current layers:


1 Open Animation per state.
Big Arm Layer = Big Arm Open Animation and set BigArmSpeed as Speed multiplier.
Small Arm Layer = Small Arm Open Animation and set SmallArmSpeed as Speed multiplier.
Shovel as seen above same setup

The above reset methods are called in the animations on the first and last frame themself.

Blend trees weirdly and sadly don’t work because of unknown reasons.(weird animations)

I try it now with animator bools, maybe there is a strange bug. I don’t know.

Animator Bools didn’t work, since only strings, floats and ints are supported in animation window.

So now extracted the Animation Clips, so they are not read only anymore, and use those in mechanim, instead of the original ones. I use an integer now for each piece, somehow as @theANMATOR2b suggested. So when any piece is in the middle of the animation, it’s set to 1, if it’s in the animation beginning it is 0, in the end it becomes 2.

Some fiddly workaround to figure out first, but returns 100% animations, with everthing needed, like cables(i need to rig them again) and with hydraulic cylinders. Pretty nice!

This way i can use finally all Excavator Arm Pieces independently. Thanks alot @GrischaG about the Layers, i didn’t knew before how they work, or what they are used for. But now i know i can have severy animations and states active at the same time, awesome! And use Additive to make the blending working properly.

Currently i’m not sure how i will handle the base and wheels, but i believe i just code them normally.

1 Like

Hey,
could you make a picture of the import options from big arm as reference and the shove or small arm were its not working?
And open the foldout where you set the events.

Maybe big arm is in loop mode and the others not. Or something else is wrong.

Grischa

I can do that, i believe the problem was, only one, in this case the BigArm, has the events working, all others became inactive or lost the connection. That’s why i made sure, to “extract” the animations, so they wont have “read only”, anymore. I will test that first.

Nothing was on loop!

Edit: I tested that again, no luck, it still doesn’t work. Only my version works.

Requested references:


I just slighly changed what you gave me but i took an int instead.

//-------------------------------------------------BIG ARM-----------------------------------------------------------------
        if (Input.GetKey(KeyCode.E) && !Input.GetKey(KeyCode.D) && anim.GetInteger("BigArmPosition")!=2)
        {
            anim.SetInteger("BigArmPosition",1);
            anim.SetFloat("BigArmSpeed",1f);
        }
        else if (!Input.GetKey(KeyCode.E) && Input.GetKey(KeyCode.D) && anim.GetInteger("BigArmPosition")!=0)
        {
            anim.SetInteger("BigArmPosition",1);
            anim.SetFloat("BigArmSpeed", -1f);
        }
        else
        {
            anim.SetFloat("BigArmSpeed", 0);
        }

//-------------------------------------------------------SMALL ARM-------------------------------------------------------------
        if (Input.GetKey(KeyCode.W) && !Input.GetKey(KeyCode.S) && anim.GetInteger("SmallArmPosition")!=2)
        {
            anim.SetInteger("SmallArmPosition",1);
            anim.SetFloat("SmallArmSpeed",1f);
        }
        else if (!Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.S) && anim.GetInteger("SmallArmPosition")!=0)
        {
            anim.SetInteger("SmallArmPosition",1);
            anim.SetFloat("SmallArmSpeed", -1f);
        }
        else
        {
            anim.SetFloat("SmallArmSpeed", 0);
        }
   //----------------------------------------------------------SHOVEL-----------------------------------------------------------------
        if (Input.GetKey(KeyCode.Q) && !Input.GetKey(KeyCode.A)  && anim.GetInteger("ShovelPosition")!=2)
        {
            anim.SetInteger("ShovelPosition",1);
            anim.SetFloat("ShovelSpeed", 1f);
        }
        else if (Input.GetKey(KeyCode.A) && !Input.GetKey(KeyCode.Q)  && anim.GetInteger("ShovelPosition")!=0)
        {
            anim.SetInteger("ShovelPosition",1);
            anim.SetFloat("ShovelSpeed", -1f);
        }
        else
        {

            anim.SetFloat("ShovelSpeed", 0);
        }

On Begin and Exit i call those:

public void BigArmPosition(int pos)//0=down,1=middle,2= up
{
excav.anim.SetInteger("BigArmPosition",pos);
excav.anim.SetFloat("BigArmSpeed",0f);
}

public void SmallArmPosition(int pos)//0=down,1=middle,2= up
{
excav.anim.SetInteger("SmallArmPosition",pos);
excav.anim.SetFloat("SmallArmSpeed",0f);
}

public void ShovelPosition(int pos)
{
excav.anim.SetInteger("ShovelPosition",pos);
excav.anim.SetFloat("ShovelSpeed",0f);
}

Again, i don’t know whats wrong with yours, since i basicly did the same.
Edit 1: I just notice i havn’t set a root node, but i think it doesn’t matter huh?

You can try setting the anim compression to optimal and reposition the events one frame towards the middle