Search Unity

How can I play a sound on variable change?

Discussion in 'Scripting' started by guyanermanator, May 16, 2019.

  1. guyanermanator

    guyanermanator

    Joined:
    Mar 17, 2018
    Posts:
    13
    So what I am trying to do is play a sound when the health of an object has changed. I would like to be able to drag in what sound as I think different things being hit will respond in different ways ya know? Thank you for your help. Here is my code.



    Code (CSharp):
    1. /// <summary>
    2. /// Health.cs
    3. /// Author: MutantGopher
    4. /// This is a sample health script.  If you use a different script for health,
    5. /// make sure that it is called "Health".  If it is not, you may need to edit code
    6. /// referencing the Health component from other scripts
    7. /// </summary>
    8.  
    9. using UnityEngine;
    10. using System.Collections;
    11.  
    12. public class Health : MonoBehaviour
    13. {
    14.     public bool canDie = true;                    // Whether or not this health can die
    15.    
    16.     public float startingHealth = 100.0f;        // The amount of health to start with
    17.     public float maxHealth = 100.0f;            // The maximum amount of health
    18.     private float currentHealth;                // The current ammount of health
    19.  
    20.     public bool replaceWhenDead = false;        // Whether or not a dead replacement should be instantiated.  (Useful for breaking/shattering/exploding effects)
    21.     public GameObject deadReplacement;            // The prefab to instantiate when this GameObject dies
    22.     public bool makeExplosion = false;            // Whether or not an explosion prefab should be instantiated
    23.     public GameObject explosion;                // The explosion prefab to be instantiated
    24.  
    25.     public bool isPlayer = false;                // Whether or not this health is the player
    26.     public GameObject deathCam;                    // The camera to activate when the player dies
    27.  
    28.     private bool dead = false;                    // Used to make sure the Die() function isn't called twice
    29.  
    30.     // Use this for initialization
    31.     void Start()
    32.     {
    33.         // Initialize the currentHealth variable to the value specified by the user in startingHealth
    34.         currentHealth = startingHealth;
    35.     }
    36.  
    37.     public void ChangeHealth(float amount)
    38.     {
    39.         // Change the health by the amount specified in the amount variable
    40.         currentHealth += amount;
    41.  
    42.         // If the health runs out, then Die.
    43.         if (currentHealth <= 0 && !dead && canDie)
    44.             Die();
    45.  
    46.         // Make sure that the health never exceeds the maximum health
    47.         else if (currentHealth > maxHealth)
    48.             currentHealth = maxHealth;
    49.     }
    50.  
    51.     public void Die()
    52.     {
    53.         // This GameObject is officially dead.  This is used to make sure the Die() function isn't called again
    54.         dead = true;
    55.  
    56.         // Make death effects
    57.         if (replaceWhenDead)
    58.             Instantiate(deadReplacement, transform.position, transform.rotation);
    59.         if (makeExplosion)
    60.             Instantiate(explosion, transform.position, transform.rotation);
    61.  
    62.         if (isPlayer && deathCam != null)
    63.             deathCam.SetActive(true);
    64.  
    65.         // Remove this GameObject from the scene
    66.         Destroy(gameObject);
    67.     }
    68. }
    69.  
     
  2. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Use C# properties to accomplish this.
    Example:
    Code (CSharp):
    1. private int myValue;
    2.  
    3. public int MyValue {
    4.    get => myValue;
    5.    set {
    6.       myValue = value;
    7.       //Perform some other logic when setting myValue here.
    8.    }
    9. }
    If you still want the private field to be visible in the inspector, add the
    SerializeField
    annotation:
    Code (CSharp):
    1. [SerializeField] private int myValue;
     
  3. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Ok, Cucci_A's solution is pretty much the nuclear option: exceedingly powerful, and difficult to contain once deployed :) .

    Since you already have a ChangeHealth() method, you need to add two small changes to your script and object.

    First, add an AudioSource component to your object. This will play the sound when health changes. Add a global vairable theAudioSource to your class.

    Code (CSharp):
    1. public AudioSource theAudioSource;
    2. public AudioClip theSoundClip;
    And drag the sound clip you want to play into the 'theSoundClip' slot in inspector.

    To automatically connect the AudioSource to your script add the following to your Start() method:

    Code (CSharp):
    1. if (heAudioSource == null){
    2.    theAudioSource = this.GetComponent<AudioSource>();
    3. }
    Finally, add the following line to your ChangeHealth() method

    Code (CSharp):
    1. theAudioSource.PlayOneShot(theSoundClip);
    That should play the sound clip every time you invoke ChangeHealth. You may want to check 'amount' and make sure it is negative before you play, or the same sound is played when you are healed, or the amount is zero.
     
    guyanermanator likes this.
  4. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    I'm not sure what you mean by this. Properties are just the C# equivalent of getters/setters in other programming languages.
     
  5. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Because (my belief) setters that contain complex logic are not only a very powerful tool, they have the ability to make the code exceedingly difficult to debug for the simple reason that they interrupt the visual flow of control. You are in essence invoking a method implicitly without any hint to it:

    the line
    Code (CSharp):
    1. i++;
    could trigger complex caclulations or actions if you set up your accessors accordingly. And unless you remember that you have an accessor written for this, this can become confusing.

    Mind you, I'm not saying that this is wrong - your post was entirely correct. I just feel that it's overkill to write an accessor to monitor a variable in a situation where we already have a clearly defined point in the code where that specific variable is changed.
     
  6. It is considered an anti-pattern, because this:
    Code (CSharp):
    1. fooObject.Bar = 5f;
    does not indicate that you're doing anything else than setting the Bar to 5f.

    This is why I prefer methods and method calls instead of properties (except for event dispatch on change and checking the value before setting -> validation). The sound in this example is called side-effect and it's not immediately clear why it is happening and why it should happen. Also it makes impossible to set Bar without sound and sooner or later you will need this (see example above with the ++ operator).
     
  7. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    I suppose that makes sense. I never really considered that.
    Maybe it's just me, but whenever I see properties, I assume there's usually more logic happening.
     
  8. guyanermanator

    guyanermanator

    Joined:
    Mar 17, 2018
    Posts:
    13



    THANK YOU SO MUCH WORKS LIKE A CHARM! Also helped me learn a lot.