Search Unity

Adding Sound to Pick Up

Discussion in 'Scripting' started by Zoneo, May 20, 2015.

  1. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    Hey guys,

    the titles says it all, im trying to add an audio clip whenever my character picks up some coins. ive added the audio component to the coins and tried added the code to the character controller. but im having a hard time getting it to work properly...

    Code (CSharp):
    1.  
    2. public class Playercontroller : MonoBehaviour {
    3.  
    4.     public float speed;
    5.     public Text CoinCounter;
    6.     public AudioClip Pickup;
    7.  
    8. void OnTriggerEnter (Collider other)
    9.     {
    10.         if (other.gameObject.CompareTag ("Coin"))
    11.             other.gameObject.SetActive (false);
    12.             count = count + 1;
    13.             SetCoinCounter ();
    14.         AudioSource.PlayClipAtPoint (Coin, transform.position);
    15.  
    16.            
    17.     }
    18.  
    I came to use this code by searching the unity forums for similar situations as mine, and this one seems the closest even though im pretty sure the original post was in JS and im using C#.

    lemme know what you guys think!!

    Thanks!

    P.S. Whats the command to search unity for a command? i remember high lighting the word. pressing a button and clicking the word and it brings me to the related commands, but i cant remember which button commands it was....
     
  2. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    You are playing an AudioClip called Coin but you didn't show where it was declared. Did you mean to play the AudioClip Pickup?

    P.S. Try: ctrl + '
     
  3. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    Switched in to "Pickup" and got this error message when i pick up the coins. everything seems to be functioning other than the sound, in my event system in my hierarchy it says "one shot audio" with every coin i pick up, but doesnt play the sound


    UnassignedReferenceException: The variable Pickup of Playercontroller has not been assigned.
    You probably need to assign the Pickup variable of the Playercontroller script in the inspector.
    UnityEngine.AudioSource.PlayClipAtPoint (UnityEngine.AudioClip clip, Vector3 position, Single volume) (at C:/buildslave/unity/build/artifacts/generated/common/modules/AudioBindings.gen.cs:683)
    UnityEngine.AudioSource.PlayClipAtPoint (UnityEngine.AudioClip clip, Vector3 position) (at C:/buildslave/unity/build/artifacts/generated/common/modules/AudioBindings.gen.cs:672)
    Playercontroller.OnTriggerEnter (UnityEngine.Collider other) (at Assets/Scripts/Playercontroller.cs:47)


    P.S. thats it!


    EDIT: I really thought I figured it out. I added the sound to the playercontroller in the inspector just like the error message called for, but it still doesnt play the sound. As I got to think about it, I had the realization that my code made the variable of the sound public (which is why its in the inspector) and so every time it comes into contact with a pick up item, it'll play the sound... well thats not what I want. What I want is for the character to call on the OTHER object to play the sound, not rely on its own scripting for it. So basically, I need the character to be able to call on the other object so that it can play different sounds when it comes into contact with different objects in the game.
     
    Last edited: May 20, 2015
  4. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    I guess, you want to access the coin, so every coin needs an audio source where the sound is in. I would prefer a global manager for this coin sound, but if you want to be it 3D and depending on the position of the coin, add a audiosource to the coin prefab and use something like this:

    Code (CSharp):
    1. void OnTriggerEnter (Collider other)
    2.     {
    3.         if (other.gameObject.CompareTag ("Coin"))
    4.             AudioSource otherAudio = other.gameObject.GetComponent<AudioSource>();
    5.             otherAudio.PlayOneShot ("YourSound");
    6.  
    7.             other.gameObject.SetActive (false);
    8.             count = count + 1;
    9.             SetCoinCounter ();
    10.         AudioSource.PlayClipAtPoint (Coin, transform.position);
    11.          
    12.     }
     
  5. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    ewww... I think it would be far more modular to have the coin handle it's side of things, and the player handle it's side of things rather than trying to get the player object do the coins job for it...

    Code (csharp):
    1.  
    2. // add to coin or anything you want to pickup
    3. public void class Collectable : Monobehaviour
    4. {
    5.     public AudioClip pickupSound;
    6.     public AudioSource audio;
    7.    
    8.     public Start()
    9.     {
    10.         audio = GetComponent<AudioSource>();
    11.     }
    12.    
    13.     public void Collected()
    14.     {
    15.         audio.PlayClipAtPoint(pickupSound, transform.position);
    16.         // do animation, whatever
    17.     }
    18. }
    19.  
    Code (csharp):
    1.  
    2.     void OnTriggerEnter(Collider other)
    3.     {
    4.         Collectable oc = other.transform.GetComponent<Collectable>();
    5.         if(oc)
    6.         {
    7.             oc.Collected();
    8.             // do what the player needs to do when they pick something up...
    9.         }
    10.     }
    11.  
     
    NomadKing and WheresMommy like this.
  6. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85


    Both of those methods make sense to me, but could you walk me through yours? im having a hard time understanding...
     
  7. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    as LeftyRighty said, you need to add the first script and the audiosource to your coin and the second script to your player. that's it. Your player handles, if he collected a collectable, and if he did he calls the "Collected" function on the coin. :)
     
  8. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    do i need both audioclip pickup and audiosource audio? it seems to just give me two fields to put the soundclip in on the coin
     
  9. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    I'm not sure, what you are asking ;) You need an audiosource component on your coin and you need to put in the clip in the inspector on the audioclip. that should do it :)
     
  10. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    audiosource is the "thing" that plays the sound (the cd player), audioclip is the sound to be played (the track on the cd). The Collectable Start() function will handle the audiosource so long as there is an audiosource component on the same object, you just need to populate the audioclip "slot" in the inspector. (I tend to make everything public so you can see if there are any nulls, you could make the audiosource variable private and the code would still work, and the slot would disappear from the inspector).

    As for understanding what's going on; the player runs into another gameobject with a trigger collider and a "Collectable" component on it. In that trigger collision it picks up a reference to the collectable class, and tells it "you've been collected" via the "Collected()" function. The player object doesn't need to know anything about the coin, doesn't care if it makes a sound or not, it just carries on doing whatever the player class does.

    When the "Collectable" component attached to the coin gets told "you've been collected" (i.e. Collected() is called) it uses the attached Audiosource to play the audioclip you've assigned it.
     
  11. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    i punched it all into my code with "Public void Collectable" and im calling on "Collectable oc" but it says Collectable does not exist... part of my confusion is im not sure if collectable is the actual code, or just you substitute for that i should be putting in there lol what exactly does "Collectable oc" mean?

    this is my playercontroller script
    Code (CSharp):
    1.  
    2. void OnTriggerEnter (Collider other)
    3.     {
    4.         Collected oc = other.transform.GetComponent<pickup>();
    5.  
    6.         if (oc)
    7.         {
    8.             oc.Collected ();
    9.             ;
    10.             if (other.gameObject.CompareTag ("Coin"))
    11.  
    12.                 other.gameObject.SetActive (false);
    13.             count = count + 1;
    14.  
    15.             SetCoinCounter ();
    16.         }
    17.     }
    18.  
    This is my Coins script
    Code (CSharp):
    1.  
    2. public void Collected ()
    3.     {
    4.         AudioSource.PlayClipAtPoint (pickup, transform.position);
    5.  
    6.     }
    7.  
     
    Last edited: May 21, 2015
  12. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    Collectable above is the entire code to create the "Collectable" component, it's its own script doesn't get put "into" any other scripts. Once you have a c# script called "Collectable" with that code in it saved you should be able to reference it as a type in you're player class script (or anywhere else). If you want it to do more you just add the code in where line 16 has the comment.

    Collectable oc ... a variable called oc ("o"ther "c"ollectable) of type Collectable.
     
  13. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    Right now my coins have a rotator script on them that makes them spin, so if i replace collectable with rotator it should work?

    IT WORKED! thanks so much for the help!
     
  14. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    gameobjects can have as many components attached to them as needed. You can just add collectable as a new component and keep rotator. So long as the two scripts are attempting to manipulate the transform's properties at the same time (i.e. one script turns the gameobject one way and the other script turns it counterwise). Breaking behaviour down into their own little scripts makes the code more reusable.
     
  15. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    @LeftyRighty Do you think you could help me make my coins reappear after a set time now? ive been searching the site trying to find a way but i cant seem to find it. I want them to reappear after a Set amount of time of being set to false, but im not sure which keywords to use in my loop.
     
  16. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    I think, you need to use SetActive(false); to disable the object, but you need some kind of manager on another active gameobject to SetActive(true); the coin again, as, as far as I know, the script won't execute anymore if your coin is disabled.
     
  17. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    i can make a coin controller or something, but what code should i use?
     
  18. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    there are a few ways of doing this... really depends on what you want to do with the coins. If you want the UI to show something about "coins collected / total coins" you'll probably want to go down the route of a manager script to centralise all that information in one place to make the UI side easier.

    If you really don't care about the coins beyond them "doing their own thing" I'd probably go down the route of having an empty GameObject as a parent of the coin which can then use SetActive(false) and the parent can use Invoke() to set the child as true again after a while.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class TriggerChildDisable : MonoBehaviour
    6. {
    7.     public Transform child; // drag the coin child into this slot
    8.     public float reactivationDelay;
    9.  
    10.     public void OnTriggerEnter(Collider other)
    11.     {
    12.         Debug.Log(transform.name + " trigger collision with " + other.transform.name);
    13.  
    14.         child.gameObject.SetActive(false);
    15.         Invoke("ReactivateChild", reactivationDelay);
    16.     }
    17.  
    18.     public void ReactivateChild()
    19.     {
    20.         child.gameObject.SetActive(true);
    21.     }
    22. }
    23.  
    parent (an empty gameobject) needs a rigidbody (without gravity :p), the child (which is your existing coin) needs to have the trigger collider (if it currently has a rigidbody remove it).
     
  19. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    @LeftyRighty right now my HUD does say the total number of coins collected in the bottom left corner, im using it as just a tracker for how many coins you've collected in general (eventually i want this game to turn into like an RPG type thing where you can buy items, but thats a long was away) so with that in mind, would i want to go with the less UI intensive option?

    I added the code you provided but its saying:
    Assets/Scripts/Coincontroller.cs(9,14): error CS1519: Unexpected symbol `public' in class, struct, or interface member declaration

    Code (CSharp):
    1.  
    2.  
    3.     public Transform child;
    4.     public float reactivationDelay
    5.  
    6.     public void OnTriggerEnter(Collider other)
    7.     {
    8.         Debug.Log (Transform.name + "Trigger collision with " + other.transform.name);
    9.  
    10.         child.GameObject.setactive (false);
    11.         Invoke ("ReavticvateChild", reactivationDelay);
    12.     }
    13.  
    14.     public void ReactivateChild ()
    15.     {
    16.         child.gameObject.SetActive (true);
    17.     }
     
    Last edited: May 22, 2015
  20. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    you're missing a ";" on line 4...
     
  21. Zoneo

    Zoneo

    Joined:
    May 1, 2015
    Posts:
    85
    every F***ing time. i thought i went line for line trying to find it :( now its saying
    Assets/Scripts/Coincontroller.cs(11,38): error CS0120: An object reference is required to access non-static member `UnityEngine.Object.name'

    from looking online i guess i have to change the way i word something... This is probably 90% of the struggle have.. i know exactly what it is i WANT it to do, and i roughly now HOW to do it, i just dont know what WORDS to use...
     
    Last edited: May 23, 2015
  22. Bradamante

    Bradamante

    Joined:
    Sep 27, 2012
    Posts:
    300
    You can use InvokeRepeating to spawn GameObjects (like a coin) repeatedly. You store the object that is returned by Instantiate in a List. You then set a max count and when the List contains too many items you don't spawn a object (just so that the level doesn't get spammed):

    Code (csharp):
    1.  
    2. public class CoinController : MonoBehavior {
    3.  
    4.   // set in Inspector
    5.  
    6.   public float delay;
    7.   public float rate;
    8.  
    9.   public float minRadius;
    10.   public float maxRadius;
    11.  
    12.   public int maxCoins;
    13.   public GameObject coinPrefab;
    14.   public List<GameObject> coinsInScene;
    15.  
    16.   void Start () {
    17.  
    18.   InvokeRepeating ( "spawnCoin", delay, rate )
    19.   }
    20.  
    21.   public void spawnCoin () {
    22.  
    23.   if ( coinsInScene.Count > maxCoins ) return;
    24.  
    25.   GameObject newCoin = Instantiate ( coinPrefab, getRandomPosInRadius(), Random.rotation );
    26.  
    27.   // adding the new Coin to the List
    28.   coinsInScene.Add ( newCoin );
    29.  
    30.   // if a Coin gets picked up, destroy it in the List
    31.   // tracking Coins and destroying them
    32.   // is beyond this code, however
    33.   }
    34.  
    35.   public Vector3 getRandomPosInRadius ( ) {
    36.  
    37.      float rRadius = Random.Range ( minRadius, maxRadius );
    38.  
    39.    return Random.insideUnitCircle * rRadius;
    40.   }
    41. }
    42.  
    Obviously, this is not a long-term solution. Long-term you want to use some kind of event system and an object pooling solution a la PoolManager.

    One thing in your code:

    Code (csharp):
    1.  
    2. child.GameObject.setactive (false);
    3.  
    should propably be

    Code (csharp):
    1.  
    2. child.gameObject.SetActive( false );
    3.  
    Btw, your posts are harder to understand without punctuation.

    One last thing ... You are asking some very basic questions here. Study the Unity Learn section and read code from Unity example projects that are available for free on the Unity Asset store. Starting with Unity you will have to read, read, read.
     
    Last edited: May 25, 2015