I recently added support for local multiplayer to our upcoming zombie survival game, Good N Dead. It was a lot easier than I expected! I moved the main camera, the HUD, and the audio listener into the prefabs we use for playable characters and did some fiddling with Rewired to associate a controller with the right player. Within about a week, we went from this:
Everything seemed to be working except for this nagging message that started appearing in the Unity console when we would test multiplayer:
Which I ignored for a while, since the game seemed to be running correctly, and since the message itself looked to be informational. It wasn't until I did a play test with my teammates that I realized what was going on. You can see (or hear) the issue in this clip we tweeted out recently:
You'll notice that certain sounds go entirely unheard. For example, when the players on the top and bottom-left fire their guns, there is no sound. However, if you watch the player on the bottom-right, you can hear everything they're doing.
The (now obvious) reason for this is that Unity only supports having one
AudioListener in a scene, which I later found to be explicitly stated in their documentation:
You should attach the Audio Listener to either the Main Camera or to the GameObject that represents the player. Try both to find what suits your game best.
Each scene can only have one Audio Listener.
$@&#! My plan to throw all these components into a player character prefab and call it a day was foiled. It became clear to me after some digging that this was unlikely to be changed any time soon and that a custom solution would be necessary.
Looking online for suggestions on how to fix this problem, I was surprised to find that there wasn't a canonical resource out there, only a sparse collection of forum threads and questions on StackOverflow. Further, it doesn't seem like there is any single solution that will suit all game types, which may be why Unity has chosen to ignore it. With that said, there are definitely solutions to this problem, which I'll attempt to summarize below:
- Give only one player an
AudioListenerand see if it's actually a problem in your game. If your players will always be close to each other on the screen and experience the same sounds, then you might not need to do anything at all. This isn't as bad of a solution as it may seem, depending on the kind of game you're creating. I could see this working for lots of fighters, top-down co-ops, side-scroller co-ops, etc.
- Create a separate game object to hold the
AudioListenerand keep it at the average position of your players (e.g., if you have 3 players close together and 1 far away, the listener will be near your cluster of 3). You may be able to further refine this by having players pull the listener object to themselves before initiating their own sounds.
- Make all your sounds two dimensional by updating every
AudioSource's spatial blend to be
2D. Two dimensional audio in Unity will always be heard regardless of distance from the
AudioListener. The obvious downside to this is that you lose the ability to have spatial audio effects like Doppler, directionality, etc. without handling this yourself, and your audio will be mixed together likely resulting in a cacophony of sounds. This is avoidable by adding custom logic at the mixer level to only play sounds by priority or that are within some minimum distance to any of the players.
- Don't use Unity's audio system (directly) and instead build a custom solution with virtual audio sources and listeners, handling the mixing and output yourself. Depending on how advanced you get with option #3, you may want to consider this one instead.
What I did
I initially went with my own option, option #5, and tried to get around this limitation by having a script that rapidly round-robin added and removed the
AudioListener component to and from each player in the scene, but the result was about as successful as you might imagine.
Option #1 was proven not to work in our game, so that was out. Option #2 wouldn't have worked well for our game, since players can be 10s or 100s of meters apart if one is collecting resources while another is back at the base building. It might have been workable if I tried this Unity forum user's suggestion, though. Option #3 seemed like a very invasive change that would ultimately have been less than ideal, so I skipped it.
That left option #4, building our own audio system with custom
AudioListener components to use in place of the built-in ones provided by Unity. Since this would have been a rather involved change as well and we have limited engineering resources (i.e., just me) I ended up solving this problem like I solve most others in Unity: by throwing money at it and splurging on the Multi Listener Pooling Audio System (MLPAS) asset by Almenara Games. There are a few others out there as well, but I haven't tried them.
MLPAS is a replacement of Unity's audio system that supports having up to 4 audio listeners in the scene at a time which is exactly what we needed. Unfortunately, since it is a replacement, adding it to your project requires replacing any
AudioSource components with their
MultiAudioSource counterparts. Similarly, you attach a
MultiAudioListener component to each player instead of the built-in
But once I did that, bam! An audio listener in each prefab and working local multiplayer audio like I originally expected.
Any comments, questions, or concerns? Let me know at firstname.lastname@example.org