Minecraft Modding: Tips For Sounds

New Sound Registry System


Since 1.9.x you need to register your SoundEvent instances in the registry. 

Choonster provides a good example of using this registry. Basically you need to create unique sound events from the ResourceLocation for the sound, then create a method that is annotated with @SubscribeEvent (and registered to the event handling bus) that handles the RegistryEvent.register event and adds your custom sound events to the registry.


Organizing Your Sound Assets And sounds.json File


See my supplementary tutorial: Jabelar's Sound Asset Tutorial.

Adding Subtitles


In your sounds.json file, you can add subtitles (text that is displayed whenever a sound is played) using the "subtitle" key. The value for the subtitle should be the key to the .lang file and in your .lang file you should put the value and actual subtitle text.

Which Method Should I Use To Play My Sound?


Vanilla has lots of methods for playing sounds, and it’s unclear which to use at times. Check out the documentation on the sound methods for more understanding.

Making Positional Sounds Heard From Greater Distance


The getSoundVolume() method in living entities is misleadingly named. The max volume of the sound is actually defined by the sound file itself plus the value in the sounds.json file. Instead, the getSoundVolume() method is used to set the distance fade for the positional sounds the entity makes. If you want to make a sound heard from farther away, just set the number higher than 1.0F. For example, the vanilla EntityGhast sets the value to 10.0F.

Playing Background Music (Not At A Position)


Thanks to TheGreyGhost for this tip.

The EntityPlayer and World classes have methods for playing sounds, such as playSound(), playSoundAtEntity(), etc.  However, these are "positional" in that the volume will vary depending on the distance from the coordinates passed to the method.

Sometimes you may want to play a background sound or music that is not positional.  In that case the key (based on inspecting vanilla code such as the MovingSoundMinecartRiding constructor) is to set ISound.AttenuationType = NONE.

A method you can use that sets this is the PositionedSoundRecord.func_147673_a() method.  So the following example should play a background sound:

// invoke from location where the Minecraft instance is available
this.mc.getSoundHandler().playSound(PositionedSoundRecord
      .func_147673_a(new ResourceLocation("magiccrusade:luan_hong")));


Stopping A Sound That Is Playing


For stopping sounds there are the Minecraft.getMinecraft().getSoundHandler().isSoundPlaying(ISound), the stopSounds() and and the stopSound(ISound) methods available.

The stopSounds() will stop all sounds.

To stop a specific sound, you'd need to have the instance of the ISound to pass to the stopSound() method, which you probably won't have if it is a vanilla sound. But for your own sounds make sure you save the instance in a field and you can use these methods.

For vanilla sounds it is a bit more complicated. The sound handler has a private field for the sound manager which has a private field for playingSounds map from which you could iterate through and check the ISound returned to find the private ResourceLocation. Since these are all private scope, you need to use Java reflection techniques.

Thanks to Izzy Axel for figuring out the reflection. The following code will stop the main music:

SoundManager mng = ReflectionHelper.getPrivateValue(SoundHandler.class, 
      Minecraft.getMinecraft().getSoundHandler(), "sndManager");
Map playingSounds = ReflectionHelper.getPrivateValue(SoundManager.class, 
      mng, "playingSounds");
Iterator it = playingSounds.keySet().iterator();
while(it.hasNext())
{
   PositionedSound ps = (PositionedSound)playingSounds.get(it.next());
   ResourceLocation reloc = ReflectionHelper.getPrivateValue(PositionedSound.class, 
         ps, "field_147664_a");
   if("music.game".equals(reloc.getResourcePath()))
   {
      Minecraft.getMinecraft().getSoundHandler().stopSound(ps);
      System.out.println("stopped music");
      break;
   }
}

Playing A Sound Only Client-Side


Most of the "play sound" type functions are intended to be called on the server side which then sends a packet (usually to all clients) telling the receiving clients to play the sound.

However, sometimes you may want to play a sound only on the client-side. Like maybe a GUI sound. Or for example I had a case where I wanted certain sounds to change volume with distance but wanted them to be heard from much further away than the vanilla positional sound would play.

Thanks to enix for this tip.

When clients receive a packet telling it to play a sound, the clients play a convenient ISound resource using the method:
Minecraft.getMinecraft().getSoundHandler().playSound(ISound)

This means, that you can play sound on client directly by creating new ISound and passing it to sound handler.

Note that vanilla Minecraft has already has helper classes such as PositionedSound, ITickableSound and MovingSound. You can use them too, or extend/implement your own (needed in certain cases, for example repeating sound like riding in minecart, which has it's own MovingSoundMinecart implementation).

3 comments: