问题描述
我和我的朋友正在尝试为手机做一个钢琴应用程序。我们的钢琴键盘“键”是 16 个彼此相邻的 3D 对象。问题是,当我几乎按屏幕上的任何地方时,它会同时播放我放在 3D 对象上的所有音频剪辑。我尝试了多个脚本,但它们都有相同或不同的问题。 这是我当前使用的脚本:
附注。我仍然是 Unity 和 C# 的菜鸟,所以欢迎提供所有帮助。 :)
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.Audio;
[RequireComponent(typeof(AudioSource))]
public class key1 : MonoBehavIoUr
{
AudioSource AudioSource;
Touch thetouch;
void Start()
{
AudioSource = GetComponent<AudioSource>();
}
void Update()
{
if (Input.touchCount > 0)
{
thetouch = Input.GetTouch(0);
if (thetouch.phase == TouchPhase.Began)
{
AudioSource.Play();
}
if (thetouch.phase == TouchPhase.Ended)
{
AudioSource.Stop();
}
if (thetouch.phase == TouchPhase.Moved)
{
AudioSource.Stop();
}
if (thetouch.phase == TouchPhase.Canceled)
{
AudioSource.Stop();
}
}
}
}
解决方法
首先,您可能不需要在每个“键”上都有一个 AudioSource。如果您只想播放声音,一个就足够了。但是,如果您想要左侧按键位于左扬声器上的 3D 声音,那么您需要在每个按键上都有一个 AudioSource。
要知道要播放哪个键,您可以从屏幕位置(您单击的位置)到 3D 世界进行简单的光线投射。你只需要做一次光线投射,所以不要在你的关键脚本中做。如果您对 3D 音效感到满意,请将其与 AudioSource 一起移至单独的脚本。
using UnityEngine;
using UnityEngine.Assertions;
public class Piano : MonoBehaviour
{
AudioSource audioSource;
private void Awake()
{
audioSource = GetComponent<AudioSource>();
Assert.IsNotNull(audioSource);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// The user has clicked (or touched) on the screen. Convert it to a position in the 3D world:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray,out hit))
{
// The user has clicked on a Collider. Does it have a Key script on it?
Key key = hit.collider.GetComponent<Key>();
if(key != null)
{
// The user clicked on a key. Play the sound (without interrupting previous sounds).
audioSource.PlayOneShot(key.clip);
}
}
}
}
}
按键上的脚本可以很简单:
using UnityEngine;
public class Key : MonoBehaviour
{
public AudioClip clip;
}
如果您想要 3D 声音,请将 AudioSource 移动到键(每个键一个 AudioSource)。钢琴代码将类似于:
using UnityEngine;
public class Piano : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// The user has clicked (or touched) on the screen. Convert it to a position in the 3D world:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray,out hit))
{
// The user has clicked on a Collider. Does it have a Key script on it?
Key key = hit.collider.GetComponent<Key>();
if(key != null)
{
key.Play();
}
}
}
}
}
和关键脚本:
using UnityEngine;
using UnityEngine.Assertions;
public class Key : MonoBehaviour
{
// You could also simply add the sound to the audioSource from the editor.
public AudioClip clip;
AudioSource audioSource;
private void Awake()
{
audioSource = GetComponent<AudioSource>();
Assert.IsNotNull(audioSource);
}
public void Play()
{
audioSource.PlayOneShot(clip);
}
}
最后是带有 3D 音效的触控版本。它与之前的示例非常相似,但我们现在还可以跟踪按键按下的时间。
和之前一样,我们使用光线投射来获取用户按下的键。但是我们也存储了fingerId,所以我们知道它是哪个手指,并且可以在松开那个手指时松开右键。
在此示例中,声音剪辑与 AudioSource 一起存储。
using UnityEngine;
public class Piano : MonoBehaviour
{
Key[] allKnownKeys;
private void Awake()
{
allKnownKeys = FindObjectsOfType<Key>();
}
void Update()
{
foreach (Touch touch in Input.touches)
{
if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
{
// Get the key using the fingerID (the finger that pressed down the key)
Key key = GetKey(touch.fingerId);
// Was it a key that was released?
if(key != null)
key.Stop();
}
else if(touch.phase == TouchPhase.Began)
{
// New touch on the screen. Convert it to a position in the 3D world:
Ray ray = Camera.main.ScreenPointToRay(touch.position);
RaycastHit hit;
if (Physics.Raycast(ray,out hit))
{
// The user has touched a Collider. Does it have a Key script on it?
Key key = hit.collider.GetComponent<Key>();
if (key != null)
{
key.Play(touch.fingerId);
}
}
}
}
}
/// <summary>
/// Gets the key which is already "down" (playing).
/// </summary>
/// <param name="fingerId"></param>
/// <returns></returns>
private Key GetKey(int fingerId)
{
foreach (Key key in allKnownKeys)
if (key.FingerId == fingerId)
return key;
return null;
}
}
以及触摸版的关键脚本:
using UnityEngine;
using UnityEngine.Assertions;
public class Key : MonoBehaviour
{
AudioSource audioSource;
private void Awake()
{
audioSource = GetComponent<AudioSource>();
Assert.IsNotNull(audioSource);
FingerId = -1;
}
public void Play(int fingerId)
{
if (FingerId >= 0)
return; // The key is already "down"
FingerId = fingerId;
audioSource.Play();
}
public void Stop()
{
FingerId = -1;
audioSource.Stop();
}
public int FingerId { get; protected set; }
}