DEV505 / CODE RECIPE
Switch tools and overlap a hammer tap.
Reuse one main Workshop play and a second tap play, with typed selection and ordered signal responses.
Set up the scene
Section titled “Set up the scene”Start with Your first sound if you have not set up a sound scene before. Use a separate test scene with one enabled Sound Manager, generated Sound Keys, and one Audio Listener near the sound object. Add Workshop from 06-Workshop to the manager's Direct events list; these are the supplied .soundgraph assets under Assets/DEV505/SoundManager/Samples.
Save the complete code below as WorkshopSounds.cs in Assets/Scripts. Wait for Unity to compile it, create an empty GameObject, and drag the script onto it. The filename must match the class name. Test one recipe component at a time so another demo controller does not compete for the same events.
Optionally assign a nearby Transform to Hammer Point for extra taps. Otherwise they use the component’s position. Set Pace between 0 and 1.
In Play Mode, wait until the Sound Manager is ready. To run a listed test action, open the component's three-dot menu in the Inspector and choose that action. These are the script's ContextMenu methods. Your own code or a Unity UI Button can call its public methods too. Inspector edits made during Play Mode are temporary.
Try the component
Section titled “Try the component”Choose Work with handsaw, then One hammer impact. The saw should keep running under the impact. Choose Work with drill to switch the main tool, then add another impact. Choose Take a break to stop both sets of tool voices. Disable the component to end the waiting sessions too.
Work with hammer selects Hammer but does not automatically repeat hits in this smaller example. Use One hammer impact for each hit. In a game, call Strike() at the animation’s contact frame. The full demo supplies its automatic hammer rhythm through animation logic.
Selection before action
Section titled “Selection before action”Work(int selectedTool) uses 0 for Hammer, 1 for Handsaw, and 2 for Drill. It creates a main session if needed, sets the Int parameter, then sends Work. That order ensures the branch reads the new tool.
The graph’s Work Sequence stops its existing tool voices, starts the selected tool path, and plays a latch click in order. These are synchronous commands. A Sequence does not wait for each sound to finish.
Two handles for overlap
Section titled “Two handles for overlap”For Hammer, Strike goes to the main handle. For Saw or Drill, Strike goes to a separate Workshop play initialized with Tool 0. Each play has its own graph state, so stopping or switching the main tool does not reuse the tap’s voice. The graph permits two Workshop instances.
TakeBreak sends the graph’s Pause signal to both handles. Keep Alive leaves silent sessions available for the next Work or Strike. This is not the same as SetPaused, which freezes time for later continuation. OnDisable stops both sessions by owner and releases that owner.
Do not stack this recipe alongside the full demo controller on the same manager: both would compete for the event’s two-instance allowance. Use the supplied scene to explore the visual demo and a separate scene to try this small integration.
Complete component
Section titled “Complete component”using DEV505.SoundManager;using UnityEngine;
public sealed class WorkshopSounds : MonoBehaviour{ [SerializeField, Range(0, 1)] private float pace = 0.5f; [SerializeField] private Transform hammerPoint = null; private ISoundService service; private SoundOwnerToken owner; private SoundHandle session; private SoundHandle taps; private int tool;
private bool Ready() { if (!Application.isPlaying || !isActiveAndEnabled) return false; var active = SoundServices.Current; if (active == null || !active.Ready) return false; if (!ReferenceEquals(service, active)) { session = default; taps = default; service = active; owner = service.OwnerOf(this); } return true; }
private SoundHandle StartSession(Transform point, int selectedTool) { var context = SoundPlayContext.Following(point, owner) .With(SoundParameters.Workshop.Tool, selectedTool) .With(SoundParameters.Workshop.Pace, pace); var result = SoundBus.Play(Sounds.Workshop, context); if (!result.Accepted) Debug.LogWarning($"Workshop could not start: {result.Status}", this); return result.Handle; }
public void Work(int selectedTool) { if (!Ready()) return; tool = Mathf.Clamp(selectedTool, 0, 2); if (!session.IsAlive()) session = StartSession(transform, tool); if (!session.IsAlive()) return; SoundBus.SetParameter(session, SoundParameters.Workshop.Tool, SoundValue.Int(tool)); SoundBus.SendSignal(session, "Work"); }
[ContextMenu("Work with hammer")] public void Hammer() => Work(0); [ContextMenu("Work with handsaw")] public void Saw() => Work(1); [ContextMenu("Work with drill")] public void Drill() => Work(2);
[ContextMenu("One hammer impact")] public void Strike() { if (!Ready()) return; if (tool == 0) { if (!session.IsAlive()) session = StartSession(transform, 0); SoundBus.SendSignal(session, "Strike"); } else { if (!taps.IsAlive()) taps = StartSession(hammerPoint ? hammerPoint : transform, 0); SoundBus.SendSignal(taps, "Strike"); } // In a game, call Strike at the animation's contact frame. }
[ContextMenu("Take a break")] public void TakeBreak() { SoundBus.SendSignal(session, "Pause"); SoundBus.SendSignal(taps, "Pause"); // Keep Alive leaves both sessions available for the next Work or Strike. }
private void Update() { if (session.IsAlive()) SoundBus.SetFloat(session, SoundParameters.Workshop.Pace, pace); }
private void OnDisable() { if (ReferenceEquals(service, SoundServices.Current) && owner.IsValid) { service.StopOwned(owner, 0); service.ReleaseOwner(owner); } session = taps = default; owner = default; service = null; }}Keep exploring
Section titled “Keep exploring”Read the demo walkthrough for the graph, parameters, controls, and Unity screenshots. The related API reference explains the calls used here. Return to all twelve recipes to choose another interaction.
This page imports the checked C# file directly. It was compiled against the documented Unity revision; its runtime checks and source hash are recorded in the documentation repository’s validation files.