The VScript Book

Chapter 3.6: Sound and Synchronization

Sounds bring a map to life, but their real power in scripting comes from synchronization. Playing an announcement is easy; making a door open the exact moment the announcement finishes requires precise timing. This chapter covers how to play sounds and schedule events based on their duration.

Step 1: Precaching Sounds (Mandatory)

Before any sound can be played from a script, it must be loaded into memory when the map starts. This is called precaching, and it prevents the game from stuttering or crashing when it tries to play the sound for the first time. Precaching must be done inside the special `Precache()` hook function.

// This script is on a logic_script or any other entity.
// This function is automatically called by the engine during map load.

function Precache() {
    // Use self.PrecacheSoundScript for each sound file you intend to use.
    self.PrecacheSoundScript("Portal.room1_TickTock");
    self.PrecacheSoundScript("GLaDOS.critical_error01");
}

Critical: Attempting to play a sound that has not been precached will either fail silently. Always precache your sounds.

Step 2: Playing Sounds and Scheduling Events

Once precached, you can play a sound on any entity using `EmitSound()`. To synchronize events, you can then use `GetSoundDuration()` to find out how long the sound is and use that value as a delay for your next `EntFire` call.

// This function plays an announcement and triggers an event exactly when it ends.
function PlayAnnouncementAndTriggerEvent() {
    local player = GetPlayer();
    if (player == null) return;

    // 1. Play the sound on the player entity.
    player.EmitSound("Portal.room1_TickTock");
    
    // 2. Get the duration of that sound in seconds.
    // The second parameter is for context, but is usually an empty string.
    local duration = player.GetSoundDuration("Portal.room1_TickTock", "");
    
    printl("Sound is " + duration + " seconds long. Scheduling next event.");
    
    // 3. Fire the 'Trigger' input on the 'next_event' entity after the sound finishes.
    EntFire("next_event", "Trigger", "", duration);
}

This pattern is the foundation of creating professionally timed sequences, ensuring your visual effects, entity movements, and gameplay events are perfectly aligned with your audio cues.