Search Unity

How to rig and use an excavator arm correctly?

Discussion in 'Animation' started by OctoMan, Aug 30, 2016.

  1. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    prev.PNG
    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.
    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.
    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.
     
  2. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    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.
     
    theANMATOR2b likes this.
  3. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    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.
    layertree.PNG
    Thats not how i animated it. What do i miss?
     
  4. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    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.
     
  5. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    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.
    speedmulti.PNG
    But there is a coding issue i have.

    Code (csharp):
    1.  
    2. //BIG ARM
    3. if(Input.GetKey(KeyCode.E)&& !Input.GetKey(KeyCode.D))
    4. {
    5. anim.SetFloat("BigArmSpeed",1f);
    6. }
    7. elseif(!Input.GetKey(KeyCode.E)&&Input.GetKey(KeyCode.D))
    8. {
    9. anim.SetFloat("BigArmSpeed",-1f);
    10. }
    11. else
    12. {
    13. anim.SetFloat("BigArmSpeed",0f);
    14. }
    15.  

    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?
     
    Last edited: Aug 31, 2016
  6. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    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
     
  7. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    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.
     
  8. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    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);
     
  9. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790
    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.
     
  10. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    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.
     
    theANMATOR2b likes this.
  11. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    Sorry for the late reply, i'll add the coding and will tell if that's working.
     
  12. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    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?
    Code (csharp):
    1.  
    2. //-------------------------------------------------BIGARM-----------------------------------------------------------------
    3. if(Input.GetKey(KeyCode.E)&& !Input.GetKey(KeyCode.D)&&(!changeBigArmDirection || !nextBigArmIsUp))
    4. {
    5. changeBigArmDirection=false;
    6. nextBigArmIsUp=true;
    7. anim.SetFloat("BigArmSpeed",1f);
    8. }
    9. if(!Input.GetKey(KeyCode.E)&&Input.GetKey(KeyCode.D)&&(!changeBigArmDirection || nextBigArmIsUp))
    10. {
    11. changeBigArmDirection=false;
    12. nextBigArmIsUp=false;
    13. anim.SetFloat("BigArmSpeed",-1f);
    14. }
    15. else
    16. {
    17. //anim.SetFloat("BigArmSpeed",0);
    18. }
    19.  
    20.  
    21.  
    22. //-------------------------------------------------------SMALLARM-------------------------------------------------------------
    23. if(Input.GetKey(KeyCode.W)&& !Input.GetKey(KeyCode.S)&&(!changeSmallArmDirection || !nextSmallArmIsUp))
    24. {
    25. changeSmallArmDirection=false;
    26. nextSmallArmIsUp=true;
    27. anim.SetFloat("SmallArmSpeed",1f);
    28. }
    29. if(!Input.GetKey(KeyCode.W)&&Input.GetKey(KeyCode.S)&&(!changeSmallArmDirection || nextSmallArmIsUp))
    30. {
    31. changeSmallArmDirection=false;
    32. nextSmallArmIsUp=false;
    33. anim.SetFloat("SmallArmSpeed",-1f);
    34. }
    35. else
    36. {
    37. //anim.SetFloat("SmallArmSpeed",0);
    38. }
    39.  
    40.  
    41.  
    42.  
    43.  
    44. //----------------------------------------------------------SHOVEL-----------------------------------------------------------------
    45. if(Input.GetKey(KeyCode.Q)&& !Input.GetKey(KeyCode.A)&&(!changeShovelDirection || !nextShovelIsUp))
    46. {
    47. changeShovelDirection=false;
    48. nextShovelIsUp=true;
    49. anim.SetFloat("ShovelSpeed",1f);
    50. }
    51. if(Input.GetKey(KeyCode.A)&& !Input.GetKey(KeyCode.Q)&&(!changeShovelDirection || nextShovelIsUp))
    52. {
    53. changeShovelDirection=false;
    54. nextShovelIsUp=false;
    55. anim.SetFloat("ShovelSpeed",-1f);
    56. }
    57. else
    58. {
    59.  
    60. //anim.SetFloat("ShovelSpeed",0);
    61. }
    62.  
    The reset Script has:
    Code (csharp):
    1.  
    2.   publicvoidSetBigArmSpeed()
    3. {
    4. Debug.Log("bigarmspeedresetted");
    5. excav.changeBigArmDirection=true;
    6. excav.anim.SetFloat("BigArmSpeed",0f);
    7. }
    8.  
    9. publicvoidSetSmallArmSpeed()
    10. {
    11. Debug.Log("smallarmspeedresetted");
    12. excav.changeSmallArmDirection=true;
    13. excav.anim.SetFloat("SmallArmSpeed",0f);
    14. }
    15.  
    16. publicvoidSetShovelSpeed()
    17. {
    18. Debug.Log("shovelspeedresetted");
    19. excav.changeShovelDirection=true;
    20. excav.anim.SetFloat("ShovelSpeed",0f);
    21. }
    22.  
     
    Last edited: Aug 31, 2016
  13. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790
    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. o_O
     
  14. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    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:
    stateshov.PNG
    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.
     
  15. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    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.
     
    theANMATOR2b likes this.
  16. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    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
     
  17. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    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.
     
    Last edited: Aug 31, 2016
  18. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    Requested references:

    bigref.PNG smallref.PNG shovref.PNG
    animref.PNG
     
  19. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    I just slighly changed what you gave me but i took an int instead.

    Code (csharp):
    1. //-------------------------------------------------BIG ARM-----------------------------------------------------------------
    2.         if (Input.GetKey(KeyCode.E) && !Input.GetKey(KeyCode.D) && anim.GetInteger("BigArmPosition")!=2)
    3.         {
    4.             anim.SetInteger("BigArmPosition",1);
    5.             anim.SetFloat("BigArmSpeed",1f);
    6.         }
    7.         else if (!Input.GetKey(KeyCode.E) && Input.GetKey(KeyCode.D) && anim.GetInteger("BigArmPosition")!=0)
    8.         {
    9.             anim.SetInteger("BigArmPosition",1);
    10.             anim.SetFloat("BigArmSpeed", -1f);
    11.         }
    12.         else
    13.         {
    14.             anim.SetFloat("BigArmSpeed", 0);
    15.         }
    16.  
    17. //-------------------------------------------------------SMALL ARM-------------------------------------------------------------
    18.         if (Input.GetKey(KeyCode.W) && !Input.GetKey(KeyCode.S) && anim.GetInteger("SmallArmPosition")!=2)
    19.         {
    20.             anim.SetInteger("SmallArmPosition",1);
    21.             anim.SetFloat("SmallArmSpeed",1f);
    22.         }
    23.         else if (!Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.S) && anim.GetInteger("SmallArmPosition")!=0)
    24.         {
    25.             anim.SetInteger("SmallArmPosition",1);
    26.             anim.SetFloat("SmallArmSpeed", -1f);
    27.         }
    28.         else
    29.         {
    30.             anim.SetFloat("SmallArmSpeed", 0);
    31.         }
    32.    //----------------------------------------------------------SHOVEL-----------------------------------------------------------------
    33.         if (Input.GetKey(KeyCode.Q) && !Input.GetKey(KeyCode.A)  && anim.GetInteger("ShovelPosition")!=2)
    34.         {
    35.             anim.SetInteger("ShovelPosition",1);
    36.             anim.SetFloat("ShovelSpeed", 1f);
    37.         }
    38.         else if (Input.GetKey(KeyCode.A) && !Input.GetKey(KeyCode.Q)  && anim.GetInteger("ShovelPosition")!=0)
    39.         {
    40.             anim.SetInteger("ShovelPosition",1);
    41.             anim.SetFloat("ShovelSpeed", -1f);
    42.         }
    43.         else
    44.         {
    45.  
    46.             anim.SetFloat("ShovelSpeed", 0);
    47.         }
    On Begin and Exit i call those:

    Code (csharp):
    1. public void BigArmPosition(int pos)//0=down,1=middle,2= up
    2. {
    3. excav.anim.SetInteger("BigArmPosition",pos);
    4. excav.anim.SetFloat("BigArmSpeed",0f);
    5. }
    6.  
    7. public void SmallArmPosition(int pos)//0=down,1=middle,2= up
    8. {
    9. excav.anim.SetInteger("SmallArmPosition",pos);
    10. excav.anim.SetFloat("SmallArmSpeed",0f);
    11. }
    12.  
    13. public void ShovelPosition(int pos)
    14. {
    15. excav.anim.SetInteger("ShovelPosition",pos);
    16. excav.anim.SetFloat("ShovelSpeed",0f);
    17. }

    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?
     
    Last edited: Aug 31, 2016
  20. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    You can try setting the anim compression to optimal and reposition the events one frame towards the middle
     
  21. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    Currently everthing works as it should, and i'm happy with the result. I added another layer now for the rotation of the upper part, and the last will be the door to open and close. Also added basic movement. And yeah thats i can and will do so far. I'm glad you helped me out.
     
    theANMATOR2b and GrischaG like this.
  22. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    What was the final Problem?
     
  23. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    The final or last problem was, i couldn't make it work as you suggested, but after i modified all as written above, everything works now, as i already explained.

    So now i can do (ignore the flying cables ;) ):
    Drive

    BigArm

    SmallArm

    Shovel

    All Together

    Rotate



    Now again i just need to rig the cables again. And animate open/ close door.
     
    theANMATOR2b likes this.
  24. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    Nice, looks good :)
     
  25. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168
    Yea thanks alot again :) I'm glad you helped me out.
     
  26. CIAARES

    CIAARES

    Joined:
    Nov 29, 2019
    Posts:
    2
    Estaba trabajando con su activo, funciona bien, pero no necesito:
    if (Input.GetKey (KeyCode.W) &&! Input.GetKey (KeyCode.S) && anim.GetInteger ("BigArmPosition")! = 2)
    y necesito usarlo con un joystick que es: Input.GetAxis ("Horizontal")

    ----------------------
    Spanish

    estuve trabajando con tu Asset funciona bien , pero no necesito :
    if (Input.GetKey (KeyCode.W) &&! Input.GetKey (KeyCode.S) && anim.GetInteger ("BigArmPosition")! = 2)
    y necesito usarlo con joystick es decir : Input.GetAxis("Horizontal")
     
  27. CIAARES

    CIAARES

    Joined:
    Nov 29, 2019
    Posts:
    2
    I need to change it to get axis
     
  28. OctoMan

    OctoMan

    Joined:
    May 10, 2014
    Posts:
    168