Sounds with Zay-ES (Sim-eth-es)

Hey

How do you guys use Zay-ES and sounds together? I’m thinking (for a start) to use it exactly like the view, having the position updated, keeping track of sounds with an index. Add them as separate nodes, add a decay longer than the sound being played (or if a streaming sound, then just a decay that defines how long). Interested in hearing your go-to-strategies for sounds.

Kind regards

Yeah, pretty much. Sometimes it depends on the type of sound, too. For example, for player walking sounds, etc. I prefer to make that reactive to game state instead of an explicit “play sound now”… though the fact that it is making a walking sound when moving could be a component. (ie: the component is always there but the sound only triggers on movement.)

Especially for networked games, the more sounds you can make reactive the better… but it never works for everything.

Hi @asser_fahrenholz,
for my recent game I created an app state for each type of sound. Example: In my game, there are some entities on fire, for example a fireball. So I check for those entities and play the sound at the specific position.

Here is the app state I am talking about.

package de.gamedevbaden.crucified.appstates.sound;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioData;
import com.jme3.audio.AudioNode;
import com.jme3.scene.Node;
import com.simsilica.es.Entity;
import com.simsilica.es.EntityData;
import com.simsilica.es.EntityId;
import com.simsilica.es.EntitySet;
import de.gamedevbaden.crucified.appstates.EntityDataState;
import de.gamedevbaden.crucified.appstates.game.GameCommanderAppState;
import de.gamedevbaden.crucified.es.components.FireState;
import de.gamedevbaden.crucified.es.components.Transform;

import java.util.HashMap;

/**
 * This app state adds sounds for entities which are on fire for example a campfire or a torch.
 * Created by Domenic on 01.07.2017.
 */
public class FireSoundAppState extends AbstractAppState {

    private EntitySet entitiesOnFire;
    private HashMap<EntityId, AudioNode> audios;
    private AssetManager assetManager;
    private Node gameNode;

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        this.audios = new HashMap<>();
        this.assetManager = app.getAssetManager();
        this.gameNode = stateManager.getState(GameCommanderAppState.class).getMainWorldNode();

        EntityData entityData = stateManager.getState(EntityDataState.class).getEntityData();
        this.entitiesOnFire = entityData.getEntities(FireState.class, Transform.class);

        for (Entity entity : entitiesOnFire) {
            addAudio(entity);
        }

        super.initialize(stateManager, app);
    }

    @Override
    public void update(float tpf) {

        if (entitiesOnFire.applyChanges()) {

            for (Entity entity : entitiesOnFire.getAddedEntities()) {
                addAudio(entity);
            }

            for (Entity entity : entitiesOnFire.getChangedEntities()) {
                updateAudio(entity);
            }

            for (Entity entity : entitiesOnFire.getRemovedEntities()) {
                removeAudio(entity);
            }

        }

    }

    private void addAudio(Entity entity) {
        AudioNode audioNode = new AudioNode(assetManager, "Sounds/SoundEffects/Fire.WAV", AudioData.DataType.Stream);
        audioNode.setVolume(0.4f);
        audioNode.setPositional(true);
        audioNode.setLocalTranslation(entity.get(Transform.class).getTranslation());
        audioNode.setRefDistance(5f);
        audioNode.setLooping(true);
        gameNode.attachChild(audioNode);
        audios.put(entity.getId(), audioNode);
        checkStatus(entity, audioNode);
    }

    private void updateAudio(Entity entity) {
        AudioNode audioNode = audios.get(entity.getId());
        audioNode.setLocalTranslation(entity.get(Transform.class).getTranslation());
        checkStatus(entity, audioNode);
    }

    private void removeAudio(Entity entity) {
        AudioNode audioNode = audios.remove(entity.getId());
        audioNode.stop();
        audioNode.removeFromParent();
    }

    private void checkStatus(Entity entity, AudioNode audioNode) {
        if (entity.get(FireState.class).isOn()) {
            audioNode.play();
        } else {
            audioNode.pause();
        }
    }

    @Override
    public void cleanup() {
        for (Entity entity : entitiesOnFire) {
            removeAudio(entity);
        }
        this.entitiesOnFire.release();
        this.entitiesOnFire.clear();
        this.entitiesOnFire = null;
        super.cleanup();
    }
}

I hope that helps!

Best regards
Domenic

By the way, speaking of decay…

The yet-to-be-released next version of SiO2 now includes a default Decay component and DecaySystem.

Sounds good!

I see what you did there.

How do you, when doing Buffered sounds, pace them so as to not spam the user with the same sound in too little time? Is there some pacing technique or cooldown technique that works better than others?

Oh sorry. I can’t tell you that because I honestly have to less experience with sounds in general.