i made this script for footsteps that should detect the terrain layer to play the specific footsteps sounds. the problem is that it only detects the terrain when the player is moving downwards on the terrain. when i go up it doesn t even detect it. it also detects that is grounded correctly. what can be the problem?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Footsteps : MonoBehaviour
{
[Header("References")]
[SerializeField] private CharacterController characterController;
[SerializeField] private AudioSource footstepAudioSource;
[SerializeField] private float walkStepRate = 0.5f;
[SerializeField] private float runStepRate = 0.3f;
[SerializeField] private float groundCheckRadius = 0.5f;
[SerializeField] private LayerMask groundLayer;
[System.Serializable]
public class TerrainFootstep
{
public TerrainLayer terrainLayer; // Terrain layer for which sounds should be played
public List<AudioClip> footstepSounds; // List of footstep sounds for this terrain layer
}
[Header("Footstep Settings")]
[SerializeField] private List<TerrainFootstep> terrainFootsteps; // List of terrain footstep settings
private float footstepTimer = 0f;
private bool isRunning = false;
void Update()
{
isRunning = Input.GetKey(KeyCode.LeftShift);
// Check if the player is grounded and moving
if (IsGrounded() && characterController.velocity.magnitude > 0.1f)
{
footstepTimer += Time.deltaTime;
if (footstepTimer >= (isRunning ? runStepRate : walkStepRate))
{
PlayFootstepSound();
footstepTimer = 0f;
}
}
}
private bool IsGrounded()
{
// Check if the player is grounded using a sphere check
bool grounded = Physics.CheckSphere(characterController.transform.position, groundCheckRadius, groundLayer);
Debug.Log("Grounded status: " + grounded);
return grounded;
}
private void PlayFootstepSound()
{
Terrain terrain = GetTerrainUnderFoot();
if (terrain != null)
{
int terrainLayerIndex = GetActiveTerrainLayerIndex(characterController.transform.position, terrain);
Debug.Log("Terrain layer index: " + terrainLayerIndex);
PlayFootstepForTerrainLayer(terrainLayerIndex, terrain);
}
else
{
Debug.LogWarning("No terrain detected underfoot.");
}
}
private Terrain GetTerrainUnderFoot()
{
RaycastHit hit;
// Raycast down to check for terrain under the player's foot
if (Physics.Raycast(characterController.transform.position, Vector3.down, out hit, groundCheckRadius, groundLayer))
{
Terrain terrain = hit.collider.GetComponent<Terrain>();
if (terrain != null)
{
Debug.Log("Terrain detected underfoot: " + terrain.name);
return terrain;
}
}
Debug.LogWarning("No terrain detected.");
return null;
}
private int GetActiveTerrainLayerIndex(Vector3 position, Terrain terrain)
{
TerrainData terrainData = terrain.terrainData;
Vector3 terrainPos = terrain.transform.position;
int mapX = Mathf.RoundToInt((position.x - terrainPos.x) / terrainData.size.x * terrainData.alphamapWidth);
int mapZ = Mathf.RoundToInt((position.z - terrainPos.z) / terrainData.size.z * terrainData.alphamapHeight);
float[,,] splatmapData = terrainData.GetAlphamaps(mapX, mapZ, 1, 1);
float maxMix = 0;
int maxIndex = 0;
for (int i = 0; i < splatmapData.GetUpperBound(2) + 1; i++)
{
if (splatmapData[0, 0, i] > maxMix)
{
maxMix = splatmapData[0, 0, i];
maxIndex = i;
}
}
return maxIndex;
}
private void PlayFootstepForTerrainLayer(int layerIndex, Terrain terrain)
{
if (layerIndex >= 0 && layerIndex < terrain.terrainData.terrainLayers.Length)
{
TerrainLayer activeLayer = terrain.terrainData.terrainLayers[layerIndex];
foreach (var terrainFootstep in terrainFootsteps)
{
if (terrainFootstep.terrainLayer == activeLayer && terrainFootstep.footstepSounds.Count > 0)
{
int randomIndex = Random.Range(0, terrainFootstep.footstepSounds.Count);
AudioClip footstepSound = terrainFootstep.footstepSounds[randomIndex];
footstepAudioSource.PlayOneShot(footstepSound);
Debug.Log("Playing footstep sound: " + footstepSound.name);
return;
}
}
Debug.LogWarning("No footstep sounds assigned for this terrain layer.");
}
else
{
Debug.LogWarning("Layer index out of range or not assigned correctly.");
}
}
}