Files
zeling_v2/Assets/_Game/Scripts/Audio/FootstepSoundPlayer.cs
2026-05-17 07:56:12 +08:00

92 lines
3.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BaseGames.Core;
using UnityEngine;
namespace BaseGames.Audio
{
/// <summary>
/// 材质感知脚步声播放器。挂在玩家 GameObject 上,由 PlayerFeedback.PlayFootstep() 驱动。
/// 每次触发时向下 OverlapCircle 探测脚下碰撞体,读取 FootstepMaterialMarker 决定材质,
/// 再从 FootstepAudioConfigSO 随机选取 Clip 并叠加 Pitch 扰动后播放。
/// 未探测到 Marker 时回落到 FootstepMaterial.Stone。
/// </summary>
public class FootstepSoundPlayer : MonoBehaviour
{
[Header("音效配置")]
[SerializeField] private FootstepAudioConfigSO _config;
[Header("地面探测")]
[Tooltip("通常复用 PlayerMovement 下的 GroundCheck 子对象")]
[SerializeField] private Transform _groundProbe;
[SerializeField] private LayerMask _groundLayer;
[SerializeField] private float _probeRadius = 0.15f;
private AudioSource _audioSource;
private readonly Collider2D[] _probeBuffer = new Collider2D[4];
private void Awake()
{
// 优先使用同对象已有的 AudioSource否则自动添加
_audioSource = GetComponent<AudioSource>();
if (_audioSource == null)
_audioSource = gameObject.AddComponent<AudioSource>();
_audioSource.spatialBlend = 0f; // 始终 2D
_audioSource.playOnAwake = false;
}
/// <summary>
/// 由 PlayerFeedback.PlayFootstep() 调用。
/// </summary>
public void Play()
{
if (_config == null) return;
var material = DetectGroundMaterial();
var entry = _config.GetEntry(material);
if (entry == null) return;
var e = entry.Value;
if (e.clips == null || e.clips.Length == 0) return;
var clip = e.clips[Random.Range(0, e.clips.Length)];
if (clip == null) return;
// pitchVariance 为 [0.8, 1.2],以 1.0 为中心扩展偏移区间
float half = e.pitchVariance - 1f;
_audioSource.pitch = Random.Range(1f - half, 1f + half);
_audioSource.PlayOneShot(clip, e.volume);
}
// ── 地面材质探测 ──────────────────────────────────────────────────────
private FootstepMaterial DetectGroundMaterial()
{
Vector2 origin = _groundProbe != null
? (Vector2)_groundProbe.position
: (Vector2)transform.position + Vector2.down * 0.5f;
int count = Physics2D.OverlapCircleNonAlloc(origin, _probeRadius, _probeBuffer, _groundLayer);
for (int i = 0; i < count; i++)
{
var marker = _probeBuffer[i].GetComponent<FootstepMaterialMarker>();
if (marker != null)
return marker.material;
}
return FootstepMaterial.Stone; // 默认回落
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
Gizmos.color = new Color(0.6f, 1f, 0.6f, 0.5f);
Vector3 origin = _groundProbe != null
? _groundProbe.position
: transform.position + Vector3.down * 0.5f;
Gizmos.DrawWireSphere(origin, _probeRadius);
}
#endif
}
}