Advertisement

How do I network sounds?

Started by May 31, 2017 04:39 PM
6 comments, last by hplus0603 7 years, 5 months ago

Kind of a question I can't really find any information on. Maybe because it's obvious to other people? Mostly looking for personally the methods you use for this sort of thing. Do you just treat all events (sound and particles) as entities?

Right now I follow a Valve style networking where I just send inputs "just in time" for the server tick, which are then executed one by one. This works really well and I like it. The server then sends out a snapshop of the game world every other tick. The snapshot includes position information, etc. But it also includes state information such as entities HP, is the HP being hit currently. Basically a bunch of booleans. The client then uses this information of booleans to build what animation playing. Should I just piggyback on that system to play sounds? Or is that too much networking information? This is a tile based 2D game btw.

Or should each game state include a list of events that happen and the client just play them that way? Maybe with what tick so the client can try to play them in order.

It would work to send a message over the network that says "play this sound". It could be an integer in the range of 0 to the number of different sounds you have. I'm sure some sound effects could be deduced by the client implicitly without the need for net synchronization. For example of something dies, blows up, or other events the client already knows about. The client can just play sounds for those since it already knew those happened.
Advertisement
The only "sound" that needs to be networked is the player voice, if you implement voice chat.

In general, entities are derived into "accepting input," "simulating physically," "generating network updates (only on authority)" and "presenting to the user."
Sounds, like particle effects, explosion smoke, and all the rest, are typically just derived from the physics state simulated on each client, and presented to the local user.

Note that some kinds of effects do depend indirectly on networking: For example, certain first-person shooters play the gun fire effect right away, but only play the target-hit effect/animation/blood-spray after they get a hit confirmation from the server.
The way to think about that is that the targeted player is not locally controlled, and the being-hit effect belongs to the target, not the shooter.
enum Bool { True, False, FileNotFound };

Thanks for the info. And sorry for the confusion I did not mean actual raw sound data.

Can you elaborate on "are typically just derived from the physics state simulated on each client, and presented to the local user."

I guess that's the question I have, how do I derive the sound, effects from the physics state? So you wouldn't recommend having a list of, for example, enums of effects/events that happened for an entity. Instead I should derive the effects & events from data based on their physics state? Such as acceleration, booleans I set, etc?

how do I derive the sound, effects from the physics state?


How do you do it for a single player game?

Typically, you will run a local simulation for each remote entity, which will let you do things like move wheels up when they drive over rocks, move arms out of the way of walls, and so forth.
This simulation typically generates footprints, dust trails, bump sounds, and so forth.
This simulation is "cosmetic" and not in sync over the network, other than very likely to look the same on all machines because they simulate the same entity, generally based on the same input position and same input commands.
enum Bool { True, False, FileNotFound };

I guess the issue is that if you network a game purely by sending the new states, you don't know what caused the state change, so don't have enough information to play a sound accordingly.

e.g. Frame 10, server says "you have 10 hitpoints". Frame 11, server says "you have 3 hit points". Do I play the "hit by sword" sound? Or the "fell into a pit" sound? No idea. What if I actually got hit by 2 people in that time-span? Or hit by 3 and healed by 1? You can't deduce it from state changes alone so there needs to be communication of the event that causes the change.

In a single player game, you might have pseudo-code like this:


if attack_successful:
    victim.hitpoints -= 10
    play_sound(SOUND_HIT_BY_WEAPON)

You can decompose that into a combination of an event (attack successful), a state-change (the hitpoints), and a side-effect (sound playing).

There are 2 reasonable ways to network this:

  1. Changes on clients happen via events. You send a message to the client like [AttackSuccessful, 10] and the client uses that data to apply the direct effects (reducing health by 10) and to handle side-effects (both visual, such as particles, and audible, such as sounds).
  2. You send state changes and events separately. The client gets one message saying [NewHitpoints, 3], and another message immediately after saying [HitByWeapon]. Again, the client handles the side-effects of the event locally.

I don't suggest micromanaging side-effects by having the server say, "Play this sound" or "Show this animation". You will want it at a more abstract level so that clients can vary their response to whatever event you send, and so you can avoid having to make server changes for cosmetic alterations.

Advertisement

Thanks Kylotan. That actually got at the heart of my question and you definitely helped me even understand my problem better. Like you said the problem is I'm only sending absolute state.

Would this work: Have a global event list on the server for each tick. I'm not 100% sure what data it will have, but something like src, target and an event enum, and what tick it happened on. When sending gamestate, I need to send the user the global events that happens for all the ticks since the last update they received (so if we update every other tick, it will contain the current executed tick, and the one before). From there the client can decide if they want to delay any of the events based on the tick ID or something. Does that sound good?

Should I also use this for animation of sprites? They also fall under the non-absolute kind of thing. Well at least some of them. Walking is kind of an on or off kind of thing. But being hit with a sword may only happen on 1 tick as far as when they were hit.

Yes, a queue of events that "matter" would be useful, if you don't run the simulation of remote entities on each of the clients.

Should I also use this for animation of sprites?


Depends on what you mean by "animation of sprites."
You probably don't want to be sending "play frame 33."
If your animations are based on state ("running" vs "swimming") then the client display logic can derive which animation to play without specific events.
If you have "actions" that are not states ("shoot" or "pick up" or whatever,) then those actions may need events to be properly presented to remote users.
Proper presentation would then include "play this animation."
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement