Guidance / Cookbook
Footsteps & Movement
Recipe: Basic Footsteps with Variations
📋 What: Player footsteps with natural variation
🎯 Use Case: Character locomotion
Asset Setup
1. Create Container:
- Create → Random Container
- Name: “Player_Footsteps_RnC”
- Inspector:
- Audio Clips: Add 6-8 footstep sounds
- Avoid Repeat Last: 2
- Use Weighting: ✓ (equal weights)
- Volume: 0.6
- Enable Volume Randomization: ✓ (Min: -2, Max: 2)
- Enable Pitch Randomization: ✓ (Min: -100, Max: 100)
- Is3D: ✓
- Min Distance: 1
- Max Distance: 15
2. Create Event:
- Create → Audio Event
- Name: “Play_Player_Footstep”
- Inspector:
- Priority: High
- Max Instances: 4
- Actions → Play Player_Footsteps_RnC → Footsteps_Bus
Code (Animation Events)
using UnityEngine;
using AudioSystem;
public class PlayerFootsteps : MonoBehaviour
{
[SerializeField] private AudioEvent footstepEvent;
// Called from animation event on footfall frames
public void PlayFootstep()
{
if (footstepEvent != null)
{
footstepEvent.Post(gameObject, transform.position);
}
}
}
Setup:
- Attach to player GameObject
- Assign footstep event
- In Animation window, add Animation Event at each footfall
- Set Function: “PlayFootstep”
✅ Result: Natural-sounding footsteps synced to animation!
Recipe: Surface-Dependent Footsteps
📋 What: Different footstep sounds per surface type
🎯 Use Case: Realistic environmental feedback
Asset Setup
1. Create Surface Containers:
Grass:
- Random Container: “Footsteps_Grass_RnC”
- 6 grass footstep clips
- Avoid Repeat Last: 2
Metal:
- Random Container: “Footsteps_Metal_RnC”
- 5 metal footstep clips
- Volume: 0.8 (louder)
Wood:
- Random Container: “Footsteps_Wood_RnC”
- 6 wood footstep clips
- Volume: 0.7
Water:
- Random Container: “Footsteps_Water_RnC”
- 5 water splash clips
- Volume: 0.65
2. Create Switch Container:
- Create → Switch Container
- Name: “Player_Footsteps_Switch_SwC”
- Inspector:
- Switch Group Name: “Surface_Type”
- Switch Entries:
- [0] “Grass” → Footsteps_Grass_RnC
- [1] “Metal” → Footsteps_Metal_RnC
- [2] “Wood” → Footsteps_Wood_RnC
- [3] “Water” → Footsteps_Water_RnC
- Default Container: Footsteps_Grass_RnC
- Is3D: ✓
- Min Distance: 1
- Max Distance: 15
3. Create Event:
- Event: “Play_Player_Footstep”
- Action: Play Player_Footsteps_Switch_SwC → Footsteps_Bus
Code
using UnityEngine;
using AudioSystem;
public class SurfaceFootsteps : MonoBehaviour
{
[SerializeField] private AudioEvent footstepEvent;
[SerializeField] private LayerMask groundLayers;
[SerializeField] private float raycastDistance = 2f;
// Called from animation event
public void PlayFootstep()
{
string surface = DetectSurface();
AudioManager.Instance.SetSwitch("Surface_Type", surface);
if (footstepEvent != null)
{
footstepEvent.Post(gameObject, transform.position);
}
}
private string DetectSurface()
{
RaycastHit hit;
Vector3 origin = transform.position + Vector3.up * 0.1f;
if (Physics.Raycast(origin, Vector3.down, out hit, raycastDistance, groundLayers))
{
// Method 1: By tag
if (hit.collider.CompareTag("Metal")) return "Metal";
if (hit.collider.CompareTag("Wood")) return "Wood";
if (hit.collider.CompareTag("Water")) return "Water";
// Method 2: By material name
Renderer renderer = hit.collider.GetComponent<Renderer>();
if (renderer != null && renderer.sharedMaterial != null)
{
string matName = renderer.sharedMaterial.name.ToLower();
if (matName.Contains("metal")) return "Metal";
if (matName.Contains("wood")) return "Wood";
if (matName.Contains("water")) return "Water";
}
// Method 3: By layer
int hitLayer = hit.collider.gameObject.layer;
if (hitLayer == LayerMask.NameToLayer("Metal")) return "Metal";
if (hitLayer == LayerMask.NameToLayer("Wood")) return "Wood";
}
return "Grass"; // Default
}
void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Vector3 origin = transform.position + Vector3.up * 0.1f;
Gizmos.DrawLine(origin, origin + Vector3.down * raycastDistance);
}
}
Surface Tagging Methods:
Option 1 - Tags:
Tag ground objects: "Metal", "Wood", "Grass", "Water"
Pro: Simple
Con: Limited to small number of tags
Option 2 - Material Names:
Name materials: "Floor_Metal", "Terrain_Grass", etc.
Pro: Unlimited surfaces
Con: Requires naming convention
Option 3 - Layers:
Create layers: Metal, Wood, Grass
Pro: Clean, performant
Con: Limited layer count
✅ Result: Realistic surface-dependent footsteps!
Recipe: Footstep Timer (No Animation Events)
📋 What: Timer-based footsteps for procedural animation or simple movement
🎯 Use Case: Games without foot animation events, procedural animation
💻 Code:
using UnityEngine;
using AudioSystem;
[RequireComponent(typeof(CharacterController))]
public class FootstepTimer : MonoBehaviour
{
[Header("Audio")]
[SerializeField] private AudioEvent footstepEvent;
[Header("Timing")]
[SerializeField] private float walkStepInterval = 0.5f;
[SerializeField] private float runStepInterval = 0.3f;
[SerializeField] private float minSpeedForFootsteps = 0.5f;
private CharacterController controller;
private float stepTimer = 0f;
private bool wasGroundedLastFrame = false;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
HandleFootsteps();
}
void HandleFootsteps()
{
// Check if moving and grounded
float speed = new Vector3(controller.velocity.x, 0, controller.velocity.z).magnitude;
bool isGrounded = controller.isGrounded;
if (isGrounded && speed > minSpeedForFootsteps)
{
// Determine interval based on speed
bool isRunning = speed > 5f; // Adjust threshold as needed
float interval = isRunning ? runStepInterval : walkStepInterval;
stepTimer -= Time.deltaTime;
if (stepTimer <= 0f)
{
PlayFootstep();
stepTimer = interval;
}
}
else
{
// Reset timer when not moving or airborne
stepTimer = 0f;
}
// Landing sound
if (isGrounded && !wasGroundedLastFrame && speed > 1f)
{
PlayFootstep();
stepTimer = (speed > 5f ? runStepInterval : walkStepInterval);
}
wasGroundedLastFrame = isGrounded;
}
void PlayFootstep()
{
if (footstepEvent != null)
{
footstepEvent.Post(gameObject, transform.position);
}
}
}
Tuning Tips:
Walk Interval: 0.4-0.6s
Run Interval: 0.25-0.35s
Min Speed: 0.3-0.8 units/sec
✅ Result: Automatic footsteps based on movement speed!
From the Pepper.SFX 3.1 guides (USER_GUIDE.md). Something wrong or unclear? Tell me.