今天实现的内容:
制作脚步声,学习了一下ScriptableObject的原理。脚本声音资源采用ScriptableObject的方式储存应用。当角色着地并正在发生位移时,播放脚步声。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "FPS/Footstep Audio Data")]
public class FootstepAudioData : ScriptableObject
{
public List<FootstepAudio> footstepAudios = new List<FootstepAudio>();
}
[System.Serializable]
public class FootstepAudio
{
// 用于标记踩在什么表面上
public string Tag;
// 声音片段列表
public List<AudioClip> AudioClips = new List<AudioClip>();
// 声音间隔
public float walkVoiceInterval;
//public float sprintingVoiceInterval;
//public float CrouchingVoiceInterval;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerFootstepListener : MonoBehaviour
{
public FootstepAudioData footstepAudioData;
public AudioSource footstepAudioSource;
private CharacterController characterController;
private Transform footstepTransform;
private float timePassed = 0;
private FPCharacterControllerMovement movement;
// 角色移动或者发生较大幅度位移时 播放脚步声
// 根据角色踩踏的不同材质播放不同的声音
// 具体实现方式采用Physics API
private void Start()
{
characterController = GetComponent<CharacterController>();
footstepTransform = transform;
movement = GetComponent<FPCharacterControllerMovement>();
}
private void FixedUpdate()
{
// 角色处于地面
if(characterController.isGrounded)
{
// 角色正在移动
if(characterController.velocity.normalized.sqrMagnitude > 0.1f)
{
// 设置下一次播放的时间
timePassed += Time.deltaTime;
// 播放移动声音
if(Physics.Linecast(footstepTransform.position,
footstepTransform.position + Vector3.down * characterController.height,
out RaycastHit temp_hitInfo)) //检测当前踩踏在什么地面上
{
#if UNITY_EDITOR
Debug.Log(temp_hitInfo.collider.tag);
Debug.DrawRay(footstepTransform.position,
-footstepTransform.up * characterController.height, Color.red);
#endif
// 对于所有类型的脚步声
foreach (var temp_audioElement in footstepAudioData.footstepAudios)
{
// 找到匹配的类型
if (temp_hitInfo.collider.CompareTag(temp_audioElement.Tag))
{
// 用于保存实际要用的声音播放间隔
float temp_VoiceInterval = 0;
if (movement.m_isSprinting && movement.m_isCrouched) //是否在蹲下时冲刺
{
temp_VoiceInterval = temp_audioElement.walkVoiceInterval;
}
else if(movement.m_isCrouched) //是否蹲下
{
temp_VoiceInterval = temp_audioElement.walkVoiceInterval * 2;
}
else if (movement.m_isSprinting) //是否在冲刺
{
temp_VoiceInterval = temp_audioElement.walkVoiceInterval / 2;
}
else // 那就只是在走了
{
temp_VoiceInterval = temp_audioElement.walkVoiceInterval;
}
Debug.Log(temp_VoiceInterval);
// 如果到了下一次播放时间
if (timePassed >= temp_VoiceInterval)
{
// 获取这个类型脚步声的素材总数
int temp_audioCount = temp_audioElement.AudioClips.Count;
// 随机获取一个
int temp_audioIndex = UnityEngine.Random.Range(0, temp_audioCount);
// 获取到对应的声音片段
AudioClip temp_footstepAudioClip = temp_audioElement.AudioClips[temp_audioIndex];
// 播放音效
footstepAudioSource.clip = temp_footstepAudioClip;
footstepAudioSource.Play();
timePassed = 0;
// 找到了就行了 直接跳出循环
break;
}
}
}
}
}
}
}
}
FootstepAudioData负责用于生成存储音效资源的ScriptableObject,PlayerFootstepListener用于角色所处地面的判断,找到对应的音效,随机获取音效,并播放音效。音效有播放间隔,来勉强达到听起来还算真实的效果吗,还实现了不同的运动状态对应不同的音效播放间隔。
效果:移动时的脚步声以及BUG
BUG以及缺陷:
有时会莫名其妙的检测不出地面的Tag。但其实是我的射线检测写错了,检测的end点应该为角色往下的点,而不是原点往下。以上代码以改正为正确的。
蹲下时没有脚步声了。应该是射线检测不到地面了。
最大的缺陷,这个音效真的很难听,算了,学会功能的实现就好。
值得注意的:
如何判断移动状态?我直接引用了之前的移动控制脚本,脚本中有描述运动状态的bool值,这一点和老师做的不一样。