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

[Solved] Overriding StateMachineBehaviours?

Discussion in 'Animation' started by Deleted User, Apr 15, 2017.

  1. Deleted User

    Deleted User

    Guest

    With an AnimatorOverrideController, animations can be overriden. Is it possible to do the same for StateMachineBehaviours?

    Basically, I have a "PlaySound" StateMachineBehaviour and would like to play a different sound for different AnimatorOverrideControllers.
     
  2. Deleted User

    Deleted User

    Guest

  3. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    As far as I'm aware there isn't a way to override a StateMachineBehaviour like you do the AnimationClips using an AnimatorOverrideController. However, you can have all your sound management done from a different component and then call that component from your StateMachineBehavior. Here is something that might get you close to what you are looking for:

    SoundCollection Component for managing the sounds that this object needs to play via the PlayStateSound StateMachineBehavior
    Code (csharp):
    1. using UnityEngine;
    2. /// <summary>
    3. /// This component manages the sounds that
    4. /// will be played based on other gameplay
    5. /// events
    6. /// </summary>
    7. [RequireComponent(typeof(AudioSource))]
    8. public class SoundCollection : MonoBehaviour
    9. {
    10.     /// <summary>
    11.     /// The types of sounds that
    12.     /// are defined in this component
    13.     /// </summary>
    14.     public enum SoundType
    15.     {
    16.         Attack,
    17.         Block,
    18.         Injured,
    19.         Death
    20.     }
    21.     /// <summary>
    22.     /// This method plays a one shot
    23.     /// audio based on the type provided
    24.     /// </summary>
    25.     /// <param name="type">The type of sound to be played</param>
    26.     public void PlaySoundType(SoundType type)
    27.     {
    28.         switch (type)
    29.         {
    30.             case SoundType.Attack:
    31.                 audioSource.PlayOneShot(AttackSound);
    32.                 break;
    33.             case SoundType.Block:
    34.                 audioSource.PlayOneShot(BlockSound);
    35.                 break;
    36.             case SoundType.Injured:
    37.                 audioSource.PlayOneShot(InjuredSound);
    38.                 break;
    39.             case SoundType.Death:
    40.                 audioSource.PlayOneShot(DeathSound);
    41.                 break;
    42.             default:
    43.                 break;
    44.         }
    45.     }
    46.     // Each of the audio clip fields
    47.     // corresponds to a type in the
    48.     // SoundType enum.
    49.     [SerializeField]
    50.     private AudioClip AttackSound;
    51.     [SerializeField]
    52.     private AudioClip BlockSound;
    53.     [SerializeField]
    54.     private AudioClip InjuredSound;
    55.     [SerializeField]
    56.     private AudioClip DeathSound;
    57.     private AudioSource audioSource;
    58.     private void Start()
    59.     {
    60.         audioSource = GetComponent<AudioSource>();
    61.     }
    62. }
    PlayStateSound StateMachineBehaviour that can be configured to play an appropriate sound. The SoundCollection provides all the sounds and can be configured on a per object basis.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. public class PlayStateSound : StateMachineBehaviour
    4. {
    5.     // What soundType should be played on enter
    6.     [SerializeField]
    7.     private SoundCollection.SoundType soundType;
    8.     // Cached reference to the soundCollection
    9.     // that is on this gameObject
    10.     private SoundCollection soundCollection;
    11.     // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    12.     override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    13.     {
    14.         if (soundCollection == null)
    15.         {
    16.             soundCollection = animator.GetComponent<SoundCollection>();
    17.             if (soundCollection == null)
    18.             {
    19.                 // Log an error is there is no sound collection
    20.                 // available to reference
    21.                 Debug.LogError("No sound collection was found on the object '" + animator.gameObject.name + "'");
    22.                 return;
    23.             }
    24.         }
    25.         soundCollection.PlaySoundType(soundType);
    26.     }
    27. }
    28. code]
    29.  
    30. I wasn't sure what your use case was so I just used somewhat standard things like attack, block, injured, and death for the example. I hope that helps you out a bit.
    31.  
    32. Cheers,
    33. TrickyHandz
     
    Deleted User likes this.
  4. Deleted User

    Deleted User

    Guest

    Thanks that's a brilliant idea! I'm not a big fan of the enum but it does point me in the right direction. I think what I'll end up doing is having a list of States & Sound. I'll have an editor that gets all the PlaySound behaviours and what states they're in similar to the AnimatorOverrideController.
     
    TrickyHandz likes this.
  5. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    Glad it helped in some way. There's a lot better ways to do this than the enum for making it scalable. You're solution sounds great! I think I might just have to follow your lead on this one as I have need for it in an upcoming project.

    Cheers,
    TrickyHandz
     
    Deleted User likes this.
  6. Deleted User

    Deleted User

    Guest

    If I get around to finishing the code first I'll make sure to post it here! ;)
     
  7. Arycama

    Arycama

    Joined:
    May 25, 2014
    Posts:
    182
    The easiest way would be to have an AudioSource on the same GameObject as the Animator which contains the state machine, then getting the referenece to that component through the animator, and telling it to play. If you need to select from more than one audio clip, you could store them in a seperate MonoBehaviour component and get the reference to that first, and then pass the desired audioClip to the audio source.
    Code (CSharp):
    1. public class AudioPlayBehaviour : StateMachineBehaviour
    2. {
    3.     public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    4.     {
    5.         animator.GetComponent<AudioSource>().Play();
    6.  
    7.         // Or if you want to get the audio from another object
    8.         var audioContainer = animator.GetComponent<AudioContainer>();
    9.         var audioClip = audioContainer.soundClips[3];
    10.         animator.GetComponent<AudioSource>().PlayOneShot(audioClip);
    11.     }
    12. }