using UnityEngine; using NeoFPS.Constants; using NeoFPS.CharacterMotion; using UnityEngine.Events; namespace NeoFPS { [RequireComponent (typeof (MotionController), typeof (ICharacterAudioHandler))] [HelpURL("https://docs.neofps.com/manual/audioref-mb-surfacefootstepaudiosystem.html")] public class SurfaceFootstepAudioSystem : MonoBehaviour { private MotionController m_MotionController = null; private ICharacterAudioHandler m_AudioHandler = null; private ISurfaceFootstepAudio m_AudioSetup = null; private RaycastHit m_Hit = new RaycastHit(); private FpsSurfaceMaterial m_LastSurfaceId = FpsSurfaceMaterial.Default; public event UnityAction onFootStep; public enum Direction { Down, InverseGroundNormal, LocalVector, WorldVector, WorldParameter, WorldParameterInverse, LocalParameter, LocalParameterInverse } public FpsSurfaceMaterial lastStepMaterial { get; private set; } private void Awake() { m_MotionController = GetComponent(); m_AudioHandler = GetComponent(); } public void SetFootstepAudio(ISurfaceFootstepAudio audio) { // Subscribe to step tracking if (m_AudioSetup == null && audio != null && audio.footstepAudio != null) m_MotionController.onStep += OnStep; // Unsubscribe from step tracking if (m_AudioSetup != null && audio == null) m_MotionController.onStep -= OnStep; m_AudioSetup = audio; } void OnStep() { float sqrSpeed = m_MotionController.characterController.velocity.sqrMagnitude; float minSpeed = m_AudioSetup.minimumSpeed; if (sqrSpeed > minSpeed * minSpeed) { // Get the surface var surface = GetGroundSurface(); // Play the audio float volume; AudioClip clip = m_AudioSetup.footstepAudio.GetAudioClip(surface, out volume); if (clip != null) m_AudioHandler.PlayClip(clip, FpsCharacterAudioSource.Feet, volume); // Fire footstep event if (onFootStep != null) onFootStep(surface); } } FpsSurfaceMaterial GetGroundSurface() { FpsSurfaceMaterial result = m_LastSurfaceId; // Get charactercontroller info var cc = m_MotionController.characterController; var radius = cc.radius; var normalisedHeight = radius / cc.height; // Raycast for surface type if (cc.RayCast(normalisedHeight, m_AudioSetup.castDirection * (m_AudioSetup.castRange + radius), m_AudioSetup.castSpace, out m_Hit, PhysicsFilter.Masks.BulletBlockers, QueryTriggerInteraction.Ignore)) { // Get surface ID from ray hit Transform t = m_Hit.transform; if (t != null) { BaseSurface s = t.GetComponent(); if (s != null) result = s.GetSurface(m_Hit); else result = FpsSurfaceMaterial.Default; } m_LastSurfaceId = result; } return result; } } }