147 lines
3.7 KiB
C#
147 lines
3.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
[RequireComponent(typeof(AudioSource))]
|
|
public class FootstepSound : MonoBehaviour
|
|
{
|
|
[Serializable]
|
|
public struct MeshSurface
|
|
{
|
|
public string tag;
|
|
|
|
public AudioClip clip;
|
|
}
|
|
|
|
[Header("Foot Transforms (final IK)")]
|
|
public Transform leftFoot;
|
|
|
|
public Transform rightFoot;
|
|
|
|
[Header("Raycast / Detection")]
|
|
public float raycastDistance = 0.15f;
|
|
|
|
public float downwardSpeedThreshold = -0.03f;
|
|
|
|
public float footstepCooldown = 0.2f;
|
|
|
|
[Header("Terrain → AudioClips (index = Terrain Layer)")]
|
|
public AudioClip[] terrainLayerClips;
|
|
|
|
[Header("Mesh → AudioClips (by Tag)")]
|
|
public List<MeshSurface> meshSurfaces = new List<MeshSurface>();
|
|
|
|
[Header("Physics Mask for Raycast")]
|
|
public LayerMask terrainAndMeshMask = -1;
|
|
|
|
private AudioSource _src;
|
|
|
|
private Vector3 _prevLeft;
|
|
|
|
private Vector3 _prevRight;
|
|
|
|
private bool _wasLeftGrounded;
|
|
|
|
private bool _wasRightGrounded;
|
|
|
|
private float _nextLeftTime;
|
|
|
|
private float _nextRightTime;
|
|
|
|
private void Awake()
|
|
{
|
|
_src = GetComponent<AudioSource>();
|
|
_src.playOnAwake = false;
|
|
_src.spatialBlend = 1f;
|
|
if ((bool)leftFoot)
|
|
{
|
|
_prevLeft = leftFoot.position;
|
|
}
|
|
if ((bool)rightFoot)
|
|
{
|
|
_prevRight = rightFoot.position;
|
|
}
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
if ((bool)leftFoot)
|
|
{
|
|
HandleFoot(leftFoot, ref _prevLeft, ref _wasLeftGrounded, ref _nextLeftTime);
|
|
}
|
|
if ((bool)rightFoot)
|
|
{
|
|
HandleFoot(rightFoot, ref _prevRight, ref _wasRightGrounded, ref _nextRightTime);
|
|
}
|
|
}
|
|
|
|
private void HandleFoot(Transform foot, ref Vector3 prevPos, ref bool wasGrounded, ref float nextAllowedTime)
|
|
{
|
|
Vector3 position = foot.position;
|
|
Vector3 vector = (position - prevPos) / Time.deltaTime;
|
|
RaycastHit hitInfo;
|
|
bool flag = Physics.Raycast(position + Vector3.up * 0.02f, Vector3.down, out hitInfo, raycastDistance, terrainAndMeshMask, QueryTriggerInteraction.Ignore);
|
|
if (!wasGrounded && flag && vector.y < downwardSpeedThreshold && Time.time >= nextAllowedTime)
|
|
{
|
|
PlayFootstep(in hitInfo);
|
|
nextAllowedTime = Time.time + footstepCooldown;
|
|
}
|
|
wasGrounded = flag;
|
|
prevPos = position;
|
|
}
|
|
|
|
private void PlayFootstep(in RaycastHit hit)
|
|
{
|
|
AudioClip audioClip = null;
|
|
audioClip = ((Physics.OverlapSphere(hit.point, 0.1f, LayerMask.GetMask("Water")).Length != 0) ? GetMeshClip("Water") : ((!hit.collider.TryGetComponent<Terrain>(out var component)) ? GetMeshClip(hit.collider.tag) : GetTerrainClip(component, hit.point)));
|
|
if (!(audioClip == null))
|
|
{
|
|
_src.pitch = UnityEngine.Random.Range(0.93f, 1.07f);
|
|
_src.PlayOneShot(audioClip);
|
|
}
|
|
}
|
|
|
|
private AudioClip GetTerrainClip(Terrain terrain, Vector3 worldPoint)
|
|
{
|
|
TerrainData terrainData = terrain.terrainData;
|
|
if (terrainData == null || terrainData.alphamapLayers == 0)
|
|
{
|
|
return null;
|
|
}
|
|
Vector3 vector = worldPoint - terrain.transform.position;
|
|
float num = Mathf.Clamp01(vector.x / terrainData.size.x);
|
|
float num2 = Mathf.Clamp01(vector.z / terrainData.size.z);
|
|
int x = Mathf.Clamp(Mathf.RoundToInt(num * (float)(terrainData.alphamapWidth - 1)), 0, terrainData.alphamapWidth - 1);
|
|
int y = Mathf.Clamp(Mathf.RoundToInt(num2 * (float)(terrainData.alphamapHeight - 1)), 0, terrainData.alphamapHeight - 1);
|
|
float[,,] alphamaps = terrainData.GetAlphamaps(x, y, 1, 1);
|
|
int num3 = 0;
|
|
float num4 = 0f;
|
|
for (int i = 0; i < alphamaps.GetLength(2); i++)
|
|
{
|
|
float num5 = alphamaps[0, 0, i];
|
|
if (num5 > num4)
|
|
{
|
|
num4 = num5;
|
|
num3 = i;
|
|
}
|
|
}
|
|
if (num3 >= terrainLayerClips.Length)
|
|
{
|
|
return null;
|
|
}
|
|
return terrainLayerClips[num3];
|
|
}
|
|
|
|
private AudioClip GetMeshClip(string tag)
|
|
{
|
|
foreach (MeshSurface meshSurface in meshSurfaces)
|
|
{
|
|
if (meshSurface.tag == tag)
|
|
{
|
|
return meshSurface.clip;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|