Mirror Networking
In this guide we add ODIN to a very simple multiplayer game built with Mirror Networking.
Basic Multiplayer Topology
You might remember this graphic which is provided in the mirror documentation. It's a good starting point to show how ODIN fits into your multiplayer topology:
This diagram basically outlines the general topology of your multiplayer application.
- Clients connect to the server and send input to the server which handles the games state and the objects
- Depending on the authority input sent to the server is just controller input (server authority) or a combination of controller input and game object positions and spawn requests (client authority)
- The server sends events to the client to keep the client's game state in sync with the master game state on the server
Every client has an exact copy of the scene and its game objects kept on the server. Of course, there is always a slight delay depending on the latency (i.e. connection speed), but overall every client has the exact same state.
Basic Multiplayer Topology with ODIN
Of course, you could build voice into your client-server application yourself. Instead of sending packets with controller input or positions, you could also send audio packets. Good luck with that. Audio is extremely hard to get right, and there are many different input devices (microphones, headsets, etc.). So, it's easier to just use our technology to add voice communication to your application.
This is the same graphic as above, just with ODIN added to the topology. Let's dive into it:
- Your client/server structure and code is mostly untouched. You can add ODIN without changing anything on your core game.
- ODIN can also be removed very easily without touching your game logic and topology!
- ODIN integration is a pure client-side implementation, that is, you don't need to add anything to the server at all!
- Once your player has been connected to the server and the game's state is mirrored to the client, ODIN comes into play.
- The client joins a room by name and starts sending audio data captured from the input device.
- The ODIN server sends events to the client (as your game server does).
- Whenever a client is connected to the ODIN server, an
OnPeerJoined
event is sent to the server. AnOnMediaAdded
event is sent directly after that if the client is sending audio data (someone might be connected to ODIN but without sending any audio - i.e., just a listener). - The client attaches the audio source to the corresponding player's game object and removes it once the client disconnects from ODIN.
- That's it. The players running around the scene will act as an audio source, and it's up to you how to handle that voice stream in the game.
Let's dive into the actual integration.
Creating a simple multiplayer game
Mirror networking has a very good guide on how to get started. We don't want to replicate that excellent document here, so head over to Mirror networking and follow the steps. We stopped at Part 11 (as subsequent steps like weapons and switching are not relevant for this guide).
After you have followed the steps, you have created these things:
- A new Unity project with a scene that has the
NetworkManager
script attached to a game object. - A player prefab which is a basic capsule with a
PlayerScript
added and some basic movement. - You can start a
Client + Host
and run around the scene with WASD on your keyboard, and connecting another client will show both clients in the scene.
Adding ODIN
In this section, we'll add ODIN to our simple multiplayer example. Every user connected to the server will join the same
ODIN room once connected to the multiplayer server. Each player will act as an
AudioSource and will play the voice of the corresponding player in real-time. Audio will use
a spatialBlend
1.0
, and as such, the 3D position will be taken
into
account by Unity automatically.
Players' voices will come out of the direction they are on screen (or even behind you), and the volume will increase or decrease depending on how close these players are.
Let's get started!
First, we need to add the Unity ODIN SDK to our project.
Download SDK and install
Follow these steps to install the ODIN SDK into the project. For this guide, we recommend using the Unity Package as it's the simplest way of installing it.
Next step is to install the ODIN Unity SDK in the new project.
Important: Please remove files of previous versions before installing a new version. Unity does not remove old files from the installation and this causes issues sometimes. So just remove the package and install the latest one. 4Players ODIN also supports Apple Silicon. Check out our FAQ to learn more on prerequisites for running ODIN with Apple Silicon.
4Players ODIN supports Unity 2019.4 or any later version. The latest version is always available in our Github repository.
There are numerous ways to install ODIN into your project. We recommend using Package Manager.
Install via Asset Store
We are in the process of getting our package back into the Unity Asset Store. In the meantime, please use the Package Manager or the Unity Package to install the SDK.
Install via Unity Package
Please download the latest version of the ODIN Unity SDK as a .unitypackage
from the [Github releases page]
(https://github.com/4Players/odin-sdk-unity/releases). Just double-click the .unitypackage
to import it into your
current Unity editor project.
Package Manager
Using the Package Manager will ensure that all dependencies are set up correctly and that you will have the most up-to-date version of the SDK. In most cases, using the Package Manager is the way to go.
To open the Package Manager, navigate to Window
and then click Package Manager
in your Unity Editor menu bar.
Using our Git Repository
Click the + button in the upper left and select Add package from git URL
. Next, enter this URL and hit enter to import
the package:
https://github.com/4Players/odin-sdk-unity.git
Using a Tarball Archive
Click the + button in the upper left and select Add package from tarball
. Next, select the odin.tgz archive you've
downloaded from the Github releases page to import the package.
Add ODIN Handler Prefab
After installation, you'll find 4Players ODIN
in Packages
in your asset browser of Unity. Navigate to
Samples/Audio3D
and drag & drop the OdinManager 3D Variant
prefab into your scene. Click on the prefab and in the
inspector you'll see something like this:
The ODIN Manager will do the following for you:
- Activate the default Microphone and capture its input (if Auto-Start
Microphone
Listener is active in
Mic-AudioClip Settings
section, which is the default). - Handles all ODIN events and exposes delegates to react to those.
Please check out the ODIN Unity SDK Manual. You'll learn everything about how to use ODIN within the Unity editor and detailed descriptions of all properties of various ODIN components you can adjust in the Unity Inspector.
Set Access Token
Now that we have added ODIN manager to our scene, we need to set an access key. This is an important topic that we don't
want to dive into right now, but you'll have to take a minute to understand that later. Check out our guide on this
topic here: Understanding Access Keys. As we want to keep things simple, you can
generate an access key right from Unity. Click on the Manage Access in
the
inspector in the Client Settings
section in the ODIN Inspector. You'll see a dialog like this:
You can click on the Generate Access Key
to generate an access key for free. This will allow you to connect with up to
25 users. However, there is no support from us.
Mapping ODIN users to players
As you can see in the diagram above, it's important to match ODIN users to the players in the game so that you can attach the audio output to the correct player. If you are just using "God Voice" mode (i.e., all players have the same volume and direction, like in radio or video conferencing), then this is not required, but it's recommended.
In Mirror, every networked object has a NetworkIdentity. And
every NetworkIdentity has a
netId, which is an integer value that is unique for every object being part of the network. Every player with a
netIdPlayerScript
attached to it also has a
netId
to match ODIN users with networked player objects.Every Odin peer has UserData
With ODIN integrated, the PlayerScript.OnStartLocalPlayer
function looks like this:
Please note: UserData Room CustomUserDataJsonFormat CustomUserDataJsonFormatUpdateUserData
method provided in the ToUserData
and FromUserData
) representation.
Once that is done, we join the ODIN room netIdShooterSample
. We have also extended the CmdSetupPlayer
function and added
another parameter to send the user's
This is the new version of CmdSetupPlayer
after integrating ODIN:
That's it. Now every user will expose their ODIN identifier to the network and will also send that identifier to ODIN. Of course, many other things can be used as the identifier, such as the user ID from authentication or a random ID.
Understanding ODIN rooms
ODIN follows a couple of very simple rules:
- All users connected to the same room can hear everyone else (except if they are muted).
- A room is created when the first user connects and is deleted when the last one disconnects from it.
- One user can join multiple rooms simultaneously.
So, it's up to us to decide how we leverage that. You can have one large room for all players, or you can have a room for each team. And you can also have multiple rooms at the same time. Think about a game like Counter-Strike. There could be three rooms:
- In one room, every player is connected, and this room uses positional 3D audio.
- One room for Terrorists (i.e., all players are connected via virtual radio), which uses layered audio.
- One room for Counter-Terrorists (i.e., all players are connected via virtual radio), which uses layered audio.
In this usage scenario, players can talk to everyone else crystal clear, as they would in real life via radio or telephone. But players would also expose their voice inside the game at their 3D position. So, if you talk to others, an opponent who is close to you can listen to what you are talking about. This exposes your position and also gives opponents a chance to learn about your strategy.
In this example, we'll stick to one room using 3D audio. Every
AudioSource
in Unity has a property called spatialBlend
Sets how much this AudioSource is affected by 3D spatialisation calculations (attenuation, Doppler effect, etc.). 0.0 makes the sound full 2D, 1.0 makes it full 3D.
So, what we do with ODIN is attach a special ODIN-powered AudioSource
spatialBlend
to1.0
to make it full
3D
audio. If you want to have "God's voice" mode (i.e., no 3D positioning), then you would set that value to 0.0
. That's
it! With ODIN, this is all you have to do to have standard, equal-volume radio-type voice or 3D positional audio.
Attach ODIN voice to player objects
Let's recap what we have now:
- We have a simple multiplayer "game" that allows multiple players to connect and run around the scene.
- Every player connects to the
ShooterSample
room in ODIN and exposes a unique ID (in this case, the NetworkIdentity.netId) to the network and ODIN.
Once connected to ODIN, the ODIN server will send events whenever something of interest happens. If just connected, ODIN
will send a couple of OnPeerJoined OnMediaAdded
You can hook up those events via code or by linking them in the inspector in Unity. In this example, we do it via the Inspector.
So, we create a new Game Object in the scene called Odin Peer Manager
and create a new script OdinPeerManager
, which
is linked to the Odin Peer Manager
game object. This is the complete OdinPeerManager
script:
We are only interested in the OnMediaAddedOnMediaRemoved
events.
Those events are sent whenever a user has joined a room and started to send audio, or if that player stopped sending
audio.
So, we created two functions OnMediaAddedOnMediaRemoved
and
linked
those two events in the Odin Manager:
Please make sure that the events OnMediaAddedOnMediaRemoved
are
activated in the Event Listeners
section.
So, let's dive into that a bit. OnMediaAdded
The sender
is a Room
object in this case. So, we cast it to the Room
. In the MediaAddedEventArgs
object, a lot
of interesting info is sent from the server to the client. We are mostly interested in the UserData
object. We can
convert that generic object using the CustomUserDataJsonFormat.FromUserData
method to a CustomUserDataJsonFormat
object that has this structure:
Perhaps you remember that when connecting to the ODIN room (see above), we created a UserData
object in
OnStartLocalPlayer
:
As you can see, we used the seed
property to set a unique ID within the ODIN ecosystem (which is the netId
).
So, the seed
in this CustomUserDataJsonFormat
is a network identifier, which should be available within our current
scene. All we have to do is find all PlayerScript
objects in the scene and find that one with this netId
, and we
have matched the player to the ODIN user. This is done in the GetPlayerForOdinPeer
function.
Please note: The OnMediaAddedisLocalPlayer
, and if that's the case, we just return null
.
Now that we have matched the player to the ODIN peer, we just need to attach the special
ODIN-powered AudioSource
AttachOdinPlaybackToPlayer
function, which in turn uses
the AddPlaybackComponent
function of the ODIN SDK.We get back a PlaybackComponent spatialBlend
That's it!
Congratulations, you have added ODIN to a multiplayer game complete with 3D spatial audio. Multiple players connect to the server, and their voice will be coming from the position they are relative to the player's position, and the volume will change with the distance.