Skip to content
Back to skills

Tools Unity Wwise

ASecurity

Wwise audio middleware integration for Unity including events, banks, RTPC, spatial audio, and callback handling.

  • 17 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aigoperformance

Works with

  • cli

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add IdoCohen560/claude-unity-game-studio --skill tools-unity-wwise --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Tools Unity Wwise?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Tools Unity Wwise
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/idocohen560-tools-unity-wwise/badge)](https://www.skillsdirectory.com/skills/idocohen560-tools-unity-wwise)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: tools-unity-wwise
description: Wwise audio middleware integration for Unity including events, banks, RTPC, spatial audio, and callback handling.
---

# Wwise Unity Integration

## Overview

Wwise is professional audio middleware providing advanced sound design capabilities. This skill covers Unity integration patterns for events, sound banks, real-time parameters, and spatial audio.

## When to Use

- Playing sound effects and music
- Dynamic audio parameter control
- 3D positional audio
- Audio state management
- Bank loading/unloading

## Core Concepts

### Sound Bank Loading

```csharp
public class AudioBankManager : MonoBehaviour
{
    [SerializeField] private AK.Wwise.Bank _initBank;
    [SerializeField] private AK.Wwise.Bank[] _coreBanks;
    
    private readonly HashSet<uint> _loadedBanks = new();
    
    public async UniTask InitializeAudio()
    {
        // Load Init bank first (required)
        await LoadBankAsync(_initBank);
        
        // Load core banks
        foreach (var bank in _coreBanks)
        {
            await LoadBankAsync(bank);
        }
    }
    
    public async UniTask LoadBankAsync(AK.Wwise.Bank bank)
    {
        if (bank == null || _loadedBanks.Contains(bank.Id))
            return;
        
        var tcs = new UniTaskCompletionSource();
        
        bank.Load(decodeBank: false, saveDecodedBank: false);
        
        // Wait for bank to be ready
        AKRESULT result = AkSoundEngine.LoadBank(
            bank.Name,
            out uint bankId
        );
        
        if (result == AKRESULT.AK_Success)
        {
            _loadedBanks.Add(bankId);
            Debug.Log($"Loaded bank: {bank.Name}");
        }
        else
        {
            Debug.LogError($"Failed to load bank {bank.Name}: {result}");
        }
    }
    
    public void UnloadBank(AK.Wwise.Bank bank)
    {
        if (bank == null) return;
        
        bank.Unload();
        _loadedBanks.Remove(bank.Id);
    }
    
    private void OnDestroy()
    {
        // Unload all banks on shutdown
        foreach (var bank in _coreBanks)
        {
            UnloadBank(bank);
        }
    }
}
```

### Async Bank Loading with Callback

```csharp
public class AsyncBankLoader
{
    public static async UniTask<bool> LoadBankAsync(string bankName)
    {
        var tcs = new UniTaskCompletionSource<bool>();
        
        AkSoundEngine.LoadBank(
            bankName,
            (uint)AkCallbackType.AK_BankLoaded,
            (in_cookie, in_type, in_info) =>
            {
                var loadInfo = (AkBankCallbackInfo)in_info;
                tcs.TrySetResult(loadInfo.loadResult == AKRESULT.AK_Success);
            },
            null,
            out uint bankId
        );
        
        return await tcs.Task;
    }
}
```

## Event Playback

### Basic Event Posting

```csharp
public class AudioEventPlayer : MonoBehaviour
{
    [SerializeField] private AK.Wwise.Event _footstepEvent;
    [SerializeField] private AK.Wwise.Event _attackEvent;
    
    private uint _playingId;
    
    public void PlayFootstep()
    {
        _footstepEvent.Post(gameObject);
    }
    
    public void PlayAttack()
    {
        // Store playing ID for potential stop
        _playingId = _attackEvent.Post(gameObject);
    }
    
    public void StopAttack()
    {
        if (_playingId != 0)
        {
            AkSoundEngine.StopPlayingID(_playingId);
            _playingId = 0;
        }
    }
    
    public void StopAll()
    {
        AkSoundEngine.StopAll(gameObject);
    }
}
```

### Event with Callback

```csharp
public class AudioEventWithCallback : MonoBehaviour
{
    [SerializeField] private AK.Wwise.Event _voiceEvent;
    
    public event Action OnVoiceComplete;
    
    public void PlayVoice()
    {
        _voiceEvent.Post(
            gameObject,
            (uint)(AkCallbackType.AK_EndOfEvent | AkCallbackType.AK_Marker),
            OnAudioCallback
        );
    }
    
    private void OnAudioCallback(
        object in_cookie,
        AkCallbackType in_type,
        AkCallbackInfo in_info)
    {
        switch (in_type)
        {
            case AkCallbackType.AK_EndOfEvent:
                OnVoiceComplete?.Invoke();
                break;
                
            case AkCallbackType.AK_Marker:
                var markerInfo = (AkMarkerCallbackInfo)in_info;
                HandleMarker(markerInfo.strLabel);
                break;
        }
    }
    
    private void HandleMarker(string label)
    {
        Debug.Log($"Audio marker: {label}");
        // Sync animation, VFX, etc.
    }
}
```

### Async Event Playback

```csharp
public static class WwiseAsyncExtensions
{
    public static async UniTask PlayAndWaitAsync(
        this AK.Wwise.Event wwiseEvent,
        GameObject gameObject,
        CancellationToken ct = default)
    {
        var tcs = new UniTaskCompletionSource();
        
        uint playingId = wwiseEvent.Post(
            gameObject,
            (uint)AkCallbackType.AK_EndOfEvent,
            (cookie, type, info) => tcs.TrySetResult()
        );
        
        if (playingId == 0)
        {
            Debug.LogWarning($"Failed to post event: {wwiseEvent.Name}");
            return;
        }
        
        // Handle cancellation
        using (ct.Register(() =>
        {
            AkSoundEngine.StopPlayingID(playingId);
            tcs.TrySetCanceled();
        }))
        {
            await tcs.Task;
        }
    }
}

// Usage
await _chargeSound.PlayAndWaitAsync(gameObject, _cts.Token);
```

## RTPC (Real-Time Parameter Control)

### Setting RTPC Values

```csharp
public class AudioParameterController : MonoBehaviour
{
    [SerializeField] private AK.Wwise.RTPC _healthRTPC;
    [SerializeField] private AK.Wwise.RTPC _speedRTPC;
    [SerializeField] private AK.Wwise.RTPC _tensionRTPC;
    
    public void SetHealth(float normalizedHealth)
    {
        // Value 0-100 for health percentage
        _healthRTPC.SetGlobalValue(normalizedHealth * 100f);
    }
    
    public void SetSpeed(float speed)
    {
        // Set on specific game object for 3D sounds
        _speedRTPC.SetValue(gameObject, speed);
    }
    
    public void SetTension(float tension, float interpolationMs = 100f)
    {
        // Smooth interpolation
        AkSoundEngine.SetRTPCValue(
            _tensionRTPC.Id,
            tension,
            gameObject,
            (int)interpolationMs
        );
    }
}
```

### RTPC with Animation

```csharp
public class AudioAnimationSync : MonoBehaviour
{
    [SerializeField] private AK.Wwise.RTPC _animationProgressRTPC;
    
    private Animator _animator;
    
    private void Update()
    {
        var stateInfo = _animator.GetCurrentAnimatorStateInfo(0);
        float progress = stateInfo.normalizedTime % 1f;
        
        _animationProgressRTPC.SetValue(gameObject, progress * 100f);
    }
}
```

## State and Switch Management

### Setting States

```csharp
public class AudioStateManager : MonoBehaviour
{
    [SerializeField] private AK.Wwise.State _combatState;
    [SerializeField] private AK.Wwise.State _explorationState;
    [SerializeField] private AK.Wwise.State _menuState;
    
    public void EnterCombat()
    {
        _combatState.SetValue();
    }
    
    public void ExitCombat()
    {
        _explorationState.SetValue();
    }
    
    public void EnterMenu()
    {
        _menuState.SetValue();
    }
}
```

### Setting Switches

```csharp
public class AudioSwitchController : MonoBehaviour
{
    [SerializeField] private AK.Wwise.Switch _surfaceSwitch;
    
    // Different surface types
    [SerializeField] private AK.Wwise.Switch _concreteSurface;
    [SerializeField] private AK.Wwise.Switch _grassSurface;
    [SerializeField] private AK.Wwise.Switch _waterSurface;
    
    public void SetSurface(SurfaceType surface)
    {
        switch (surface)
        {
            case SurfaceType.Concrete:
                _concreteSurface.SetValue(gameObject);
                break;
            case SurfaceType.Grass:
                _grassSurface.SetValue(gameObject);
                break;
            case SurfaceType.Water:
                _waterSurface.SetValue(gameObject);
                break;
        }
    }
}
```

## Spatial Audio

### AkGameObj Setup

```csharp
public class SpatialAudioSetup : MonoBehaviour
{
    private AkGameObj _akGameObj;
    
    private void Awake()
    {
        // Ensure AkGameObj component exists
        _akGameObj = GetComponent<AkGameObj>();
        if (_akGameObj == null)
        {
            _akGameObj = gameObject.AddComponent<AkGameObj>();
        }
        
        // Register with Wwise
        AkSoundEngine.RegisterGameObj(gameObject, gameObject.name);
    }
    
    private void OnDestroy()
    {
        AkSoundEngine.UnregisterGameObj(gameObject);
    }
}
```

### Listener Configuration

```csharp
public class AudioListenerSetup : MonoBehaviour
{
    [SerializeField] private bool _isDefaultListener = true;
    
    private void Awake()
    {
        // Register as game object
        AkSoundEngine.RegisterGameObj(gameObject, "AudioListener");
        
        if (_isDefaultListener)
        {
            // Set as default listener
            ulong[] listeners = { AkSoundEngine.GetAkGameObjectID(gameObject) };
            AkSoundEngine.SetDefaultListeners(listeners, 1);
        }
    }
    
    private void OnDestroy()
    {
        AkSoundEngine.UnregisterGameObj(gameObject);
    }
}
```

### Room and Portal System

```csharp
public class AudioRoom : MonoBehaviour
{
    [SerializeField] private AkRoom _akRoom;
    [SerializeField] private float _reverbLevel = 1f;
    [SerializeField] private float _transmissionLoss = 0.5f;
    
    private void Awake()
    {
        _akRoom = GetComponent<AkRoom>();
        if (_akRoom == null)
        {
            _akRoom = gameObject.AddComponent<AkRoom>();
        }
        
        // Configure room
        _akRoom.reverbLevel = _reverbLevel;
        _akRoom.transmissionLoss = _transmissionLoss;
    }
}
```

## Volume Control

### Volume Settings

```csharp
public class VolumeController : MonoBehaviour
{
    [SerializeField] private AK.Wwise.RTPC _masterVolumeRTPC;
    [SerializeField] private AK.Wwise.RTPC _musicVolumeRTPC;
    [SerializeField] private AK.Wwise.RTPC _sfxVolumeRTPC;
    [SerializeField] private AK.Wwise.RTPC _voiceVolumeRTPC;
    
    public void SetMasterVolume(float volume)
    {
        _masterVolumeRTPC.SetGlobalValue(VolumeToDb(volume));
    }
    
    public void SetMusicVolume(float volume)
    {
        _musicVolumeRTPC.SetGlobalValue(VolumeToDb(volume));
    }
    
    public void SetSFXVolume(float volume)
    {
        _sfxVolumeRTPC.SetGlobalValue(VolumeToDb(volume));
    }
    
    public void SetVoiceVolume(float volume)
    {
        _voiceVolumeRTPC.SetGlobalValue(VolumeToDb(volume));
    }
    
    // Convert linear 0-1 to dB scale
    private float VolumeToDb(float linear)
    {
        if (linear <= 0.0001f) return -96f; // Silence
        return 20f * Mathf.Log10(linear);
    }
    
    public void Mute()
    {
        AkSoundEngine.SetRTPCValue("MasterVolume", -96f);
    }
    
    public void Unmute()
    {
        // Restore to saved value
        SetMasterVolume(PlayerPrefs.GetFloat("MasterVolume", 1f));
    }
}
```

## Animation Event Integration

### Wwise Animation Event Callback

```csharp
public class WwiseAnimationEventHandler : MonoBehaviour
{
    [SerializeField] private AK.Wwise.Event _footstepEvent;
    [SerializeField] private AK.Wwise.Event _whooshEvent;
    [SerializeField] private AK.Wwise.Event _impactEvent;
    
    // Called from animation event
    public void PlayFootstep()
    {
        _footstepEvent.Post(gameObject);
    }
    
    // Called from animation event with string parameter
    public void PlaySoundByName(string eventName)
    {
        AkSoundEngine.PostEvent(eventName, gameObject);
    }
    
    // Called from animation event
    public void PlayWhoosh()
    {
        _whooshEvent.Post(gameObject);
    }
    
    // Called from animation event
    public void PlayImpact()
    {
        _impactEvent.Post(gameObject);
    }
}
```

## Performance Patterns

### Pooled Audio Sources

```csharp
public class PooledAudioEmitter : MonoBehaviour, IPoolable
{
    private uint _currentPlayingId;
    
    public void OnSpawn()
    {
        AkSoundEngine.RegisterGameObj(gameObject);
    }
    
    public void OnDespawn()
    {
        if (_currentPlayingId != 0)
        {
            AkSoundEngine.StopPlayingID(_currentPlayingId);
            _currentPlayingId = 0;
        }
        AkSoundEngine.UnregisterGameObj(gameObject);
    }
    
    public void PlayEvent(AK.Wwise.Event wwiseEvent)
    {
        _currentPlayingId = wwiseEvent.Post(gameObject);
    }
}
```

### Batch Event Posting

```csharp
public class AudioBatcher
{
    private readonly List<(AK.Wwise.Event evt, GameObject go)> _pendingEvents = new();
    private const int MaxEventsPerFrame = 10;
    
    public void QueueEvent(AK.Wwise.Event evt, GameObject go)
    {
        _pendingEvents.Add((evt, go));
    }
    
    public void ProcessBatch()
    {
        int processed = 0;
        
        while (_pendingEvents.Count > 0 && processed < MaxEventsPerFrame)
        {
            var (evt, go) = _pendingEvents[0];
            _pendingEvents.RemoveAt(0);
            
            if (go != null)
            {
                evt.Post(go);
            }
            
            processed++;
        }
    }
}
```

## Error Handling

### Safe Event Posting

```csharp
public static class WwiseSafeExtensions
{
    public static uint PostSafe(this AK.Wwise.Event evt, GameObject go)
    {
        if (evt == null || !evt.IsValid())
        {
            Debug.LogWarning("Attempted to post invalid Wwise event");
            return 0;
        }
        
        if (go == null)
        {
            Debug.LogWarning($"Attempted to post event {evt.Name} on null GameObject");
            return 0;
        }
        
        return evt.Post(go);
    }
    
    public static void SetValueSafe(this AK.Wwise.RTPC rtpc, GameObject go, float value)
    {
        if (rtpc == null || !rtpc.IsValid())
        {
            Debug.LogWarning("Attempted to set invalid RTPC");
            return;
        }
        
        rtpc.SetValue(go, value);
    }
}
```

## Best Practices

1. **Load Init bank first** - Required for Wwise to function
2. **Register game objects** - Before posting events
3. **Unregister on destroy** - Prevent memory leaks
4. **Use callbacks for sync** - Animation markers, end events
5. **Pool audio emitters** - Reduce registration overhead
6. **Batch event posts** - Limit per-frame calls
7. **Use RTPC interpolation** - Smooth parameter changes
8. **Manage bank memory** - Load/unload per scene
9. **Set switches before events** - Order matters
10. **Profile audio CPU** - Wwise has profiler tools

## Troubleshooting

| Issue | Solution |
|-------|----------|
| No sound | Check bank loaded, game object registered |
| Event not found | Verify bank contains event, bank is loaded |
| RTPC not working | Check RTPC name matches Wwise project |
| 3D audio wrong | Verify listener setup, position updates |
| Memory issues | Unload unused banks |
| Clicks/pops | Use fade times, check sample rates |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.