Terrain doesn t get detected

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.");
        }
    }
}

Hi @TuyLoL,

It sounds like you’re encountering an issue with detecting the terrain properly when moving upwards. The problem could be related to how the raycast is being used in the GetTerrainUnderFoot() method.

When you’re moving upwards, the player’s position might be slightly above the terrain, and with a Vector3.down raycast, the terrain might be out of reach, especially if the groundCheckRadius is small. Here are a few suggestions to help resolve the issue:

  1. Increase the Raycast Distance: You could increase the raycast distance in the Physics.Raycast method. Instead of using groundCheckRadius, try increasing it a bit to ensure the ray hits the terrain when moving uphill:
if (Physics.Raycast(characterController.transform.position, Vector3.down, out hit, groundCheckRadius + 1f, groundLayer))
  1. Adjust the Raycast Origin: Another option is to start the raycast slightly higher than the player’s current position, ensuring it hits the terrain:
Vector3 rayOrigin = characterController.transform.position + Vector3.up * 0.5f;
if (Physics.Raycast(rayOrigin, Vector3.down, out hit, groundCheckRadius + 1f, groundLayer))
  1. Double Check Terrain Layer: Ensure the terrain is correctly assigned to the specified groundLayer mask. If it’s not included, the raycast won’t detect it correctly.
  2. Debugging the Raycast: You can also debug the raycast visually by drawing a debug line to see where it’s hitting:
Debug.DrawLine(rayOrigin, rayOrigin + Vector3.down * (groundCheckRadius + 1f), Color.red);

This will give you a visual representation of where the ray is cast and might help you pinpoint why it’s missing the terrain.

By implementing one or more of these adjustments, you should be able to get more consistent terrain detection, even when moving upwards.

Let me know if this helps or if you need further assistance!