Files
2026-03-03 05:27:03 +05:00

178 lines
6.1 KiB
C#

using UnityEngine;
using UHFPS.Scriptable;
using UHFPS.Tools;
using static UHFPS.Scriptable.SurfaceDefinitionSet;
namespace UHFPS.Runtime
{
[RequireComponent(typeof(AudioSource))]
public class FootstepsSystem : PlayerComponent
{
public enum FootstepStyleEnum { Timed, HeadBob, Animation }
public SurfaceDefinitionSet SurfaceDefinitionSet;
public FootstepStyleEnum FootstepStyle;
public SurfaceDetection SurfaceDetection;
public LayerMask FootstepsMask;
public float StepPlayerVelocity = 0.1f;
public float JumpStepAirTime = 0.1f;
public float WalkStepTime = 1f;
public float RunStepTime = 1f;
public float LandStepTime = 1f;
[Range(-1f, 1f)]
public float HeadBobStepWave = -0.9f;
[Range(0, 1)] public float WalkingVolume = 1f;
[Range(0, 1)] public float RunningVolume = 1f;
[Range(0, 1)] public float LandVolume = 1f;
public SurfaceDefinition CurrentSurface;
private AudioSource audioSource;
private Collider surfaceUnder;
private bool isWalking;
private bool isRunning;
private int lastStep;
private int lastLandStep;
private float stepTime;
private bool waveStep;
private float airTime;
private bool wasInAir;
private void Awake()
{
audioSource = GetComponent<AudioSource>();
}
private void OnControllerColliderHit(ControllerColliderHit hit)
{
surfaceUnder = FootstepsMask.CompareLayer(hit.gameObject.layer)
? hit.collider : null;
}
private void Update()
{
if(stepTime > 0f) stepTime -= Time.deltaTime;
if (PlayerStateMachine.IsGrounded)
{
if (surfaceUnder != null)
{
CurrentSurface = SurfaceDefinitionSet.GetSurface(surfaceUnder.gameObject, transform.position, SurfaceDetection);
if (FootstepStyle != FootstepStyleEnum.Animation && CurrentSurface != null)
EvaluateFootsteps(CurrentSurface);
}
}
else
{
airTime += Time.deltaTime;
wasInAir = true;
}
}
private void EvaluateFootsteps(SurfaceDefinition surface)
{
float playerVelocity = PlayerCollider.velocity.magnitude;
bool isCrouching = PlayerStateMachine.IsCurrent(PlayerStateMachine.CROUCH_STATE);
isWalking = PlayerStateMachine.IsCurrent(PlayerStateMachine.WALK_STATE);
isRunning = PlayerStateMachine.IsCurrent(PlayerStateMachine.RUN_STATE);
if (isCrouching)
return;
if (FootstepStyle == FootstepStyleEnum.Timed)
{
if(wasInAir)
{
if(airTime >= LandStepTime)
PlayFootstep(surface, true);
airTime = 0;
wasInAir = false;
}
else if(playerVelocity > StepPlayerVelocity && stepTime <= 0)
{
PlayFootstep(surface, false);
stepTime = isWalking ? WalkStepTime : isRunning ? RunStepTime : 0;
}
}
else if (FootstepStyle == FootstepStyleEnum.HeadBob)
{
if (wasInAir)
{
if (airTime >= LandStepTime)
PlayFootstep(surface, true);
airTime = 0;
wasInAir = false;
}
else if (playerVelocity > StepPlayerVelocity)
{
float yWave = PlayerManager.MotionController.BobWave;
if (yWave < HeadBobStepWave && !waveStep)
{
PlayFootstep(surface, false);
waveStep = true;
}
else if (yWave > HeadBobStepWave && waveStep)
{
waveStep = false;
}
}
}
}
private void PlayFootstep(SurfaceDefinition surface, bool isLand)
{
if (!isLand && surface.SurfaceFootsteps.Count > 0)
{
lastStep = GameTools.RandomUnique(0, surface.SurfaceFootsteps.Count, lastStep);
AudioClip footstep = surface.SurfaceFootsteps[lastStep];
float volume = surface.FootstepsVolume;
float volumeScale = (isWalking ? WalkingVolume : isRunning ? RunningVolume : 0f) * volume;
audioSource.PlayOneShot(footstep, volumeScale);
}
else if (surface.SurfaceLandSteps.Count > 0)
{
lastLandStep = GameTools.RandomUnique(0, surface.SurfaceLandSteps.Count, lastLandStep);
AudioClip landStep = surface.SurfaceLandSteps[lastLandStep];
float volume = surface.LandStepsVolume;
float volumeScale = LandVolume * volume;
audioSource.PlayOneShot(landStep, volumeScale);
}
}
public void PlayFootstep(bool runningStep)
{
if (surfaceUnder == null)
return;
CurrentSurface = SurfaceDefinitionSet.GetSurface(surfaceUnder.gameObject, transform.position, SurfaceDetection);
if (CurrentSurface != null)
{
isWalking = !runningStep;
isRunning = runningStep;
PlayFootstep(CurrentSurface, false);
}
}
public void PlayLandSteps()
{
if (surfaceUnder == null)
return;
CurrentSurface = SurfaceDefinitionSet.GetSurface(surfaceUnder.gameObject, transform.position, SurfaceDetection);
if (CurrentSurface != null) PlayFootstep(CurrentSurface, true);
}
}
}