Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Change a sprite during a mecanim animation: need some help with my method of doing it

Discussion in '2D' started by esco1979, May 9, 2014.

  1. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    136
    Hey everyone. I've looked around ALL OVER for an answer to this but all I could find was a $15 store item that claims it could do this, and I KNOW I don't need that to do it right (especially for that obscene price). I did find a way to do this, however I am curious if there is a more effecient way to accomplish what I am doing.

    Here is the scenario: these all use 2d sprites BTW. I have a parent object Alucard with a child object called sword. Now this sword is set up so when used by playing an ATTACK animation on Alucard, the sword is moved as needed, and the sprite for it is changed as needed along with the collision box via mecanim. And this was easy to do. But I wanted to set it up so that based on what weapon was equipped, the sprite used for each frame of the sword's attack would vary (ex. if short sword is equipped we use short_sword1 for frame one, if the shotel is equipped it uses shotel1 for frame1 in the animation instead).

    However when I then tried to setup a script where based on what point in the animation was running (by checking animator.GetCurrentAnimatorStateInfo(0).normalizedTime) it was always off some. For example if I set it to change the sprite at 2/25 in the mecanim timeline (since it is 25 steps long and the sprite should be changed when it hits step #2), it would do so later or earlier than it should. When I used Debug.log I noticed that no matter which update I placed the debug into (normal, fixed or late) the number still varied constantly each time, and too much to be used accurately (example: once when it hit step 2 it equalled .097, next time .0934, etc.). So I scrapped that idea.

    I then tried setting up a var that incremented by one if the attack animation was being played. I did this in fixedupdate so it was done only once per frame. This had a similar issue: the value could vary by one or two, and thus my animaton would then be off. It didn't matter whether I did this via fixed update in a script constantly running or if I did it via mecanim by just adding an event to the timeline that ran each step. So again scrapped the idea.

    The only way I got it to work was to make sure that on each step of the mecanim timline where I wanted to change the sprite, there was a DIFFERENT sprite. Thus via a script I could then do this:

    Code (csharp):
    1.  
    2. switch (sprite_renderer.sprite.name)
    3. {
    4.     case "short_sword_1": {frame_num = 0; break;}
    5.     case "frame1_dup1": {frame_num = 1; break;}
    6. etc.
    7. }
    8.  
    This required me literally making duplicates of several sprites on the sprite sheet (a total of 7). This isn't a big deal space or resources wise since they are tiny. But I was wondering, is there another way to do this using one of the aofrementioned methods? I AM NEW TO UNITY, and this seems very innefficient, so can someone please advise me if there is another way to do it? I would really appreciate it. :cool:

    NOTE: the one limit I noticed is that in order to change the sprite, since mecanim already assigns one, I have to put my sprite_renderer.sprite = etc. statement under late_update so it works.
     
    Last edited: May 9, 2014
  2. TomasJ

    TomasJ

    Joined:
    Sep 26, 2010
    Posts:
    256
    If I understood you correctly, then the solution is to use animation events.
    Open up the animation window and add an event to a particular frame. Then setup a function to call and decide which sprite to set there.
     
  3. Loius

    Loius

    Joined:
    Aug 16, 2012
    Posts:
    546
    I think the "intended" way to implement something like this is to have multiple animations. (if I understand right)

    So you have your Alucard object with its animations, and your Weapon object that moves around to be where it's supposed to. Whenever you attack, you'd set (for instance) "isAttacking=true" on the Weapon's animator. Each time you equip a different weapon, you'd set "usingWeapon=#" on the Weapon animator, where each different weapon has a unique number. Then the Weapon's animator would have a state it gets to while isAttacking==true, where it uses a blend tree to determine which animation to play.
     
  4. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    8,952
    You can also use layers in the animator.
     
  5. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    136
    TomasJ: that is a great idea and should work perfect! I will use that. Thanks so much!

    As for the other methods I still have questions as I am new to Unity and wish to learn:

    Loius: I actually was going to try multiple animations, but the problem with this would be that since the weapon is a CHILD of Alucard, the only way I found to do this would be to make the animations for alucard. He has FOUR for just attacking with swords; that means I would have to do four per sword. There are 12 swords, so that would be 48 animations. So I figured that wasn't a good idea.

    I also tried making the sword a seperate object; but then that makes positioning it a real problem, not to mention that the sword animation doesn't always perfectly sync up with alucard. Any suggestions/examples here would be appreciated IF there is a way to do it as it is possible I was doing it wrong. But I don't see how.

    Zombiegorilla: I have no clue how to properly use layers, even after reading the documentation since its mostly for 3d stuff. But I REALLY want to learn this. Could you explain it to me please?
     
    Last edited: May 10, 2014
  6. Loius

    Loius

    Joined:
    Aug 16, 2012
    Posts:
    546
    yeah, i actually built my plugin specifically for this case because i couldn't figure out how to do it 'efficiently' in mecanim :)

    i think you could get away with something like:

    every late update,breplace sprite with currentWeaponName + time index as string
    could also replace alu's sprite every frame with the proper weapon version

    time index could probably be derived from normalized time.

    havent tried this myself, but its what i'd do.

    -sent from phone, excuse dumb letters
     
  7. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    136
    No idea what you mean by getting the time index from mormalized time? How could this be done different from what I already did above (which SHOULD work)? Please elaborate.
     
  8. Loius

    Loius

    Joined:
    Aug 16, 2012
    Posts:
    546
    I'm having a really hard time visualizing your setup.

    First thing is you should never need duplicated sprites, that makes no sense to me.
    Second is you say "Now this sword is set up so when used by playing an ATTACK animation on Alucard, the sword is moved as needed, and the sprite for it is changed as needed along with the collision box via mecanim.", then I say "So you have your Alucard object with its animations, and your Weapon object that moves around to be where it's supposed to", then you say "I also tried making the sword a seperate object; but then that makes positioning it a real problem, not to mention that the sword animation doesn't always perfectly sync up with alucard.". How can you move the sword if it's not a separate object? Are you saying the sprite's pixels change in such a way it looks like the sword is moving?

    If you are controlling the rate of sprite changes for both Alucard and his sword, then it will always sync up. 0.097 and 0.0932 are indistinguishable when we're dealing with 25 frames; there should be no issue. You'll always be limited by how often Update updates, there's no way to land on 0.96 if that is not when a display frame happens. If you just copy Alucard's current frame index onto his weapon it should work fine.

    Code (csharp):
    1. weapon.sprite = GetNamedSprite(alucard.sprite.name.Replace("Alucard_","Sword_"))
     
  9. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    136
    FYI: TomasJ's way doesn't work UNLESS you setup the script to run via LATEUPDATE, and use a counter to keep track of which one should be run. I tried just making a seperate script to run for each step of the mcanim animation (when needed) and it did NOTHING!
     
  10. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    8,952
    Sure thing. I just posted a tutorial here:
    http://forum.unity3d.com/threads/245564-2D-Puppet-Rigging-Tips-amp-Tricks

    This also covers weapon switching. As you don't want to try to fight the animator by trying to override it.