I’m trying to move from OnMouseDown to Input.Touch to allow for multiple touches. So I have a series of prefabs that have an audio clip attached via the editor and this script is attached to each prefab. Multiple prefabs can be spawned at the same time. If I touch one of the prefabs the audio clip is played for every prefab currently instantiated.
using UnityEngine;
public class Clickable : MonoBehaviour
{
public AudioClip animalSound;
public AudioSource playerAudio;
public bool oneShotPlaying;
void Start()
{
playerAudio = GameObject.Find("Audio Source").GetComponent<AudioSource>();
}
void Update()
{
Action();
}
public void Action()
{
if (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.touches[0].position);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.collider != null && !oneShotPlaying)
{
oneShotPlaying = true;
playerAudio.PlayOneShot(animalSound, 1.0f);
Debug.Log("Click");
Invoke("SetBoolBack", 2.5f);
}
}
}
}
public void SetBoolBack()
{
oneShotPlaying = false;
}
}
This same logic works fine with OnMouseDown but I get the audio clip for all prefabs when using this code for touch.
In general, DO NOT use Find-like or GetComponent/AddComponent-like methods unless there truly is no other way, eg, dynamic runtime discovery of arbitrary objects. These mechanisms are for extremely-advanced use ONLY.
If something is built into your scene or prefab, make a script and drag the reference(s) in. That will let you experience the highest rate of The Unity Way™ success of accessing things in your game.
“Stop playing ‘Where’s GameWaldo’ and drag it in already!”
Ok, so I killed the GameObject.Find line and was able to assign a unique Audio Source to each prefab via the Inspector. This did not resolve the issue of all instantiated prefabs sounds playing when one is touched. It seems like the code should know I only want to interact with the object I’m touching so only play the audio assigned to that object? It doesn’t make sense.
There doesn’t seem to be an accessible method for something like:
hit.collider.gameObject.playerAudio.PlayOneShot(animalSound);
or
hit.collider.gameObject.GameObject.playerAudio.PlayOneShot(animalSound);
something like this will run but it plays all clips:
hit.collider.gameObject.GetComponent().PlayOneShot(animalSound, 1.0f);
How is it playing clips for all of them if I specifically reference the gameObject from hit?
All of the instances of that script is doing “if a raycast hits anything, play a sound”. So then every instance of that script will play a sound if you mouse over anything.
You can have a script like that, but then you only want one of it.
This is what I wound up using for code, the main difference is I’m using an Audio Source created on the prefab and assigned the clip there. I get to use the isplaying now instead of my hokey bool method and I can control the volume etc. on each clip via the editor so, swings and roundabouts.
using UnityEngine;
public class Clickable : MonoBehaviour
{
public AudioSource playerAudio;
void Update()
{
Action();
}
public void Action()
{
if (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.touches[0].position);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.collider != null && !hit.collider.gameObject.GetComponent<AudioSource>().isPlaying)
{
hit.collider.gameObject.GetComponent<AudioSource>().Play();
Debug.Log("Click");
}
}
}
}
}
… you already have an AudioSource… stop doing unnecessary GetComponents… they’re not helping!
The likely code within the Raycast block will be something like:
if (Physics.Raycast(ray, out hit))
{
// we don't need to test hit.collider if we're in this block: we DID hit something.
// this is only checking to make sure it is us
if (hit.collider.gameObject == gameObject)
{
playerAudio.Play();
}
}
That’s it. You can also check if it is playing if you like, but seriously, stop with the GetComponents before you hurt someone.