Search Unity

Simple question about Mechanim multiple characters

Discussion in 'Animation' started by frankmat, May 14, 2014.

  1. frankmat

    frankmat

    Joined:
    Sep 14, 2013
    Posts:
    42
    This should be a simple answer I am hoping... but I can't find it anywhere!! :(

    I've downloaded the sample project file from here https://www.youtube.com/watch?v=Xx21y9eJq1U

    Obviously I can move this human around easy enough... my question is what if you want want yo animate 2,3 or even 20 humans independently.

    1. Do you use the same Animation Controller?
    2. If YES... then how do you go about doing this?
    3. If NO... does that mean you have to have a seperate animation controller for each human/avatar?

    Basically what I am trying to do is move characters independently based on variables coming from a file.... relating to position from and to... time etc. I want to use a different animation state depending on what they are doing: eg: walking, running, jumping.

    Unfortunately when I attach the botscript.cs to the avatars... they all animate at the same time... and i am stuck.
     
    Last edited: May 14, 2014
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    The answer is YES. Animation Controllers use animation parameters, which act as control inputs. For example, a trigger parameter named Jump might transition the avatar from the Idle state to the Jumping state, and a float parameter named Speed might control how fast the avatar should run.

    Generally one or more higher-level control scripts set animation parameters to make the avatar do something.

    The demo botscript sets the same animation parameter values across the board, which is why every avatar that uses the script behaves the same.

    To make each avatar behave independently, the control script(s) must set the animation parameters independently. Usually a control script does this by sensing the avatar's state in the world and setting the appropriate parameters. For example, if the character "sees" a zombie, the script might set parameters that cause the avatar to turn and run away. Or if the character is at the base of a wall, the script might set a trigger parameter that plays a "climb over wall" animation. These scripts dip into the realm of AI.

    They're often hand-written, but you can use tools such as PlayMaker or AI for Mecanim, too.

    Now, if you want an avatar to behave differently on the animation level, you can use a different Animation Controller. A snake might have an animation controller that has states such as Slither, Coil, and Strike. A bird might have a different animation controller with states such as Fly, Land, and Waddle.
     
    Last edited: May 14, 2014
  3. frankmat

    frankmat

    Joined:
    Sep 14, 2013
    Posts:
    42
    Ok thanks.. I think you kind of answered my question there.

    I understand everything about the animation parameters controlling the avatars state. In my case though I have animations that are the same for every character. eg: soccer player. They kick, head, run etc

    Because I have 10 outfielders.... I was wondering could I use the single animation controller to do this? or do I have to have 10 animation controllers that are effectively identical copies of each other ... but are assigned to a different Avatar inside the Inspector? so that it affects a different avatar?

    I guess what I am doing is different because there is no AI as such.... what I am doing is effectively "replaying" movements of 11 characters on a field based on values within a text file.... the values in the text file are also going to store "Animation Type" as well so that I can script which animation to play at which particular time of the game.
     
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    You can use a single animation controller. The difference between each character will be the control inputs provided by the higher-level script.

    In this case, your higher-level script will need to set the character's animation parameters so that the avatar does what's specified in the text file. It might even be better to use Animator.Play() or .CrossFade() to directly change the animation state rather than setting an animation parameter.

    For example, say character 3 kicks at time 02:49. Then at 02:49 the script on character 3 (but only on character 3) should either set a trigger parameter such as "Kick" to true, or play an animation state directly such as Animator.Play("Kick").
     
  5. frankmat

    frankmat

    Joined:
    Sep 14, 2013
    Posts:
    42
    Thanks really appreciate this. Just so I understand you'd have 1 animator controllers but each character would have its own script? So in effect 11 player scripts each assigned to a different avatar?
     
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    It would probably be the same script file (e.g., OutfielderControl.cs) but a different instance on each character. Scripts have public properties that you set in the inspector. You can define a property for the character's number. This value would be unique for each character. For example:
    Code (csharp):
    1.  
    2. public class OutfielderControl : MonoBehaviour {
    3.     // This is my character number:
    4.     public int number;
    5. }
    6.  
    Since you have a single text file, you might want to do it slightly differently. You can have a master script that processes the entire text file and sends controls to each of the characters. Here's a really simplified example:
    Code (csharp):
    1.  
    2. public class MasterScript : MonoBehaviour {
    3.  
    4.     // Assign the characters to this array:
    5.     public GameObject[] outfielders;
    6.  
    7.     // Call this when you process a line in the text file:
    8.     void ProcessTextFileItem(int outfielderNumber, string newState) {
    9.         // Change the outfielder's animation state:
    10.         outfielders[outfielderNumber].GetComponent<Animator>().Play(newState)
    11.     }
    12.  
    13. }
    14.  
     
  7. frankmat

    frankmat

    Joined:
    Sep 14, 2013
    Posts:
    42
    Great thanks. Ironically that is exactly what I had already done... I had a gameEngine script and an Outfielder script. I had the players as a GameObject array as you have done.

    The difference was that I wasn't playing the animation from my gameEngine script as you are. I wanted to load the textfile in the gameEngine and then pass the variables to the Outfielder script... and then calculate and run the animatiom from there.

    What I've now decided to do is have a calculationAnim.cs script with functions that return a value back to the gameEngine based on the parameters passed. I'll then change the outfielder anim state as you have done above.. and play at certain points.

    Thanks very much for this. If anything it confirmed that the track I was on initially was the right one... so I'll revert back to it knowing I am not wasting my time!
     
  8. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Happy to share my thoughts on this. Someone else might suggest using the legacy animation system because animation is easier to control on a frame-by-frame basis. But Mecanim is the future so I try to stick with it.
     
  9. frankmat

    frankmat

    Joined:
    Sep 14, 2013
    Posts:
    42
    Yes I am trying as hard as I can to run with Mecanim... the legacy animation system will eventually be phased out.

    I'm still doing some Proof of concepts to see whether legacy or mecanim is the way to go... and still none the wiser. Because my game is replaying the movement of characters based on a text string... and everything is allocated to time... eg:

    4.0534 move from xyz, to xyz and play run animation
    8.2345 move from xyz to xyz and play run animation

    Obviously I need the character to be at the xyz position in time for the next action.... so I am going for the time approach to the transition.

    In simplified form it looks like this



    Code (csharp):
    1.  
    2. timeMarker[i] = 4.0534f   //The time the transition starts
    3. startMarker[i] = getVector3(0,0,0)  //The starting vector
    4. endMarker[i] = getVector3(25,0,14)   //The finish vector
    5.  
    6. journeyLength[i] = Vector3.Distance (startMarker[i],endMarker[i]);
    7.  
    8. void Update() {
    9.  
    10. distCovered[i] =(Time.time - timeMarker[i]) * 8.0f;
    11. fracJourney[i] = (distcovered[i] / journeyLength[i]);
    12. players[i].transform.position = Vector3.Lerp(startMarker[i], endMarker[i], fracJourney[i]);
    13.  
    14. players[i].GetComponent<Animator>().SetFloat("Speed", 2.0f);
    15. players[i].GetComponent<Animator>().Play(locomotionState);
    16. }
    17.  

    Now this actually runs well... plays the running animation and gets to the right point in the time I would expect. Unfortunately it is not very realistic.... a player runs and then comes to a complete idle stop. I thought I would turn on "rootmotion" within the state... and that makes it more realistic with the player coming to a gradual stop.

    The problem is that it no longer goes to the correct point. Because it uses Time.time to work out how long it will take to get there... as soon as Mecanim starts to slow down the character.... it moves slower (which I want) but of course means the time taken to get there is not the same as it was if the speed was constant without root motion. Not sure how to fix this yet.

    On top of this... if I use root motion... and the player moves to a 2nd vector... it does s by running in a nice curve. That is all well and good and looks pretty.... but of course the animation stops short of it's intended target because the original journeyLength that I had calculated (which is basically a point to point straight line) is no long correct... as Mechanims root motion in turning... means the character moves with a slight curve towards the target.... which of course means the journeyLength is longer.

    Not sure yet how to overcome this.
     
    Last edited: May 16, 2014
  10. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Look ahead, say, 0.3 seconds. If the text file says that a character stops moving at 00:33, then crossfade to idle at 00:30.

    Or set up a blend tree based on Speed, where 0 is the idle clip and 1 is the run clip. At 00:30, start damping Speed down to 0 over 0.3 seconds.
     
  11. frankmat

    frankmat

    Joined:
    Sep 14, 2013
    Posts:
    42
    This was what I originally did which was causing the issues. Because the speed damps down...using root motion it meant the character slowed down as well (which I wanted) so he stopped short of his intended target (Which I didn't want.. ass Target was based on distCovered which calculate how far into the journey you were).

    I need to make it somehow based on distance remaining rather than distance covered.
     
  12. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697

    Since you need to stop at an exact point, it sounds like manually controlling position might work better than root motion. If you base the distance on the current speed, then it should sync up pretty well with the footsteps in the animation.