{"id":23134907,"url":"https://github.com/4players/odin-unity-demo","last_synced_at":"2025-07-01T03:05:13.380Z","repository":{"id":37783988,"uuid":"421020096","full_name":"4Players/odin-unity-demo","owner":"4Players","description":"Unity multiplayer sample project for 4Players ODIN","archived":false,"fork":false,"pushed_at":"2025-05-13T17:09:40.000Z","size":56128,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-01T03:04:03.335Z","etag":null,"topics":["3d-audio","demo","example","multiplayer","network","odin","photon","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/4Players.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-10-25T12:45:36.000Z","updated_at":"2025-02-04T17:30:11.000Z","dependencies_parsed_at":"2025-07-01T03:04:06.282Z","dependency_job_id":null,"html_url":"https://github.com/4Players/odin-unity-demo","commit_stats":null,"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"purl":"pkg:github/4Players/odin-unity-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-unity-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-unity-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-unity-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-unity-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/4Players","download_url":"https://codeload.github.com/4Players/odin-unity-demo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-unity-demo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262887159,"owners_count":23379766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d-audio","demo","example","multiplayer","network","odin","photon","unity"],"created_at":"2024-12-17T12:13:38.910Z","updated_at":"2025-07-01T03:05:13.333Z","avatar_url":"https://github.com/4Players.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ODIN Unity Demo Project\n\nPlease check out https://www.4players.io/odin/ for more info on ODIN.\n\nAdditional info on this demo project can be found here: https://www.4players.io/odin/guides/unity/pun-sample/.\n\n## Download the Binary\n\nYou can download the binaries here: https://github.com/4Players/odin-unity-demo/releases/latest\n\n## ⚠ Downloading the ZIP ⚠\n\n**Please note**: This repository uses LFS. You need to clone this repo with LFS enabled. **Downloading the ZIP file via Githubs Download ZIP functionality does not work!**\n\nTo enable git lfs, enter `git lfs install` in your git bash in your local repository.\n\n# ODIN Demo: Extended Audio System and Multiplayer with Photon PUN 2\nIn this guide we’ll walk you through the basic concepts of integrating ODIN into a multiplayer game. This demo will use\nthe Photon PUN 2 multiplayer framework, but ODIN can be integrated into your game using any multiplayer solution or\neven without multiplayer. The ODIN-Demo itself also allows us to easily switch out Photon for another framework.\n\nIf you are unsure why you should use ODIN for that, learn more about our features and what makes us special in our [introduction](https://www.4players.io/odin/introduction/).\n\n## Project Structure\n\nThe ODIN-Demo project's scripts are split up into the categories:\n- **Audio**: Scripts in here handle the custom Audio System behaviour like directional audio and occlusion effects. \n- **ODIN:** Handles anything related to core ODIN-features, without external dependencies. If you'd like to use a multiplayer framework other than Photon, you can safely reuse the files contained in this assembly.\n- **Photon:** Anything Photon specific is contained in here, like joining Photon Rooms or synchronizing Player Position.\n- **GameLogic:** Anything else required for the demo, like the player's movement or view state (1st-person or 3rd-person).\n\nYou can find demo settings in the `Assets \u003e ODIN-Demo \u003e Settings` directory, e.g. settings for the occlusion effects, ODIN room names, Push-To-Talk settings and Input definitions. Any prefabs used in the demo can be found in the `Assets \u003e ODIN-Demo \u003e Prefabs` directory, with the player prefab being located in the `Resources`.\n\nThe demo scene's hierarchy contains three root game objects used for categorizing behaviours:\n- **Environment:** Contains all visible objects or lights that contain the scene's visuals.\n- **Gamelogic:** Behaviours like the `PhotonPlayerSpawner` or the ODIN room join logic are placed here.\n- **UI:** The root object for in-game UI, like the settings menu or the radio room's active user display.\n\n## ODIN terms and behaviours\n\nThis is a short introduction into the most important ODIN terms - for more in-depth information [please take a look at the ODIN documentation](https://www.4players.io/odin/introduction/).\n\n### Rooms, Peers and Media\n\nEvery client connects to an ODIN server, authenticates with an access token and joins a room. Once the client has joined a room, they are a peer inside the ODIN room. Every peer can add media to that room, linked to a physical device like a microphone. Clients can join multiple rooms at the same time and can add multiple media streams at the same time.\n\nTo find more information on the basic ODIN topology, [please take a look at the Basic Concepts documentation](https://www.4players.io/odin/introduction/structure/).\n\n### OdinHandler\n\nThe OdinHandler script is a singleton behaviour, wrapping the functionality of the native ODIN SDK for use in Unity. You can access the script via `OdinHandler.Instance`. \n\nThe most important use-cases are the `OdinHandler.Instance.JoinRoom` method for joining ODIN rooms and the events for listening for ODIN events, like `OnRoomJoin`, `OnPeerJoined` and `OnMediaAdded`. To use the `OdinHandler`, make sure to add a variant of the `OdinManager` prefab into your project. The prefab also contains the `OdinEditorConfig` script, which allows us to set the Access Key and Room Audio Processing settings in the inspector.\n\nIf you don't yet have an ODIN subscription and just want to test out ODIN's functionality, you can use a generated key by pressing the __Manage Access__ Button and then selecting __Generate Access Key__. The resulting access keys can be used to access the ODIN network with up to 25 concurrently connected users free of charge.\n\n![The OdinManager prefab in the inspector view](Documentation/ODIN-TechDemo-OdinManager.webp)\n\n\n### PlaybackComponent\n\nThe ODIN SDK provides the `PlaybackComponent` script to easily play back audio data received from\nthe ODIN server. Each `PlaybackComponent` represents one media stream and is identified by a media id, a\npeer id and a room name.\n\n### User Data\n\nEvery peer in Unity can store arbitrary information as user data. When local user data is updated, the server updates user data on all clients. Read more about user data in the guide: [Understanding User Data](https://www.4players.io/odin/guides/unity/user-data/).\n\n## Multiplayer\n\n### Lobby\n\nBecause ODIN works framework independent, we won't go too much into detail on how to set up Photon - for an in-depth explanation, [please take a look at Photon's starter guide](https://doc.photonengine.com/en-us/pun/current/getting-started/pun-intro). \n\n**Note:** When first entering the Unity project, Photon will require you to add an App Id - simply follow the instructions to add or create your own App Id.\n\n![Image of the Lobby Scene](Documentation/ODIN-TechDemo-Lobby.webp \"The Lobby\")\n\nWe wait for a connection to the photon network, before allowing users to join a Photon room. In the demo we'll simply add all players to the same room. We also use the `PhotonNetwork.AutomaticallySyncScene = true` option to automatically load the correct scene for each player joining.\n\nAfter pressing the `Join` Button, the player will either connect to an existing Photon room or create a new Photon room as a Master Client. As a master client, we'll use the call: \n```c#\n    PhotonNetwork.LoadLevel(sceneToLoad);\n```\nOtherwise Photon will automatically load the correct scene.\n\n### Demo Level\n\nWhen entering the Demo Level scene, two things happen:\n\n1. We instantiate the player over the Photon network using `PhotonNetwork.Instantiate` and the `ODINPlayer` prefab. This is kicked off by the `PhotonPlayerSpawner` script on Start. **Note:** The player prefab needs to be located in a `Resources` subdirectory, in order for Photon to be able to instantiate the player correctly.\n\n2. We automatically connect to two ODIN rooms (Voice and Radio) with \n\n```c#\nOdinSampleUserData userData = new OdinSampleUserData(refPlayerName.Value);\nOdinHandler.Instance.JoinRoom(refRoomName.Value, userData);\n```\n\nWe don't have to send user data when joining an ODIN room, but in this case we already have access to the player name from the value entered in the Lobby scene, so it makes sense to supply it while joining.\n\n`OdinSampleUserData` is a serializable C# class which implements the `IUserData` interface. This is a requirement for any userdata transmitted using ODIN. The interface member function `ToBytes()` simply provides an UTF8 encoding of a JSON representation of the class. The class contains app specific properties like the player's name, his capsule color and a unique user id. The unique user id is used to connect an ODIN media stream to a Photon View - specifically the unique user id is equal to the photon view id - and therefore required for the proximity chat.\n\n## ODIN\n\n\n### Global Voice Chat - Radio transmissions\n\nIn the demo project, users automatically join the ODIN room named _Radio_, in\nwhich players can communicate as if using radio transmitters - when pressing down the `V` key, the microphone input\ncan be heard by all players in the room independent of their position.\n\nFor this scenario, the demo project provides the `OdinDefaultUser` script, which uses the \n`OdinHandler.Instance.OnMediaAdded` Event to spawn an instance of a prefab with a `PlaybackComponent` for\neach media stream in the ODIN room. The event provides the room name, peer id and media id required\nfor the `PlaybackComponent` to work. \n\nWe added the `OdinDefaultUser` script as a local-player-only behaviour - so it will only spawn Playbacks as children of the Player.\nThis doesn't matter for our radio transmissions, because they can be heard globally and shouldn't react to the distance between the\nlocal Player and remote Players. It also ensures, that each radio stream will only be spawned once for the player. But it also\nentails that the `OdinDefaultUser` script should only be used for ODIN rooms which do not make use of proximity voice chat. \n\nIn the next paragraph we'll take a look at how the Tech Demo implements a Proximity Chat which reacts to the distance between local Player and remote Players. We'll also take a look at how to create the \"Radio Crackling\" Effect in paragraph [Playback Settings](#playback-settings---distance-and-radio-effects).\n\n\n### Proximity Chat - connecting ODIN to the multiplayer framework\n\nIn a multiplayer scenario, we encounter the issue of connecting a user's ODIN media stream to the user's avatar\nin the game, e.g. in our demo project we'd want a player's voice in the proximity chat to come\nfrom the player's capsule. But because ODIN and the multiplayer framework don't know of each other's existence, we first have to logically connect the two concepts.\n\nThe abstract `AOdinMultiplayerAdapter` script gives access to the methods `string GetUniqueUserId()` and \n`bool IsLocalUser()`. This adapter is used to connect the player's representation in the multiplayer \nframework (using the framework's equivalent of an unique user id) to an ODIN peer. On ODIN's side we use \ncustom user data to keep track of that id. When joining an ODIN room, the `AOdinMultiplayerAdapter` \nautomatically sets the `uniqueUserId` of our custom ODIN user data for the current peer and sends an \nupdate to the other peers in the room. On receiving the update, those clients then use a reference to `AOdinMultiplayerAdapter` \nto compare the remote peer's `uniqueUserId` to the id supplied by the remote adapter's `GetUniqueUserId()`.\nIf both ids are equal,we know that an ODIN peer is represented by the referenced `AOdinMultiplayerAdapter`.\n\n![Diagram showcasing the adapter pattern for connecting ODIN to a multiplayer framework.](Documentation/ODIN-TechDemo-AdapterDiagram.svg)\n\nIn the demo project\nwe use this information to correctly play black the proximity chat audio at a player's location -\nspecifically using the `Odin3dAudioVoiceUser`, which automatically creates a `PlaybackComponent` for each remote user.\n\nThe demo project utilizes Photon as a multiplayer framework, so we add the\n`PhotonToOdinAdapter` to our player. The adapter uses `PhotonView.ViewID` as a unique user id and\n`PhotonView.IsMine`\nto determine whether the adapter represents a local or a remote player. To switch out Photon for another\nmultiplayer framework, simply provide your own class extending `AOdinMultiplayerAdapter`.\n\n### Playback Settings - Distance and Radio Effects\n\nODIN relies on Unity's `AudioSource` Components to play Media Streams. We can therefore just use the built in functionality of Audio Sources to adjust the distance at which players can hear each other. For any `AOdinUser` implementation (i.e. `OdinDefaultUser` for Radio transmissions and `Odin3dAudioVoiceUser` for Proximity Voice Chat) we can reference a prefab that will be spawned for each\nMedia Stream. These Prefabs not only have a `PlaybackComponent` on them, but also contain an `AudioSource`. So, to change the Playback Behaviour of Media Streams in-game, we have to change the `AudioSource` Settings on the prefab. The Tech Demo uses the prefabs `OdinRadioAudioSource` and `OdinVoiceAudioSource` in the `ODIN-Sample \u003e Prefabs` folder. \n\nThe `OdinRadioAudioSource` prefab simply has a full 2D `Spatial Blend` setting and the `Bypass Reverb Zone` enabled. The latter lets us avoid Unity's Reverb Zones, e.g. Echo effects in large rooms. The most interesting setting can be found in the `Output` option - here we reference an Audio Mixer Group Controller. The Radio Group Controller defines the list of effects that the incoming Radio room Media Streams go through, before being output to the User. The combination of these effects creates the Radio's crackling effect, giving Players a more immersive experience.\n\n![The Radio Mixer Settings for creating the crackling effect.](Documentation/ODIN-TechDemo-RadioEffects.webp)\n\nThe `OdinVoiceAudioSource` prefab on the other hand has a full 3D `Spatial Blend` setting and does not bypass reverb zones - we want this `AudioSource` to simulate a human voice in the real world, which is naturally affected by the environment. The prefab uses the 3D Sound Settings of the `AudioSource` component to further specify this effect - the `Min Distance` value defines the distance at which the voice will be heared at full volume and the `Max Distance` defines the distance at which we won't hear the voice anymore.\nAdditionally we can see the `Volume Rolloff` set to `Logarithmic Rolloff` - this best approximates a real world setting. If required, the rolloff can easily be customized by choosing a linear or custom setting.\n\n![The AudioSource component's 3D sound settings.](Documentation/ODIN-TechDemo-ProximitySettings.webp)\n\n These three options majorly define the fading behaviour of a player's voice in the distance - at least when there aren't any objects between the audio source and listener. Occlusion effects are not part of Unity's Audio System, but we've included our own, custom solution for this in the Tech Demo, which is explained in-depth in the [Audio Occlusion paragraph](#audio-occlusion).\n\n### Proximity Chat - Room Optimization\n\nAnother feature of ODIN is, that the ODIN servers can automatically stop the transmission of Media Streams based on the spatial distance between the local player and other players in the ODIN room. This allows us to optimize the bandwidth required for each player, avoiding unnecessary downstreams for voice transmissions that can't be heard by the player due to distance anyway! Of course, we only want to use this for proximity-based ODIN rooms, not for global rooms like the Radio Chat.\n\nTo enable this feature, we use the methods \n\n```c#\nroom.SetPositionScale(scale);\n```\nand \n```c#\nroom.UpdatePosition(position.x, position.y);\n```\nAs ODIN is not aware of the scale your game is operating at, it initially uses a Unit Circle as the cutoff radius. If we use the [previously mentioned](#playback-settings---distance-and-radio-effects) `Max Distance` to calculate `scale` as\n```c#\nfloat scale = 1.0f / MaxVoiceDistance;\n```\nwe can automatically disable streams that wouldn't be transmitted by the `Audio Source` due to distance anyway. \n\n__Note:__ The position scale should be set to the same value for all Peers in the ODIN room. The scale value also has to be set individually for each room that will utilize ODIN's optimization feature.\n\nFor ODIN to be able to use the distance values for optimization, we have to transmit player positions at regular intervals. The function `room.UpdatePosition` lets us define the client's current position in the room. If we define the correct room scale, we can simply use the player's `transform.position` x and z values. \n\n __Note:__ For now, we can only transmit 2D positions with this method. But as most games have a wide horizon and aren't scaled vertically, this is not a real drawback.\n\n In the Tech Demo, the `OdinPositionUpdate` component regularly updates the player's position. Using entries to the Room Settings array on the `OdinPositionUpdateSettings` scriptable object, we can define the activation status and the cutoff radius for each ODIN room individually.\n\n ![The ODIN room optimization and position update settings.](Documentation/ODIN-TechDemo-RoomOptimization.webp)\n\n### Push-To-Talk\n\nPush-To-Talk is handled by the `OdinPushToTalk` script using settings defined in a `OdinPushToTalkSettings` scriptable object. If the push-to-talk button for a specific room is pressed, the script will access the user's mediastream and set a user's mute status using `targetRoom.MicrophoneMedia.SetMute()`.\n\nThe `OdinPushToTalkSettings` scriptable object allows rooms to be either be voice-activated or require a button press to talk - if you'd like to make this available as a user-setting, you can use the `OdinPushToTalkSettingsController`, which automatically sets the room's activation method based on a Unity Toggle. Take a look at the Tech Demo's `Settings` prefab (found in `ODIN-Sample \u003e Prefabs \u003e UI`) for more information.\n\n\n### Audio Filter Settings\n\nThe ODIN SDK provides quite a few Room Audio Processing settings, like Voice Activity Detection, Echo Cancellation, Noise Suppression levels and more. If you're content with using the same settings for all users, you can simply adjust the values on the `OdinManager` prefab ([as shown here](#odinhandler)). \n\nThe Tech Demo has a sample implementation on how to allow users to adjust these settings in the game. The `Settings` prefab (found in  `ODIN-Sample \u003e Prefabs \u003e UI`) uses Unity's `Toggle`, `Slider` and `Dropdown` UI components to adjust the Audio Settings. The `OdinAudioFilterSettingsController` script contains entries that map the UI component's input to ODIN's filter values and even stores the changes to file. For a fast integration into your game, you can use the Tech Demo implementation and adjust the UI graphics to your liking.\n\n ![The UI for adjusting the Audio Filter Settings.](Documentation/ODIN-TechDemo-AudioFilterSettings.webp)\n\n\n### Choosing an Input Device\n\nThe previously mentioned `Settings` prefab also allows players to choose their input device. The options that are available are based on Unity's `Microphone.devices` ouput and displayed in the dropdown component. Updating the input device used by ODIN is then as simple as calling\n\n```c#\nMicrophoneReader microphoneReader = OdinHandler.Instance.Microphone;\nmicrophoneReader.StopListen();\nmicrophoneReader.CustomInputDevice = true;\nmicrophoneReader.InputDevice = selectedDevice;\nmicrophoneReader.StartListen();\n```\nwhere the `selectedDevice` is one of the string options listed in the `Microphone.devices` array. The Tech Demo uses the implementation in the `OdinMicrophoneController` script, which also handles saving and loading the users selection in previous game sessions.\n\n\n## Audio\n\nTo better showcase the capabilities of ODIN in apps and games, we've implemented some audio\nfeatures that are often used in games, but not included in Unity's Audio System: Audio Occlusion and Directional Audio. Because we want to keep things simple and performant, we're going to approximate those effects, using Unity's ``AudioLowPassFilter`` component\nand by adjusting the volume of individual audio sources.\n\n### Audio Occlusion\n\nAudio Occlusion should occur when an object is placed between the audio listener (our player) and audio sources in the scene - e.g.\nhearing the muffled sounds of an enemy approaching from behind a wall.\nUnity does not have any kind of built-in audio occlusion, so we need to implement our own system. \nThe `OcclusionAudioListener` script contains most of the occlusion logic and is placed, together with the `AudioListener` script,\non our local player object. The `OcclusionAudioListener` registers objects with colliders, that enter the detection range and have at least one `AudioSource` script attached in the transform hierarchy. By default the detection range \nis set to 100 meters - Audio Sources that are farther away than that are usually \nnot loud enough to be affected meaningfully by our occlusion system.\nWe then apply the occlusion effects to each of the registered Audio Sources in every frame. \n\nOur occlusion effects have the parameters \n`Volume`, `Cutoff Frequency` and `Lowpass Resonance Q`:\n- **Volume:** Multiplier for the audio source's volume.\n- **Cutoff Frequency:** Removes all frequencies above this value from the output of the Audio Source. This value is probably\nthe most important for our occlusion effect, as is makes the audio sound muffled. The cutoff frequency can range\nfrom 0 to 22.000 Hz.\n- **Lowpass Resonance Q:** This value determines how much the filter dampens self-resonance. This basically means, the \nhigher the value, the better sound is transmitted through the material the filter is representing. E.g. for imitating an iron\ndoor, the `Lowpass Resonance Q` value should be higher than for imitating a wooden door.\n\nThe occlusion effect is based on the thickness of objects between our \n`AudioListener` and the `AudioSource`. For each audio source we check for colliders placed between the listener and the source using raycasts and\ndetermine the thickness of the collider. This thickness value is then used to look\nup the final effect values from an `AudioEffectDefinition` ScriptableObject. For each of \nthe three parameters `Volume`, `Cutoff Frequency` and `Lowpass Resonance Q` the ScriptableObject\ncontains a curve, which maps from the collider's thickness on the x-Axis to the parameter value\non the y-Axis.\n\nThe image below shows an Audio Effect Definition Scriptable Object for the Concrete material. When selecting the `Cutoff Frequency Curve`, Unity's Curve Editor window shows up to allow finetuning the settings. The x-axis displays the thickness of an occluding object in meters. The curve then maps to the cutoff frequency displayed on the y-axis.\n\n![The Audio Effect Definition Scriptable Object.](Documentation/ODIN-TechDemo-OcclusionEffectSettings.webp)\n\nThe `AudioEffectDefinition` is retrieved using one of two options:\n- By placing an `AudioObstacle` script on the collider's gameobject. This can be\nused to customize a collider's occlusion effect and give it a certain material's damping\nbehaviour. The demo uses the `AudioObstacle` to define the occlusion effect of a brick wall,\na wooden door, a glass pane or even a 100% soundproof barrier.\n- By falling back to the default `AudioEffectDefinition` - this option is used, if no `AudioObstacle`\nis attached to the collider. \n\nYou can create your own `AudioEffectDefinition` by using the `Create \u003e Odin-Sample \u003e AudioEffectDefinition` \nmenu in your project hierarchy. \n\n### Directional Audio\n\nUnity's built in audio system allows us to hear differences between sounds coming from left \nor right, but not whether the object is in front or behind us. The `DirectionalAudioListener` script will take care\nof this using basically the same effects as the audio occlusion system. \n\nSimilar to the `OcclusionAudioListener`, we apply an effect to each Audio Source in \nthe detection range - but instead of using the thickness of objects between source and listener,\nwe interpolate the effect based on the angle between the listener's forward vector and a vector\npointing from the listener to the audio source. \n\nNote: The implementation currently won't let us differentiate between sound coming from above or below. To implement this behaviour, \nplease take a look at the implementation of [Head Related Transfer Functions (HRTF)](https://en.wikipedia.org/wiki/Head-related_transfer_function).\n\n\n### Environmental Effects\n\nThe Tech Demo Level contains a few rooms, that highlight the Audio Occlusion effect for different materials. Additionally, we've used Unity's `AudioReverbZone` components to add environmental effects to these rooms, to further increase the players immersion. Unity provides a few presets which simulate different environments - e.g. the Demo Level's \"Brick Room\" uses the `Stone Corridor` preset - but also allows to be set to a custom arrangement. The effect will start to be heared at `MaxDistance` and is at full force inside of the `MinDistance` radius.\n \nWhile the Voice transmissions are affected by the reverb zones, the Radio transmissions are not, due to the `Bypass Reverb Zone` setting on the Playback Prefab - as described [here](#playback-settings---distance-and-radio-effects).\n\n![The Brick Room with the highlighted Audio Reverb Zone.](Documentation/ODIN-TechDemo-EnvironmentalEffects.webp)\n\n## Game Logic\n\n### Player Name and Color Synchronization\n\nThe `OdinNameDisplay` and `OdinSyncedColor` scripts use the ODIN's custom User Data to synchronize the player's name and avatar color. With `OdinHandler.Instance.OnPeerUserDataChanged.AddListener()` we listen to any changes on a remote peers User Data - if the peer's `UniqueUserId` equals the Multiplayer Adapter's `GetUniqueUserId()`, we read the name and display it above the peer's avatar using a world-space canvas and display the color on the avatar's mesh.\n\n### Mobile Controls\n\nBecause ODIN works on mobile platforms, we've added mobile controls to the Tech Demo. The implementation is based on Unity's new Input System and allows players to move, rotate, use Push-To-Talk and switch from 3rd to 1st-person view with On-Screen controls. Take a look at the `UI \u003e MobileControlsCanvas` in the DemoLevel scene's hierarchy for implementation details. The mobile controls can be simulated in Editor by selecting the `Enable in Editor` option on the `ActivateIfMobile` script. \n\n![Activated mobile controls in the Tech Demo.](Documentation/ODIN-TechDemo-MobileControls.webp)\n\n### Toggling the in-game radio objects\n\nThe `ToggleRadioBehaviour` script implements the basic behaviour for turning the in-game radio sources on and off - if an  avatar enters the detection radio, the controlling player will be able to toggle the radio status with a button press. The status then gets synchronized with the `PhotonRadioSynchronization` behaviour, which listens to the toggle's `onRadioToggled` UnityEvent.\n\nIf you'd like to implement another multiplayer framework, simply replace the `PhotonRadioSynchronization` with your own synchronization behaviour. \n\n### Switching Player Views\n\nUsers can switch from 3rd-person to 1st-person view on button press - this is implemented in the `ToggleViews` script by toggling game objects found in the `ODINPlayer \u003e LocalPlayerBehaviours \u003e ControllerState` hierarchy of the `ODINPlayer` prefab. The `FirstPersonBehavior` object contains the camera and behaviours for 1st-person controls, the `ThirdPersonBehavior` contains everything for 3rd-person controls.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4players%2Fodin-unity-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F4players%2Fodin-unity-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4players%2Fodin-unity-demo/lists"}