> For the complete documentation index, see [llms.txt](https://malbersanimations.gitbook.io/animal-controller/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://malbersanimations.gitbook.io/animal-controller/utilities/steps-manager.md).

# Steps Manager

Last updated AC v1.5.3

## Overview

The Steps Manager plays footstep sounds and emits Foot Tracks and Dust particles every time a foot touches the ground. It makes the animal feel more alive when it interacts with the world.

Since AC v1.5.3 the Steps Manager is surface aware. With a **Step Surfaces** asset assigned, each ground type (grass, sand, wood, stone...) can play its own sound and use its own Tracks and Dust particles. Without that asset it keeps the classic behaviour: one sound list, one Tracks system and one Dust system for every surface.

![](https://963537199-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lzhr1XSMzMqNXjRnNlb%2F-LzwhaokWjmA6YeG0muL%2F-Lzx17z9ZNUlksy90a_b%2FUnity_SSPqb3Xh3x.png?alt=media\&token=a4920c49-7639-4a67-aed7-c55a17a170b4)

Add it with **Add Component → Malbers → Utilities → Effects - Audio → Step Manager**.

## Requirements

* One **Steps Manager** on the character (usually on the root or on a child object).
* One [**Step Trigger**](/animal-controller/utilities/step-trigger.md) on each foot, with a `SphereCollider` set as Trigger. The Step Trigger finds its manager automatically on Awake when the `Steps Manager` field is empty.
* An `AudioSource` on each foot. If a foot has none, the Step Trigger adds one on Awake (3D sound, volume taken from `Steps Volume`).
* The ground colliders must be on a layer included in `Ground Layer`.
* Optional: a **Step Surfaces** asset for per-surface sounds and particles.

## How it Works

1. The `SphereCollider` on a foot enters a ground collider. The Step Trigger ignores trigger colliders and colliders outside `Ground Layer`.
2. The Step Trigger calls `EnterStep(foot, groundCollider)` on the Steps Manager and waits `Wait Next Step` seconds before it can fire again.
3. The manager skips the step when `Active` is false, or when `Tracks Only On States` has items and the character is not in one of those States.
4. If a **Step Surfaces** asset is assigned, the manager resolves the surface under the foot (see below).
5. The sound is chosen in this order: the matched surface entry `Sound` → the Step Surfaces `Default Sound` → the manager `Sounds` field. The clip is played on the foot `AudioSource` at `Steps Volume`.
6. The manager raycasts down from the foot against the ground collider. On a hit it emits `Dust Particles` from the Dust system and one particle from the Tracks system, rotated to the ground normal and lifted by `Track Offset`.

### How a surface is resolved

Surface detection lives in the static `SurfaceResolver` class. It is shared by the Steps Manager, the Wagon Controller (Wheel Surfaces) and any custom component. The ground collider is classified once and cached, then matched in this order:

1. **Surface Tag.** If the ground collider (or one of its parents or children) has a `Surface Tag` component, its `Surface ID` is looked up on the Step Surfaces asset. A match wins immediately.
2. **Terrain Layer.** If the ground is a `TerrainCollider`, the dominant Terrain Layer under the foot is read from the terrain alphamap (a 1x1 read). Its **Diffuse** texture is looked up in the `Textures` of every entry.
3. **Mesh material.** For any other collider, the main texture of the parent `Renderer` shared material is looked up in the `Textures` of every entry. The properties probed are `_MainTex`, `_BaseMap`, `_BaseColorMap`, `_Albedo` and `_AlbedoMap` (Built-in, URP, HDRP and common custom shaders).

When nothing matches, the manager falls back to `Default Sound` on the asset and to the default Tracks and Dust.

{% hint style="info" %}
A `Surface Tag` always wins over texture matching. Use it on props that share a texture with a different surface, or on grounds whose shader has no main texture.
{% endhint %}

{% hint style="info" %}
Texture matching compares the texture asset reference, not its name. Drag the same texture used by the Terrain Layer (its Diffuse) or by the Material into the entry `Textures` list. Steps 2 and 3 are skipped entirely when no entry on the asset has a texture.
{% endhint %}

{% hint style="info" %}
Each texture and each Surface ID can belong to one entry only. Duplicates log a warning in the Editor and the first entry wins.
{% endhint %}

## Parameters

The inspector has two tabs: **General** and **Debug**.

### General tab

#### Active

Enables or disables the Steps Manager. When it is false on Awake, the Step Triggers deactivate themselves.

#### Wait Next Step

Time in seconds a foot waits before it can register a new step.

#### Ground Layer

Layer Mask used to find the ground. Steps on colliders outside this mask are ignored.

#### Tracks Only On States

List of `StateID`. Tracks, dust and sounds play only while the character is in one of these States. When the list is empty they are always on.

#### Steps Volume

Volume applied to every foot `AudioSource` each time a step sound is played. Changing it at runtime is heard on the next step.

#### Surface Sounds

Optional **Step Surfaces** asset with per-surface sounds and particle overrides. When empty, the `Sounds` field below is used for every surface.

#### Sounds

Sound played when the animal creates a track. It accepts an `AudioClip`, an Audio Random Container, or an Audio Clip List variable (one clip picked at random). It is the last fallback when a Step Surfaces asset is assigned.

#### Tracks

Particle System for the foot tracks. It can be a scene object or a prefab. A prefab is instantiated under the manager on Awake.

#### Track Offset

Extra distance from the ground to avoid the track flickering with the ground.

#### Instantiate Tracks

Instantiates a new Tracks Particle System on every step and parents it to the ground object, instead of emitting from the global Tracks system. Use it for tracks on moving platforms.

#### Ground Is Static

Shown when `Instantiate Tracks` is on. Static grounds emit from the global Tracks system and only non-static grounds get an instantiated track.

#### Dust

Particle System for the dust. It can be a scene object or a prefab. A prefab is instantiated under the manager on the first step.

#### Dust Particles

Amount of particles emitted from the Dust system on every step.

#### Scale

Scale applied to the Tracks and Dust particle systems, including the per-surface overrides.

### Debug tab

#### Console

Logs every step to the console: foot, ground, surface entry, sound, dust and tracks.

#### Simulate Step

Play Mode only. Raycasts down from the last (or first) foot using `Ground Layer` and calls `EnterStep`, so the setup can be tested without moving the character.

#### Feet

Lists every Step Trigger that resolves to this manager and flags a missing `AudioSource` or a collider that is not set as Trigger.

#### Live readout

Play Mode only. Shows the step counters, the last foot and ground, how the ground was classified (Surface Tag, Terrain layer or Mesh material), the matched texture, the surface entry, the sound and where it came from, and the Dust and Tracks systems used.

\[Insert Image - Steps Manager Debug tab in Play Mode]

## Step Surfaces asset

`MStepSurfaces` is a ScriptableObject that stores footstep sounds and particle overrides per surface.

Create it with **Create → Malbers Animations → Effects → Step Surfaces**, then assign it to the `Surface Sounds` field of the Steps Manager. The same asset can be shared by every character, and by the Wagon Controller `Wheel Surfaces`.

\[Insert Image - Step Surfaces asset inspector]

#### Default Sound

Fallback sound used when the ground matches no surface entry. It accepts an `AudioClip`, an Audio Random Container, or an Audio Clip List variable.

#### Surfaces

Reorderable list of surface entries. Each row shows the entry `Name` and its `Surface ID`. Select a row to edit the rest of the entry.

### Surface entry fields

#### Name

Name of the surface. Inspector readability only.

#### Surface ID

Optional `SurfaceID` asset (create one with **Create → Malbers Animations → ID → Surface**). Matched first when the ground has a `Surface Tag` component with the same ID.

#### Textures

Terrain Layer diffuse textures and/or mesh material main textures that identify this surface.

#### Sound

Sound played when stepping on this surface. `AudioClip` or Audio Random Container. When empty, the asset `Default Sound` is used.

#### Track Mode

How this surface treats the Tracks particles: `None`, `Disable` or `Override`.

#### Track Override

Custom Tracks Particle System. Shown only when `Track Mode` is `Override`.

#### Dust Mode

How this surface treats the Dust particles: `None`, `Disable` or `Override`.

#### Dust Override

Custom Dust Particle System. Shown only when `Dust Mode` is `Override`.

| Field  | None                            | Disable                   | Override                                                                               |
| ------ | ------------------------------- | ------------------------- | -------------------------------------------------------------------------------------- |
| Tracks | Uses the Steps Manager `Tracks` | No tracks on this surface | Uses `Track Override` (prefab or scene object, instantiated once and cached per entry) |
| Dust   | Uses the Steps Manager `Dust`   | No dust on this surface   | Uses `Dust Override` (prefab or scene object, instantiated once and cached per entry)  |

{% hint style="info" %}
`Override` with an empty Particle System falls back to the Steps Manager default. The inspector warns about it, and also about entries that have neither a `Surface ID` nor a `Texture`, since those can never match.
{% endhint %}

## Events

The Steps Manager has no Unity Events. Use the **Debug** tab or the `Console` toggle to inspect what each step did.

## API

```csharp
// Enable or disable the steps (e.g. while sleeping or dead)
public virtual void EnableSteps(bool value);

// Called by a Step Trigger when a foot touches the ground
public virtual void EnterStep(StepTrigger foot, Collider surface);

// Animator Behaviour messages (IAnimatorListener). "EnableSteps" with a bool works
public virtual bool OnAnimatorBehaviourMessage(string message, object value);

// Play Mode readouts (read-only)
public int StepCount;
public int SkippedSteps;
public StepSurface LastSurface;      // matched entry, null when none
public AudioResource LastSound;
public string LastSoundSource;       // "Surface Entry", "Asset Default Sound", "Sounds Field" or "None"
```

Step Surfaces asset (`MStepSurfaces`):

```csharp
public StepSurface FindBySurfaceID(SurfaceID id);
public StepSurface FindByTexture(Texture texture);
public StepSurface FindByName(string surfaceName);

// Runtime edits keep the lookup maps in sync
public bool AddTexture(StepSurface entry, Texture texture);
public bool AddTexture(SurfaceID id, Texture texture);
public bool AddTexture(string surfaceName, Texture texture);
public bool RemoveTexture(StepSurface entry, Texture texture);
public bool RemoveTexture(Texture texture);

public void Invalidate();  // rebuild the maps after editing [Surfaces] directly
```

Any component can resolve a surface with the shared `SurfaceResolver`. This is what the Wagon Controller does for its wheels:

```csharp
using MalbersAnimations;
using System.Collections.Generic;
using UnityEngine;

public class WheelSurface : MonoBehaviour
{
    public MStepSurfaces Surfaces;

    // Optional. Caches the classification of each ground collider
    private readonly Dictionary<Collider, StepGroundInfo> cache = new();

    void OnGround(Collider ground, Vector3 contactPoint)
    {
        StepSurface entry = SurfaceResolver.Resolve(Surfaces, ground, contactPoint, cache);

        AudioResource sound = entry != null && entry.sound != null
            ? entry.sound
            : Surfaces.DefaultSound.Value;
    }
}
```

`SurfaceResolver` also exposes the individual steps:

```csharp
public static StepGroundInfo ClassifyGround(Collider ground);                       // Surface Tag, Terrain or Renderer
public static Texture SampleDominantLayer(in StepGroundInfo info, Vector3 worldPos); // dominant Terrain Layer diffuse
public static Texture GetMaterialMainTexture(Material mat);                          // main texture without shader errors
```
