Unity3D 在播放器与带有特定标签的 object 碰撞时播放声音
Unity3D playing sound when Player collides with an object with a specific tag
我使用 Unity 2019.2.14f1 创建一个简单的 3D 游戏。
在那个游戏中,我想在我的玩家与具有特定标签的游戏对象发生碰撞时播放声音。
MainCamera 有一个音频监听器,我正在使用 Cinemachine Free Look,它跟随我的化身,在 ThridPersonController 中(我正在使用出现在 Standard Assets 上的那个 - 但我隐藏了 Ethan 并添加了我自己的 character/avatar).
带有我要销毁标签的游戏对象有一个音频源:
为了让声音在碰撞时播放,我首先创建一个空的游戏对象作为 AudioManager,并向其添加一个新组件(C# 脚本):
using UnityEngine.Audio;
using System;
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public Sound[] sounds;
// Start is called before the first frame update
void Awake()
{
foreach (Sound s in sounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.clip = s.clip;
s.source.volume = s.volume;
s.source.pitch = s.pitch;
}
}
// Update is called once per frame
public void Play (string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.source.Play();
}
}
并创建脚本 Sound.cs:
using UnityEngine.Audio;
using UnityEngine;
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)]
public float volume;
[Range(.1f, 3f)]
public float pitch;
[HideInInspector]
public AudioSource source;
}
之后,在 Unity UI 中,我转到 gameObject AudioManager 中的 Inspector,并在我命名为 CatchingPresent 的脚本中添加了一个新元素。
在第三人称角色脚本中,为了在与游戏对象(具有特定标签)发生碰撞时销毁它,我添加了以下内容:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
}
}
它工作正常,因为特定的 object 在碰撞时消失了。现在,为了在播放器与带有标签的 object 发生碰撞时播放声音 "CatchingPresent",在本例中为 Present
,我尝试将以下内容添加到 if
在 OnCollisionEnter
:
FindObjectOfType<AudioManager>().Play("CatchingPresent");
但是我得到错误:
The type or namespace name 'AudioManager' could not be found (are you
missing a using directive or an assembly reference?)
AudioManager.instance.Play("CatchingPresent");
但是我得到错误:
The name 'AudioManager' does not exist in the current context
由于在进入播放模式之前需要修复所有编译器错误,因此欢迎任何有关如何在玩家与带有标签 Present
的游戏对象发生碰撞后播放声音的指导。
编辑 1:假设它有帮助,这里是完整的 ThirdPersonUserControl.cs:
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.CrossPlatformInput;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof (ThirdPersonCharacter))]
public class ThirdPersonUserControl : MonoBehaviour
{
public Text countText;
public Text winText;
private int count;
private ThirdPersonCharacter m_Character; // A reference to the ThirdPersonCharacter on the object
private Transform m_Cam; // A reference to the main camera in the scenes transform
private Vector3 m_CamForward; // The current forward direction of the camera
private Vector3 m_Move;
private bool m_Jump; // the world-relative desired move direction, calculated from the camForward and user input.
private void Start()
{
count = 20;
SetCountText();
winText.text = "";
// get the transform of the main camera
if (Camera.main != null)
{
m_Cam = Camera.main.transform;
}
else
{
Debug.LogWarning(
"Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls.", gameObject);
// we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them!
}
// get the third person character ( this should never be null due to require component )
m_Character = GetComponent<ThirdPersonCharacter>();
}
private void Update()
{
if (!m_Jump)
{
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
}
}
// Fixed update is called in sync with physics
private void FixedUpdate()
{
// read inputs
float h = CrossPlatformInputManager.GetAxis("Horizontal");
float v = CrossPlatformInputManager.GetAxis("Vertical");
bool crouch = Input.GetKey(KeyCode.C);
// calculate move direction to pass to character
if (m_Cam != null)
{
// calculate camera relative direction to move:
m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;
m_Move = v*m_CamForward + h*m_Cam.right;
}
else
{
// we use world-relative directions in the case of no main camera
m_Move = v*Vector3.forward + h*Vector3.right;
}
#if !MOBILE_INPUT
// walk speed multiplier
if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f;
#endif
// pass all parameters to the character control script
m_Character.Move(m_Move, crouch, m_Jump);
m_Jump = false;
}
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
//FindObjectOfType<AudioManager>().Play("CatchingPresent");
AudioManager.instance.Play("CatchingPresent");
}
}
void SetCountText()
{
countText.text = "Missing: " + count.ToString();
if (count == 0)
{
winText.text = "You saved Christmas!";
}
}
}
}
编辑 2:Unity 中的层次结构:
使用 FindObjectOfType<AudioManager>().Play("CatchingPresent");
时,我自己导入脚本没有任何问题。尝试从编辑器中重新导入脚本(右键单击项目文件夹 > 全部重新导入。这可能需要一段时间,具体取决于项目的大小)
要使用 AudioManager.instance.Play("CatchingPresent");
,您首先需要创建一个静态变量来保存 instance
,就像这样(这仅适用于单例,如果有多个 AudioManager
,则会中断在现场):
public class AudioManager : MonoBehaviour
{
//Create a static AudioManager that will hold the reference to this instance of AudioManager
public static AudioManager Instance;
public Sound[] sounds;
//Assign Instance to the instance of this AudioManager in the constructor
AudioManager()
{
Instance = this;
}
// Rest of the AudioManager code
}
这样做,并使用您的其余代码也适用于我。
通过简单地将音频源添加到 ThirdPersonController(使用我想调用的 AudioClip)并在 if 语句中添加 GetComponent<AudioSource>().Play();
来重新制定我正在遵循的方法并解决了问题,如下所示:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
GetComponent<AudioSource>().Play();
}
}
我使用 Unity 2019.2.14f1 创建一个简单的 3D 游戏。
在那个游戏中,我想在我的玩家与具有特定标签的游戏对象发生碰撞时播放声音。
MainCamera 有一个音频监听器,我正在使用 Cinemachine Free Look,它跟随我的化身,在 ThridPersonController 中(我正在使用出现在 Standard Assets 上的那个 - 但我隐藏了 Ethan 并添加了我自己的 character/avatar).
带有我要销毁标签的游戏对象有一个音频源:
为了让声音在碰撞时播放,我首先创建一个空的游戏对象作为 AudioManager,并向其添加一个新组件(C# 脚本):
using UnityEngine.Audio;
using System;
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public Sound[] sounds;
// Start is called before the first frame update
void Awake()
{
foreach (Sound s in sounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.clip = s.clip;
s.source.volume = s.volume;
s.source.pitch = s.pitch;
}
}
// Update is called once per frame
public void Play (string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.source.Play();
}
}
并创建脚本 Sound.cs:
using UnityEngine.Audio;
using UnityEngine;
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)]
public float volume;
[Range(.1f, 3f)]
public float pitch;
[HideInInspector]
public AudioSource source;
}
之后,在 Unity UI 中,我转到 gameObject AudioManager 中的 Inspector,并在我命名为 CatchingPresent 的脚本中添加了一个新元素。
在第三人称角色脚本中,为了在与游戏对象(具有特定标签)发生碰撞时销毁它,我添加了以下内容:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
}
}
它工作正常,因为特定的 object 在碰撞时消失了。现在,为了在播放器与带有标签的 object 发生碰撞时播放声音 "CatchingPresent",在本例中为 Present
,我尝试将以下内容添加到 if
在 OnCollisionEnter
:
FindObjectOfType<AudioManager>().Play("CatchingPresent");
但是我得到错误:
The type or namespace name 'AudioManager' could not be found (are you missing a using directive or an assembly reference?)
AudioManager.instance.Play("CatchingPresent");
但是我得到错误:
The name 'AudioManager' does not exist in the current context
由于在进入播放模式之前需要修复所有编译器错误,因此欢迎任何有关如何在玩家与带有标签 Present
的游戏对象发生碰撞后播放声音的指导。
编辑 1:假设它有帮助,这里是完整的 ThirdPersonUserControl.cs:
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.CrossPlatformInput;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof (ThirdPersonCharacter))]
public class ThirdPersonUserControl : MonoBehaviour
{
public Text countText;
public Text winText;
private int count;
private ThirdPersonCharacter m_Character; // A reference to the ThirdPersonCharacter on the object
private Transform m_Cam; // A reference to the main camera in the scenes transform
private Vector3 m_CamForward; // The current forward direction of the camera
private Vector3 m_Move;
private bool m_Jump; // the world-relative desired move direction, calculated from the camForward and user input.
private void Start()
{
count = 20;
SetCountText();
winText.text = "";
// get the transform of the main camera
if (Camera.main != null)
{
m_Cam = Camera.main.transform;
}
else
{
Debug.LogWarning(
"Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls.", gameObject);
// we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them!
}
// get the third person character ( this should never be null due to require component )
m_Character = GetComponent<ThirdPersonCharacter>();
}
private void Update()
{
if (!m_Jump)
{
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
}
}
// Fixed update is called in sync with physics
private void FixedUpdate()
{
// read inputs
float h = CrossPlatformInputManager.GetAxis("Horizontal");
float v = CrossPlatformInputManager.GetAxis("Vertical");
bool crouch = Input.GetKey(KeyCode.C);
// calculate move direction to pass to character
if (m_Cam != null)
{
// calculate camera relative direction to move:
m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;
m_Move = v*m_CamForward + h*m_Cam.right;
}
else
{
// we use world-relative directions in the case of no main camera
m_Move = v*Vector3.forward + h*Vector3.right;
}
#if !MOBILE_INPUT
// walk speed multiplier
if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f;
#endif
// pass all parameters to the character control script
m_Character.Move(m_Move, crouch, m_Jump);
m_Jump = false;
}
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
//FindObjectOfType<AudioManager>().Play("CatchingPresent");
AudioManager.instance.Play("CatchingPresent");
}
}
void SetCountText()
{
countText.text = "Missing: " + count.ToString();
if (count == 0)
{
winText.text = "You saved Christmas!";
}
}
}
}
编辑 2:Unity 中的层次结构:
使用 FindObjectOfType<AudioManager>().Play("CatchingPresent");
时,我自己导入脚本没有任何问题。尝试从编辑器中重新导入脚本(右键单击项目文件夹 > 全部重新导入。这可能需要一段时间,具体取决于项目的大小)
要使用 AudioManager.instance.Play("CatchingPresent");
,您首先需要创建一个静态变量来保存 instance
,就像这样(这仅适用于单例,如果有多个 AudioManager
,则会中断在现场):
public class AudioManager : MonoBehaviour
{
//Create a static AudioManager that will hold the reference to this instance of AudioManager
public static AudioManager Instance;
public Sound[] sounds;
//Assign Instance to the instance of this AudioManager in the constructor
AudioManager()
{
Instance = this;
}
// Rest of the AudioManager code
}
这样做,并使用您的其余代码也适用于我。
通过简单地将音频源添加到 ThirdPersonController(使用我想调用的 AudioClip)并在 if 语句中添加 GetComponent<AudioSource>().Play();
来重新制定我正在遵循的方法并解决了问题,如下所示:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
GetComponent<AudioSource>().Play();
}
}