Build a session with Netcode for Entities
Build a multiplayer game with sessions using Netcode for Entities for projects using Unity's Data-Oriented Technology Stack (DOTS).
Read time 9 minutesLast updated 12 hours ago
Similar to Build a session with Netcode for GameObjects, use the principles of Multiplayer Services sessions to create one capsule controlled by the session's host and the other capsule controlled by the joining player. However, this time, use Network for Entities, which is part of Unity's Data-Oriented Technology Stack (DOTS).
Prerequisites
In addition to the Multiplayer Services package, ensure that you install the following packages through the Unity Registry in the Editor's Package Manager:- (or
Widgets): Enables you to test multiplayer services without having to write code.com.unity.multiplayer.widgets - (or
Multiplayer Play Mode): Enables you to test multiplayer functionality without leaving the Unity Editor.com.unity.multiplayer.playmode - Packages required by Netcode for Entities, which include entities, entities graphics, and networking capabilities:
- (or
Netcode for Entities), version 1.3.0 or later.com.unity.netcode - (or
Entities Graphics), version 1.3.0 or later.com.unity.entities.graphics
Configure Multiplayer Play Mode
To configure Play Mode for your project to test multiple players:- In the Unity Editor, go to Window > Multiplayer > Multiplayer Play Mode.
- In the Multiplayer Play Mode window, enable Player 2.
Configure Build Profiles and Project Settings
You must add the current scene to the build to enter Play Mode. To add the current scene to the build:- In the Unity Editor, select File > Build Profiles.
- Select Open Scene List.
- Select Add Open Scenes.
- Close Build Profiles.
- Select Edit > Project Settings > Player > Resolution and Presentation.
- Enable Run in Background.
- Close Project Settings.
Create an initial scene
Set up a way to share data between the client and the server in Netcode for Entities:- Right-click (macOS: Ctrl+click) within the Hierarchy window in the Unity Editor.
- Select New Sub Scene > Empty Scene.
- Name the new scene .
SharedData.unity
Create a ghost prefab
To synchronize data across a client and server setup, create a definition of the networked object, called a ghost. To create a ghost prefab:- In the Hierarchy window, right-click (macOS: Ctrl+click) on the new scene.
- Select GameObject > 3D Object > Capsule.
- Rename the capsule to .
Player Prefab - In the Project tab, right-click (macOS: Ctrl+click) the folder, then Create > Folder.
Assets - Name the new folder .
Prefabs - Drag the object from the Hierarchy window into the
Player Prefabfolder to make it into a prefab.Prefabs - In the Hierarchy window, right-click (macOS: Ctrl+click) the capsule, then select Delete. This deletes the player from the scene.
IComponentAuthor-
In the Project tab, right-click (macOS: Ctrl+click) the folder, then Create > Folder.
Assets -
Name the new folder .
Scripts - Right-click (macOS: Ctrl+click) the new Scripts folder, then Create > Scripting > Empty C# Script.
-
Rename this script .
PlayerAuthoring.cs - Double-click the script to edit it.
-
Replace the contents of this script with the following code:
using Unity.Entities;using Unity.NetCode;using UnityEngine;public struct Player : IComponentData{}[DisallowMultipleComponent]public class PlayerAuthoring : MonoBehaviour{ class Baker : Baker<PlayerAuthoring> { public override void Bake(PlayerAuthoring authoring) { var entity = GetEntity(TransformUsageFlags.Dynamic); AddComponent<Player>(entity); } }}
-
In the Project tab, select >
Assets>Prefabsso its Inspector window opens.Player Prefab -
In the Project tab, select >
Assets.Scripts -
Drag the script to the
Player AuthoringInspector window to attach it.Player Prefab - In the Inspector window, select Add component.
-
Search for , then select it and add it to the Prefab.
Ghost Authoring Component
- Enable Has Owner. This setting automatically adds and checks a new property called Support Auto Command Target.
- Change the Default Ghost Mode to Owner Predicted. Later, setting the member of the Ghost Owner Component through code ensures that you predict your own movement.
NetworkId
Create a spawner
To tell Netcode for Entities which ghosts to use, reference the Prefabs from the sub-scene. First, create a new component for the spawner:- In the Project tab, right-click (macOS: Ctrl+click) the Scripts folder, then Create > Scripting > Empty C# Script.
- Rename this script .
PlayerSpawnerAuthoring.cs - Replace the contents of this script with the following code:
using Unity.Entities;using UnityEngine;public struct PlayerSpawner : IComponentData{ public Entity Player;}[DisallowMultipleComponent]public class PlayerSpawnerAuthoring : MonoBehaviour{ public GameObject PlayerPrefab; class Baker : Baker<PlayerSpawnerAuthoring> { public override void Bake(PlayerSpawnerAuthoring authoring) { PlayerSpawner component = default(PlayerSpawner); component.Player = GetEntity(authoring.PlayerPrefab, TransformUsageFlags.Dynamic); var entity = GetEntity(TransformUsageFlags.Dynamic); AddComponent(entity, component); } }}
- Right-click (macOS: Ctrl+click) within the Hierarchy window, then select Create Empty.
- Rename the empty GameObject to .
Spawner - With the empty object selected in the Hierarchy window, in its Inspector window, select Add Component.
- Attach the script.
Player Spawner Authoring - With the Inspector window still open, select the Assets > Prefabs folder from the Project tab.
- Drag the Player Prefab object to the Player Prefab field in the Spawner's Inspector window.
- In the Hierarchy window, move the Spawner into the SharedData sub-scene.
Enable communication and spawn the prefab
To enable communication between the client and server, you need to establish a connection. The Multiplayer Services SDK manages connections so you can focus on which messages are communicated. Netcode for Entities uses remote procedure calls (RPC) for its messages. A critical concept in Netcode for Entities isInGameInGameGoInGame- In the Project tab, right-click (macOS: Ctrl+click) the Scripts folder, then Create > Scripting > Empty C# Script.
- Rename this script .
GoInGameSystems.cs - Replace the contents of this script with the following code:
using UnityEngine;using Unity.Collections;using Unity.Entities;using Unity.NetCode;using Unity.Burst;/// <summary>/// This allows sending RPCs between a standalone build and the Editor for testing purposes in the event that, when you finish this example,/// you want to connect a server-client standalone build to a client-configured Editor instance./// </summary>[BurstCompile][WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ServerSimulation | WorldSystemFilterFlags.ThinClientSimulation)][UpdateInGroup(typeof(InitializationSystemGroup))][CreateAfter(typeof(RpcSystem))]public partial struct SetRpcSystemDynamicAssemblyListSystem : ISystem{public void OnCreate(ref SystemState state){SystemAPI.GetSingletonRW<RpcCollection>().ValueRW.DynamicAssemblyList = true;state.Enabled = false;}}// RPC request from client to server for game to go "in game" and send snapshots / inputspublic struct GoInGameRequest : IRpcCommand{}// When client has a connection with network ID, go in game and tell server to also go in game[BurstCompile][WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ThinClientSimulation)]public partial struct GoInGameClientSystem : ISystem{[BurstCompile]public void OnCreate(ref SystemState state){// Run only on entities with a PlayerSpawner component datastate.RequireForUpdate<PlayerSpawner>();var builder = new EntityQueryBuilder(Allocator.Temp) .WithAll<NetworkId>() .WithNone<NetworkStreamInGame>();state.RequireForUpdate(state.GetEntityQuery(builder));}[BurstCompile]public void OnUpdate(ref SystemState state){var commandBuffer = new EntityCommandBuffer(Allocator.Temp);foreach (var (id, entity) in SystemAPI.Query<RefRO<NetworkId>>().WithEntityAccess().WithNone<NetworkStreamInGame>()){ commandBuffer.AddComponent<NetworkStreamInGame>(entity); var req = commandBuffer.CreateEntity(); commandBuffer.AddComponent<GoInGameRequest>(req); commandBuffer.AddComponent(req, new SendRpcCommandRequest { TargetConnection = entity });}commandBuffer.Playback(state.EntityManager);}}// When server receives go in game request, go in game and delete request[BurstCompile][WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]public partial struct GoInGameServerSystem : ISystem{private ComponentLookup<NetworkId> networkIdFromEntity;[BurstCompile]public void OnCreate(ref SystemState state){state.RequireForUpdate<PlayerSpawner>();var builder = new EntityQueryBuilder(Allocator.Temp) .WithAll<GoInGameRequest>() .WithAll<ReceiveRpcCommandRequest>();state.RequireForUpdate(state.GetEntityQuery(builder));networkIdFromEntity = state.GetComponentLookup<NetworkId>(true);}[BurstCompile]public void OnUpdate(ref SystemState state){// Get the prefab to instantiatevar prefab = SystemAPI.GetSingleton<PlayerSpawner>().Player;// Ge the name of the prefab being instantiatedstate.EntityManager.GetName(prefab, out var prefabName);var worldName = new FixedString32Bytes(state.WorldUnmanaged.Name);var commandBuffer = new EntityCommandBuffer(Allocator.Temp);networkIdFromEntity.Update(ref state);foreach (var (reqSrc, reqEntity) in SystemAPI.Query<RefRO<ReceiveRpcCommandRequest>>().WithAll<GoInGameRequest>().WithEntityAccess()){ commandBuffer.AddComponent<NetworkStreamInGame>(reqSrc.ValueRO.SourceConnection); // Get the NetworkId for the requesting client var networkId = networkIdFromEntity[reqSrc.ValueRO.SourceConnection]; // Log information about the connection request that includes the client's assigned NetworkId and the name of the prefab spawned. UnityEngine.Debug.Log($"'{worldName}' setting connection '{networkId.Value}' to in game, spawning a Ghost '{prefabName}' for them!"); // Instantiate the prefab var player = commandBuffer.Instantiate(prefab); // Associate the instantiated prefab with the connected client's assigned NetworkId commandBuffer.SetComponent(player, new GhostOwner { NetworkId = networkId.Value}); // Add the player to the linked entity group so it is destroyed automatically on disconnect commandBuffer.AppendToBuffer(reqSrc.ValueRO.SourceConnection, new LinkedEntityGroup{Value = player}); commandBuffer.DestroyEntity(reqEntity);}commandBuffer.Playback(state.EntityManager);}}
Move the prefab
Because you used the Support Auto Command Target feature when you set up the ghost component, you can take advantage of theIInputComponentDataSystemIInputComponentData- In the Project tab, right-click (macOS: Ctrl+click) the Scripts folder, then Create > Scripting > Empty C# Script.
- Rename this script .
PlayerInputAuthoring.cs - Replace the contents of this script with the following code:
using Unity.Burst;using Unity.Entities;using Unity.NetCode;using UnityEngine;public struct PlayerInput : IInputComponentData{ public float Horizontal; public float Vertical;}[DisallowMultipleComponent]public class PlayerInputAuthoring : MonoBehaviour{ class PlayerInputBaking : Unity.Entities.Baker<PlayerInputAuthoring> { public override void Bake(PlayerInputAuthoring authoring) { var entity = GetEntity(TransformUsageFlags.Dynamic); AddComponent<PlayerInput>(entity); } }}[UpdateInGroup(typeof(GhostInputSystemGroup))]public partial struct SamplePlayerInput : ISystem{ public void OnCreate(ref SystemState state) { state.RequireForUpdate<NetworkStreamInGame>(); state.RequireForUpdate<PlayerSpawner>(); } public void OnUpdate(ref SystemState state) { foreach (var playerInput in SystemAPI.Query<RefRW<PlayerInput>>().WithAll<GhostOwnerIsLocal>()) { playerInput.ValueRW = default; playerInput.ValueRW.Horizontal = Input.GetAxis("Horizontal"); playerInput.ValueRW.Vertical = Input.GetAxis("Vertical"); } }}
- In the Project tab, select >
Assets>Prefabs.Player Prefab - Attach the script to the
Player Input AuthoringInspector window.Player Prefab
- In the Project tab, right-click (macOS: Ctrl+click) the Scripts folder, then Create > Scripting > Empty C# Script.
- Rename this script .
PlayerMovementSystem.cs - Replace the contents of this script with the following code:
using Unity.Entities;using Unity.Mathematics;using Unity.NetCode;using Unity.Transforms;using Unity.Burst;[UpdateInGroup(typeof(PredictedSimulationSystemGroup))][BurstCompile]public partial struct PlayerMovementSystem : ISystem{ [BurstCompile] public void OnUpdate(ref SystemState state) { var speed = SystemAPI.Time.DeltaTime * 4; foreach (var (input, trans) in SystemAPI.Query<RefRO<PlayerInput>, RefRW<LocalTransform>>().WithAll<Simulate>()) { var moveInput = new float2(input.ValueRO.Horizontal, input.ValueRO.Vertical); moveInput = math.normalizesafe(moveInput) * speed; trans.ValueRW.Position += new float3(moveInput.x, 0, moveInput.y); } }}
Create and join your session
The following steps outline how to create and join a session using Netcode for Entities with Widgets and Multiplayer Services in the Unity Editor.-
Right-click (macOS: Ctrl+click) in the Hierarchy window, then select Multiplayer Widgets > Create > Create Session.
- In the Hierarchy window, select Canvas > Create Session.
-
In its Inspector window, set Rect Transform > Pos Y to .
50 - Right-click (macOS: Ctrl+click) in the Hierarchy window, then select Multiplayer Widgets > Info > Show Session Code.
- Right-click (macOS: Ctrl+click) in the Hierarchy window, then select Multiplayer Widgets > Join and Leave > Join Session By Code.
- In the Hierarchy window, select Canvas > Join Session By Code.
-
In its Inspector window, set Rect Transform > Pos Y to .
-100 - In the Unity Editor, select File > Save to refresh the scene for Player 2.
- In the Toolbar, select Play to enter Play Mode.
- In the Game window's Session Name field, enter a name and then select Create.
- Copy the session join code.
- In the Player 2 window's Session Code field, enter the code and then select Join.
Move the capsules
To move the capsules in your multiplayer session:- Click within the Game window of the Editor.
- Use the arrow keys on your keyboard to move the capsule around the scene. It moves in real time in both the Game window and the Player 2 window.
- Click within the Player 2 window.
- Use the arrow keys on your keyboard to move the capsule around the scene. Notice that the other capsule (Player 2's capsule) moves this time.
Additional resources
- Networked cube (Unity Netcode for Entities)
- Get acquainted with DOTS (Unity Learn)
- Multiplayer Services SDK