# What is the Purpose of this DOTS Tutorial?

This is a sample project that demonstrates how to create a multiplayer AR experience using Unity's DOTS. We include full workflows, code, GIFs and also share links to external resources.

![Finished Project - Shooting down AR player from desktop](/files/-MSQkWsGeth5AFZOJPkL)

![Finished Project - Getting shot down by desktop player](/files/-MSQk_ChKcD34qcuPQvM)

## Purpose

At [Moetsi](https://dots-tutorial.moetsi.com/www.moetsi.com) we build Reality Modeling technologies for Mixed Reality experiences. By Reality Modeling we mean the process of: capturing the spatial map of a real space, modeling the state of the environment, the objects within it, and the positioning of the sensors, and then packaging all that data for end-users.&#x20;

A lot of our use-cases require both desktop and "live" (AR) client networking because part of what our technology does is stream updated Reality Models in real-time to connected clients, which is data-intensive.

We choose Unity DOTS and DOTS NetCode for our interaction layer. DOTS is able to handle heavy data processing without draining device batteries. DOTS NetCode uses an authoritative server model that works best for Reality Modeling.

Unity DOTS is still in the beginning of its development and as such, it goes through major iterations. Sometimes there are breaking changes in newly released versions of DOTS. We know this is frustrating, so to help out the developer community, the Moetsi team does its very best to keep up with DOTS' evolution and its interdependencies across Unity's technology stack. There are not too many DOTS tutorial resources online, which means there are probably a lot of developers going through the same (painful) process of trying to figure it all out. Resources on how to connect GameObject (Monobehaviour) Unity and ECS Unity (hybrid development) seems to be especially lacking for things like UI Toolkit and UI Builder. Hopefully Moetsi fills a void for you and the rest of the dev community!

We provide a tutorial and sample project for how to create a multiplayer real-time XR experience using Unity's DOTS. Anyone that has been interested in trying out DOTS but are concerned about stability or package interoperability can use this tutorial knowing that "it will work."

In this gitbook, we build a project that can deploy to both desktop and ARKit platforms. Desktop players will be able to navigate using WASD keys and AR players will be able to navigate using their device movement.

![Navigating between scenes using UI and appropriately handling creating/destroying client/server worlds](/files/-MR6nT8oNuxIKApT1sOW)

Github repo: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample>

Our focus here is on **how** to put pieces together in Unity DOTS, not an explanation of **what** the pieces are. For in-depth explanations of "what" the technologies are, we provide links to great external resources throughout this gitbook; we encourage you to click on those to dive in and learn more!

The styling (GUI) in the builds of this sample project is very minimal **on purpose**.  Our sample project is not meant to provide a how-to guide on how to make a [juicy](https://www.youtube.com/watch?v=Fy0aCDmgnxg) [game](https://www.youtube.com/watch?v=AJdEqssNZ-U) because we do not want to cloud your understanding of the underlying architecture. That's why we exclude anything but **basic** example implementations. If you are underwhelmed by the styling of the Asteroids apps in this project, that is *good*. A lot of the other DOTS tutorial materials we found online added superfluous bells and whistles that made projects more interesting, but we found that they got in the way of our true understanding of the underlying architecture.

Code-alongs will take anywhere from 5 to 50 hours (depending on your skill level and time commitment to reading through each line) and they cover these Unity packages:

{% tabs %}
{% tab title="Entities" %}
**Entities v0.50**

* Sub Scenes and conversions
  * Setting components and variables during conversion
* Using EntityManager to make structural changes
* Using command buffers to make structural entity changes
  * Setting up command buffers to run parallel jobs with CreateCommandBuffer().AsParallelWriter()&#x20;
* Adding, setting, and removing components
* Generating authoring components and IConvertGameObjectToEntity interfaces
* Entity queries and .ForEach()
  * Setting local variables for entity queries
  * Scheduling a job with .Schedule()
  * Scheduling a parallel job with .ScheduleParallel()
  * Running on the main thread with .Run()
  * Combining job handles with JobHandle.CombineDependencies()
* Create and destroy entities programmatically
* Create and move entities based on user input
* Create an entity from a prefab
  * Adding cameras to entities
* Hybrid ECS
* New Build Configuration system
  * Creating a base shared configuration
* Sub-second "play" start

{% content-ref url="/pages/-MPezXtKnPfbloHy1ufP" %}
[Intro to Unity ECS](/unity-ecs/intro-to-unity-ecs)
{% endcontent-ref %}
{% endtab %}

{% tab title="Physics" %}
**Physics v0.50**

* Unity DOTS Physics
  * Using FixedStepSimulationGroup for custom systems
* Physics Body
  * Adjusting PhysicsVelocity programmatically
* Physics Shape
  * Updating shape type to prefab
* Physics Categories
  * Custom categories for collisions and interactions
* Triggers on collisions
  * Changing stateless triggers to buffer of stateful triggers
* Acting on triggers to change materials and destroy entities

{% content-ref url="/pages/-MPgOocrqWjrmKu\_2he0" %}
[Intro to Unity DOTS Physics](/unity-dots-physics/intro-to-unity-dots-physics)
{% endcontent-ref %}
{% endtab %}

{% tab title="Netcode" %}
**NetCode v0.50.1-preview\.19**

* Navigating multiple ECS Worlds
* Creating a ECS NetworkConnectionEntity (NCE) on the client and server
  * Creating a socket connection using NetworkStreamReceiveSystem
  * Use GhostDistanceImportance component
* Sending RPCs between server and client
  * Sending data with RPCs
  * Using InvokeExecute on receiving NCE to make updates
* Loading a game on the client
  * Using NetworkStreamInGame component
* Updating the CommandTargetComponent on a NetworkConnectionEntity
  * Setting targetEntity field to ICommandData buffer
* Creating networked entities ("Ghosts")
  * Updating Supported Ghost Modes
* Server-spawned entities
* Sending client inputs with ICommandData
  * Predicted responses to ICommandData by using ClientSimulationSystemGroup.ServerTick
* Responding to ICommandData on both the client and server using GhostPredictionSystemGroup.ShouldPredict()
* Client-predicted entities and predicted-spawned entities
  * Adding PredictedGhostSpawnRequestComponent
* Ghost classification systems
  * Traversing GhostSpawnBuffer to locate predicted spawn entity
* Proper NetCode entity destruction
  * Server-side destruction
  * Using ISystemStateComponent component as a call back to clean up destroyed players

{% content-ref url="/pages/-MPgPrI22gT8N4wMwmhN" %}
[Intro to DOTS NetCode](/dots-netcode/intro-to-dots-netcode)
{% endcontent-ref %}
{% endtab %}

{% tab title="UI B/TK" %}
**UI Builder  v1.0-preview\.18 and UI Toolkit v1.0**

* Data binding
* Creating a UI document and Panel settings
* Creating a ScreenManager to handle switching between views
* Nesting uxmls and custom Visual Elements in UI Builder
* Creating custom VisualElements
  * Setting callbacks on OnGeometryChange()
  * Updating DisplayStyle to switch views
* Creating a USS document shared by multiple UXML files
  * Creating a class list
  * Extracting inline styles to a class list
  * Adding classes to elements from StyleSheets
  * Creating custom styling for :hover and :active states
* Styling a view to be responsive to changes in width to be prepared for both mobile and desktop views
  * Creating headers and footers for game information
  * Using display flex in UI builder to create responsive designs
  * Using standard Unity elements to create an interface
    * VisualElement
    * Label
    * TextField
    * Button
  * Adding a png or SVG background
* Creating a ListView
  * Setting a source of data for the ListView
  * Creating custom click events and loading data when clicking on an item in ListView
* Loading data in between scenes to configure host/client build
  * Using launch GameObjects to configure a scene to build as a host or a client
* Creating a ClientServerBootstrap to stop automatic world creation
  * Triggering client and server worlds manually
  * Supporting Thin Clients when creating client worlds
* Deleting all entities and worlds
  * Using UniversalQuery in EntityManager
  * Clean up to return to initial state

{% content-ref url="/pages/-MPgQLKOZsbGRjCjkWaG" %}
[Intro to UI Toolkit](/ui-builder-and-ui-toolkit/intro-to-ui-toolkit-in-unity-ecs)
{% endcontent-ref %}
{% endtab %}

{% tab title="Multi" %}
**NetCode v0.50.1-preview\.19, UDP Client, Broadcasting, Threads, Graceful exits**

* Using launch GameObjects to configure server and client connections
  * Triggering starting a game as a NetCode host
  * Triggering starting a game joining as a NetCode client to a specific IP address
* Sending and receiving broadcast messages
  * Automatically sending a broadcast UDP packet of game information including IP address on LAN for players to join
  * Listening and receiving broadcast UDP packet information and populating a ListView of available LAN games
* Running Threads for listening for UDP packets
  * Being able to run non-Unity handled threads in-game
* Graceful handling of joining and leaving games
  * Adding NetworkStreamRequestDisconnect component on both host and client when quitting a game
  * Handling timeouts by querying for NetworkStreamDisconnected components from host and client to clean up entities and return to title screen
* Server-authoritative score keeping
  * Creating PlayerScore ghosts and HighestScore ghosts to keep player scores authoritative and in sync
  * Associating scores with NCE NetworkIdComponent values
* UI Toolkit + DOTS
  * Updating Game UI through a MonoBehaviour by pulling DOTS data from ghosts
* Using GhostRelevancyMode to be mindful of network transmission
  * We will implement a server-side system that will send only send ghosts within a certain radius to players
  * We will also include overriding this behavior to send player scores to all players

{% content-ref url="/pages/-MPgQfWDNpqhXU-Gsi3i" %}
[Intro to Unity NetCode Multiplayer](/multiplayer/intro-to-unity-netcode-multiplayer)
{% endcontent-ref %}
{% endtab %}

{% tab title="AR Found" %}
**AR Foundation v4.2.3**

* Setting up AR Foundation + ARKit plug-in
* Adding AR Foundation GameObjects to game
  * AR Session Origin
  * AR Session
* Dynamically checking if deployed to AR platform and disabling AR functionality if not
  * Creating AR-specific systems that only run when AR enabled
* Grabbing Pose Driver value and providing it to ECS to move player
  * Updating input response systems for updated movement controls
* Pulling ECS data spawn position and updating Pose Driver to move location of AR Camera to behind player
  * Use ARSessionOrigin.MakeContentAppearAt() to update origin of AR session based on game play
* Dynamically updating UI for AR instructions when deployed to AR platform
* Updating PanelSettings to be responsive to both desktop and mobile platforms

{% content-ref url="/pages/-MPgQyRt8KXD5CnTJhOO" %}
[Intro to AR Foundation](/ar-foundation/intro-to-ar-foundation)
{% endcontent-ref %}
{% endtab %}
{% endtabs %}

The UI Builder and UI Toolkit sections can be useful as standalone. However, all of the other sections build off one another so it is best to go page-by-page.

{% hint style="success" %}
At Moetsi, we will always update our sample project to stay current with the latest set of working Unity packages.

Case in Point: in the middle of us writing this gitbook, DOTS was upgraded mid-project and we adjusted course to update our documentation and all of the packages as soon as possible.
{% endhint %}

### This Sample Project is intended for developers who:

✅ already have a general understanding of Unity

✅ are looking for info on Entity, Jobs, Physics, NetCode, UI Builder, UI Toolkit, Multiplayer and AR Foundation packages

✅ are looking for a stable  Monobehaviour / ECS hybrid solution template that is kept up to date with new releases

✅ are interested in building apps and/or experiences with high number of components/performance requirements (very often these are AR or other mixed reality apps, but don't need to be!)

### This is NOT intended for those who:

❌ are looking for a full introduction to software development

❌ are looking for a "Pure ECS" solution

❌ are looking for a framework


# Important Notes For Using This Gitbook

Some things to keep in mind as you code along.

## Although the code and files will **always** be up-to-date, the gifs might not be.

If and when Unity releases a new version update, or updates package versions, we will update the code in this gitbook to properly implement the changes, and we will also update the branches of our repo as well (links are always found at bottom of each section of this gitbook).

**This means that the code and instructions in this gitbook&#x20;*****will*****&#x20;always "work" for the specified Unity/packages version.**

However unlike the code, we won't be able to re-upload all of the gifs to stay current because then we'd have to rebuild the project from scratch each time Unity or a package updates and screen-record each and every step, and that seems like a full-time job. Instead of spending our time re-uploading gifs, we think that we can better serve the Mixed Reality (XR) community by focusing on building more tools for XR developers rather than making gifs pixel-perfect.

Rule of thumb: always trust the **code snippet itself**, when following instructions throughout the gitbook; do *not* fixate on the code within the gif! Why? Because the code **in the gif** might be old, and it might not be the *exact* same in the snippet. **Trust the code snippet and instructions, not that in the gif!** The reason why we included the gifs at all is to provide some comfort in demonstrating that we make the files as described by the instructions; again, the gifs are not meant to be the source of truth.

However, if a new Unity/package update creates a wildly different flow and the gifs start to become debilitatingly misleading, then we will absolutely update them to not throw anyone off. But otherwise, we will just stay focused on updating the instructions and code.

For example, in the middle of us writing this gitbook, DOTS NetCode was updated structurally, so we removed a "GhostCollection" workflow. The workflow would have still worked, but it would not serve the purpose of informing you of the latest-and-greatest. But, always: **trust the code snippet and instructions, not the gif!**

If you have any questions or comments at all, please [join our Discord](https://discord.com/invite/88j758eUvs) and let us know. If you are confused while coding along to this gitbook, there are probably also other people who are confused. Let's work together to make it more clear for everyone.&#x20;

## We create most of the external files in Visual Studio Code, our editor of choice

We find that creating files **outside** of Unity causes less errors with preview packages. Our editor of choice is Visual Studio Code.

One recurring issue we find is that when we create a file by right-clicking in Unity and selecting "Create," Unity sometimes has a tough time updating after the fact, which leads to it spitting out errors and then us needing to "Reimport All" assets.&#x20;

Accordingly, you'll notice in our gifs that the approach we take to create a file, is to generally (1) duplicate an existing file in VSCode, (2) rename it to the name described in the instructions, then (3) update it with the code snippet.

In the beginning of the gitbook we use the "right-click and select Create" approach to creating files to help illustrate different concepts, but again, it is error-prone, so that's why you'll see that later on in the gitbook we switch to almost exclusively creating new files outside of the Unity Editor.

## Poke around! Try stuff out!

Unity ECS is tough. It's hard to wrap your head around in the beginning, so we encourage you to run little experiments throughout the code-along (i.e. by changing Components or Systems) and seeing how the project reacts.

If you find yourself coding along with this gitbook and wondering "what would happen if I changed this to..." then go ahead and change it and see what happens!

Don't be scared to try and implement different things; we provide a link to the branch of the completed code state at the end of each page, so you should feel comfortable taking some risks without fear that you'll screw up your code.&#x20;


# Intro to Unity ECS

This gitbook explains "how" to implement ECS in Unity. It does not explain "what" ECS is, but we make sure to provide links to external resources throughout the gitbook that explain the "what."

## What you'll develop in this ECS section

![](/files/-MQ3aruTRJpt1D3aRJS3)

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Building-the-Project/>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

### Functionalities included

* Sub Scenes and conversions
  * Setting components and variables during conversion
* Using EntityManager to make structural changes
* Using command buffers to make structural entity changes
  * Setting up command buffers to run parallel jobs with CreateCommandBuffer().AsParallelWriter()&#x20;
* Adding, setting, and removing components
* Generating authoring components and IConvertGameObjectToEntity interfaces
* Entity queries and .ForEach()
  * Setting local variables for entity queries
  * Scheduling a job with .Schedule()
  * Scheduling a parallel job with .ScheduleParallel()
  * Running on the main thread with .Run()
  * Combining job handles with JobHandle.CombineDependencies()
* Create and destroy entities programmatically
* Create and move entities based on user input
* Create an entity from a prefab
  * Adding cameras to entities
* Hybrid ECS
* New Build Configuration system
  * Creating a base shared configuration
* Sub-second "play" start

## Entity Component System (ECS)

If you can spare **32 minutes** listen to [Far North Entertainment](https://www.youtube.com/watch?v=yTGhg905SCs) explain Unity's ECS and their hybrid approach **(A MUST-WATCH IF HAVE NEVER WORKED WITH ECS)**

If you can spare **64 minutes** listen to Blizzard's [Timothy Ford](https://www.youtube.com/watch?v=W3aieHjyNvw) explain general concepts in ECS (Not Unity-specific ECS, actually Blizzard's own game engine, but the architectural concepts are the same) **<-- ESPECIALLY RECOMMENDED FOR NETCODE**

If you can spare **42 minutes** listen to  Unity's 5-part series overview of ECS: [Part 1](https://www.youtube.com/watch?v=WLfhUKp2gag), [Part 2](https://www.youtube.com/watch?v=z9WE3fwre-k), [Part 3](https://www.youtube.com/watch?v=WZ6-LxwxWEI), [Part 4](https://www.youtube.com/watch?v=j2z5KRWZTDA\&t=1s). [Part 5](https://www.youtube.com/watch?v=D1KShj8ZV_I) for more explanations of ECS

If you can spare **23 minutes** listen to [Code Monkey](https://www.youtube.com/watch?v=ILfUuBLfzGI) explain ECS

If you can spare **9 minutes** listen to [Brackeys](< https://www.youtube.com/watch?v=_U9wRgQyy6s>) (RIP 💀) explain ECS

...and if you can spare **0 minutes** to understand ECS:

![So, yeah... Watch at least one of the videos. Top rec is the first one (Far North Entertainment)](/files/-MPjugO8Yh1_0221_VYq)

### **😃**☯ **Learning Unity ECS will be worth it! Look at these performance gains:**

![Screenshot from the Far North Entertainment video above.](/files/-MXIPDFahCv83nSOlxNH)

### 😳 ☯ Unity ECS will be painful at times (costs of ECS):

Sure, ECS is exciting, but from the Forum threads we linked to above, you can see that there is plenty of complexity that needs to be managed.&#x20;

Learning a functional programming paradigm can be difficult. Shifting out of a OOP mindset dominance can be difficult, too. ECS demands both, to some degree.&#x20;

Luckily, Unity's current ECS architecture is actually a hybrid (of both Monobehaviour and ECS), which eases you into the data-oriented paradigm if you are totally new to it.&#x20;

### Unity resources

{% hint style="info" %}
Read the high-level overview of how Unity thinks about and implements ECS: \
<https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/index.html>
{% endhint %}

{% hint style="info" %}
Read through the explanation of Unity Editor's ECS-specific windows\
<https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/editor-workflows.html>
{% endhint %}

Unity documentation for Unity.Entities 0.50.1-preview\.2:  <https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/>. **Refer to this for more information.**

Unity forums for DOTS: <https://forum.unity.com/forums/data-oriented-technology-stack.147/> . **Pretty responsive to questions.**

Unity samples for ECS: <https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/ECSSamples/ReleaseNotes.md> . **More in-depth and complicated examples.**

Big brain discussion on ECS vs. GameObjects: <https://forum.unity.com/threads/whats-your-opinion-on-ecs-video-by-bobby-anguelov.1029871/> . **If you are a bit more hardcore.**

### **To be best prepared for the code-alongs**

* [ ] Watch an ECS explanation video(s)
* [ ] Read through Unity's high-level overview
* [ ] Read through Unity's "Entity Debugger" explanation

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Create a Unity ECS Project

Full workflows and code to set up an ECS project with the latest Unity Editor and compatible package versions

## What you'll develop on this page

!["Authored" game settings data in a converted Sub Scene appears in the Entity Debugger](/files/-MPqk3WZsMzL_pKbn58m)

Basic game settings will be configured in a GameObject in a Sub Scene and can be viewed in the "Entity Inspector" at runtime.

Github branch link:  <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Setting-up-a-project-for-ECS>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Project setup

* Install **Unity Hub 3.1.2** if you do not have it already
  * <https://unity3d.com/get-unity/download>
* Install **Unity 2020 LTS** if you do not have it already
  * Go to "Installs" on the left side of Unity Hub pop-up window
  * Click "Add" and choose 2020 LTS
  * Once 2020 LTS is downloaded, click on the three vertical dots menu (kebab menu) in the top right corner to add platform modules depending on your development and target platforms (OSX/Linux/Windows)
  * Include iOS Build Support if you plan on completing the AR Foundation section of this gitbook
* Go to "Projects" in Unity Hub and click "New" in the top left corner of the window and select 2020 LTS and create a new 3D project, "Asteroids 3D XR Multiplayer"

![](/files/O5OLjgwVLUf9ESolVy6L)

{% hint style="info" %}
**Note For those on a Fedora system (does not apply to Mac or Windows)**

[From Giiba on our discord](https://discordapp.com/channels/793167347741491241/793167672489410560/987100951305408522):<br>

* Upon installing the burst compiler you might get a cascading error that began with `blah blah... DllNotFoundException: libdl.so ...blah blah`
* `/usr/lib64/libdl.so.2` and creating a symlink `ln -s libdl.so.2 libdl.so` solved the error
* Editor needs a restart after adding the symlink
  {% endhint %}

### Packages setup

* Once you're in Unity, navigate to Package Manager from "Window"
* Within PackageManager find the little "gear" icon and select Advanced Project Settings
* Check the box next to Enable Preview Packages and also Show Dependencies at the bottom of the window
  * This will allow you to view the preview packages that are included included in the package manifest
* Outside of the Unity Editor, navigate to your project folder (wherever you saved it locally) and open manifest.json (inside the Packages folder) with Visual Studio code or your choice of editor
  * These are all the packages that are currently included in your project
* We are now going to manually add a new package, the Hybrid Rendering package
* Add the following line to the manifest somewhere between the curly braces and save:

  ```
  "com.unity.rendering.hybrid": "0.50.0-preview.44",
  ```

![Enable preview packages and dependencies then add hybrid renderer package](/files/-MPpElHn3K6hmM-ZDT2N)

* When you click back into the Editor, Unity will notice the updated manifest and "pull" down the hybrid renderer package we added (you'll see the "Importing" window pop-up with a loading bar)
* If interested, read up to the "Hybrid Renderer" section [in this blog post](https://gametorrahod.com/game-object-conversion-and-subscene/) to learn what makes the Hybrid Renderer "hybrid"
  * Read the whole blog post if you want to be an ECS master, it is a great post!
* Hybrid Renderer automatically pulls in ECS dependencies (everything you need for ECS)
* You can see the dependencies of the package if you navigate back to the PackageManager in the Editor and select the Hybrid Renderer
* A key dependency is Entities, which brings in even more additional dependencies
  * Entities v0.50.1-preview\.2
    * Burst v1.6.4
    * Properties v1.7.0-preview
    * Properties UI v1.7.0-preview
    * Serialization v1.7.0-preview\.1
    * Collections v1.2.3
    * Mathematics v1.2.5
    * Asset Bundle v1.0.0
    * Unity Web Request v2.3.1-preview
    * Performance testing API v2.3.1-preview
    * Mono Cecil v1.10.1
    * Jobs v0.50.0-preview\.9
    * Scriptable Build Pipeline v1.19.2
    * Platform v0.50.0-preview\.4
    * Roslyn Compiler for Unity v0.2.1-preview
    * Unity Profiling Core API v1.0.0

![Overview of selecting Entities in the PackageManager](/files/x6gO9dlfUqviz9ul5xLC)

* Adding certain packages directly from the Package Manager window is no longer possible ([explanation in this post](https://forum.unity.com/threads/visibility-changes-for-preview-packages-in-2020-1.910880/))
* For the duration of this gitbook, we will almost exclusively be adding packages through the manifest
* Now we will add support for Universal Render Pipeline
  * [This error](https://forum.unity.com/threads/no-srp-present-no-compute-shader-support-or-running-with-nographics-hybrid-renderer-disabled.1256796/) is what happens if you do not add the Universal Render Pipeline (URP)
* We could have also chosen to add High Definition Render Pipeline (HDRP), but we want to support mobile devices
  * More discussion on difference between URP vs. HDRP can be [found here](https://forum.unity.com/threads/urp-vs-hdrp-for-performance.977790/)
* Add the following line to the manifest somewhere between the curly braces and save

```
"com.unity.render-pipelines.universal": "10.9.0",
```

{% hint style="success" %}
We now have an ECS project set up

* created a new project
* added hybrid renderer to the manifest
* added universal render pipeline support
  {% endhint %}

## Sub Scene setup

#### First, some background:

Unity has no plans to sunset GameObject/MonoBehaviour functionality given how incredibly powerful and mature their workflows are. That's why Unity built tools and workflows for developers to support GameObject workflows *while also* utilizing DOTS (a sorta 2-in-1, you can use both!).

One such workflow is the use of "**Sub Scenes**." Unity gives you the ability to split your scene into several Sub Scenes, which you use to partition various GameObjects and Entities. When a Sub Scene window is **open** (a.k.a. 'editable'), it operates in "GameObject world." When a Sub Scene window is **closed**, it operates in "Entity-performant world" (so to speak).&#x20;

How is this helpful? Here's an example: Visual artists can build GameObject assets in Sub Scenes, as long as they have the Sub Scene window open in Editor. When done editing, the visual artist closes the Sub Scene window, which triggers the conversion of these GameObjects into Entities. The Entities can be streamed in and out as needed. The conversion-to-entities allows for enormous environments (as demo'd in the [Megacity walkthrough](https://www.youtube.com/watch?v=j4rWfPyf-hk) below). If all those entities had been game objects, it would absolutely destroy the performance of a normal authoring scene.

This is how it works: This conversion of GameObjects into Entities within a Sub Scene takes place either when (1) you hit Play in Editor while the Sub Scene is open at the time, or when (2) you close the Sub Scene from its Inspector.&#x20;

The conversion process itself shouldn't take long at all, even for massive scale environments like the Megacity demo.

*External resources, if interested:*

Unity's Megacity walkthrough: <https://www.youtube.com/watch?v=j4rWfPyf-hk> **Watch this if you want a general understanding of the benefits of Sub Scenes.**

Unity's post about Sub Scene conversion workflows: <https://forum.unity.com/threads/new-subscene-converttoentity-workflows.638785/> **Read this if you want to be up to speed with the latest and greatest updates.**

Procedural generation of Sub Scenes: <https://forum.unity.com/threads/generate-sub-scenes-programmatically.868984/#post-6598072> **Read this if you are hardcore.**

#### **Now let's implement:**

* Right click on the Hierarchy window and choose "New Sub Scene" > "Empty Scene"
* Name this Scene "ConvertedSubScene"
  * This will automatically create a new folder with the parent scene name
  * This will also automatically create a new folder called "SceneDependencyCache" which is used by Unity to help load/unload Sub Scenes
* Select "ConvertedSubScene" in Hierarchy
* Check out the settings in the Inspector
  * "Auto Load Scene" is selected true by default, which means this Sub Scene will automatically load its Entities when the scene is loaded
* Double click on "ConvertedSubScene" in Assets/Scenes/SampleScene and hit the play button
  * Notice there are no cameras in the Sub Scene so nothing renders
* Return to "SampleScene" in Assets/Scenes and hit the play button
  * Notice that the skybox renders because a camera is present

![Adding a Sub Scene to the scene and naming it "ConvertedSubScene"](/files/-MPq6dlZivwabs-srPf5)

![](/files/-MPugV5fdBlEc-YTm4VJ)

You can see how Unity is able to handle both GameObjects and Entities at runtime without adding a single Entity, Component, or System. The GameObject camera functions as expected even with a Sub Scene loaded into the scene.

Unity has release DOTS-specific Editor windows to help developers manage their DOTS projects. The windows available can be found in "Window" > "DOTS".

![DOTS specific Editor views to assist in development](/files/kYt6ikTnznfDM4CA9l9S)

* Let's combine all these views into a single view that has tabs for DOTS Hierarchy, Systems, Components, and Archetypes
  * We will use these windows to see:
    * Entities (hexagon icon)
    * Components (puzzle piece icon)
    * Systems (hexagon with arrows icon)
    * As well as "Archetypes" (types of entities) (hexagon with interior edges icon)

![Our DOTS Windows](/files/QOWjmsbXPZAvQ0BsBrfE)

If you are confused by Worlds and Systems read the section on ["System organization" at the bottom of the link here](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_core.html).

* Let's checkout the amount of entities we currently have in our project by navigating to DOTS Hierarchy by clicking on the DOTS Hierarchy tab
  * You should see 3 entities (while the Sub Scene is "open")

![Total entities while the Sub Scene is "open" (open Sub Scenes do not convert the GameObjects contained within the Sub Scene)](/files/m2LBIfGiUNCp3ppjeE1u)

* Now let's close the Sub Scene by hitting the "close" button in the inspector when the ConvertedSubScene is selected in the Hierarchy
* You Should now see 5 entities

![](/files/F1ZjlCoSQ0yAE3RD8hbb)

* Click through the 5 entities and notice how their components and values are shown in the Editor Inspector window
* Think of the current 5 entities as autogenerated Unity.Entities "boiler plate" for our current scene / SubScene setup

We now are able to navigate Entities, Systems, Archetypes and see their associated data in the Inspector.

{% hint style="success" %}
We now have a Sub Scene loading into our scene

* We created a Sub Scene "ConvertedSubScene" in the Hierarchy
* Created a "DOTS Window" containing the new DOTS Editor Views
* Saw how the number of entities changes when a Sub Scene is open vs. closed because of the Sub Scene conversion work flow
  {% endhint %}

## Initializing game settings

#### First, some background:

> ####
>
> ## Conversion Workflow <a href="#conversion-workflow" id="conversion-workflow"></a>
>
> To use Unity’s DOTS technology, you need to create entities, components and systems.
>
> The generation process that consumes GameObjects (authoring data) and generates entities and components (runtime data) is called *conversion*.
>
> * This process is the preferred way of authoring ECS data
> * It is a fundamental part of DOTS, and not something temporary
> * Conversion is only about data, there is no conversion process for code
>
> The overall workflow looks like this:
>
> 1. The Unity Editor is a user interface to work with authoring data
> 2. The conversion from authoring data to runtime data happens in the Unity Editor
> 3. The runtime (e.g. the game) should only ever have to deal with runtime data
>
> ### Fundamental principles <a href="#fundamental-principles" id="fundamental-principles"></a>
>
> Authoring data and runtime data are optimized for wildly different goals.
>
> * Authoring data is optimized for flexibility
>   * Human understandability and editability
>   * Version control (mergeability, no duplication)
>   * Teamwork organization
> * Runtime data is optimized for performance
>   * Cache efficiency
>   * Loading time and streaming
>   * Distribution size
>
> A key observation is that nothing requires a 1:1 mapping between GameObjects and entities.
>
> * A single GameObject can turn into a set of entities, e.g. procedural generation
> * Multiple GameObjects can be aggregated into a single entity, e.g. LOD baking
> * Some GameObjects might have no purpose at runtime, e.g. level editing markers
>
> The same can be said about components. A conversion system can read from any amount of Unity components and add any amount of ECS components to any amount of entities.
>
> ### Key concepts <a href="#key-concepts" id="key-concepts"></a>
>
> All those concepts get explained in further detail in the rest of this document, but it's useful to introduce some vocabulary beforehand.
>
> * **Authoring scene**\
>   A regular Unity scene, containing GameObjects, destined to be converted to runtime data.
> * **Subscene**\
>   A simple GameObject component that references an authoring scene and will either load the authoring scene (when the Subscene is in edit mode), or stream in the converted entity scene (when the Subscene is closed).
> * **Entity scene**\
>   The result of converting an authoring scene. Because entity scenes are the output of an asset import, they are stored in the Library folder. Entity scenes can be made of multiple sections, and each of those can be independently loaded.
> * **LiveConversion**\
>   When an authoring scene is loaded as GameObjects for editing, every change will trigger an update to the entity scene, making it look as if the entity scene was directly edited, we call this process *LiveConversion*.
>
> From [Unity's ECS Conversion Workflow documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/conversion.html)

{% hint style="info" %}

#### 🔑 Major Key Alert 🔑

It is worth the effort to read through the [Conversion Workflow documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/conversion.html) and wrap your head around Authoring/Conversion as it is a key part of ECS development
{% endhint %}

Unity's Talk on conversion workflows: <https://www.youtube.com/watch?v=TdlhTrq1oYk> **Watch this if you want a more in-depth explanation (and/or prefer videos).**

#### Now let's implement:

* Create a new folder called "Scripts and Prefabs" in the "Assets" folder within the Project window
* Navigate to the "Scripts and Prefabs" folder, right click, choose "Create" > "ECS" > "Runtime Component Type", and name it "GameSettingsComponent"
  * This is the "Component" from Entity **Component** System
  * The Component type is an interface called [IComponentData (link to Unity docs)](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.IComponentData.html)
* Copy the below code snippet into GameSettingsComponent.cs:

```
using Unity.Entities;

public struct GameSettingsComponent : IComponentData
{
    public float asteroidVelocity;
    public float playerForce;
    public float bulletVelocity;
    public int numAsteroids;
    public int levelWidth;
    public int levelHeight;
    public int levelDepth;
}
```

> Make sure to first clear the file before pasting in this code snippet.&#x20;

* Components cannot store data pre-runtime. So, to set game settings in the Editor ("authoring"), we will need to use the conversion workflow
  * ~~public float asteroidVelocity = 10f;~~ (cannot set values in IComponentData)

![Create Scripts and Prefabs folder and GameSettingsComponent.cs](/files/-MPqZxz1jDOrbNPxtRtI)

* Navigate to "ConvertedSubScene" and create a new Empty GameObject named "GameSettings"
  * This GameObject will hold a script that will allow us to "author" game settings in the Editor and have those values converted at runtime into the GameSettingsComponent
* In "Scripts and Prefabs", right click, choose "Create" > "ECS" > "Authoring Component Type", and create "SetGameSettingsSystem"
  * "Authoring" will allow us to add data in the Editor before runtime
* SetGameSettingsSystem.cs

```
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

public class SetGameSettingsSystem : UnityEngine.MonoBehaviour, IConvertGameObjectToEntity
{
    public float asteroidVelocity = 10f;
    public float playerForce = 50f;
    public float bulletVelocity = 500f;

    public int numAsteroids = 200;
    public int levelWidth = 2048;
    public int levelHeight = 2048;
    public int levelDepth = 2048;
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        var settings = default(GameSettingsComponent);

        settings.asteroidVelocity = asteroidVelocity;
        settings.playerForce = playerForce;
        settings.bulletVelocity = bulletVelocity;

        settings.numAsteroids = numAsteroids;
        settings.levelWidth = levelWidth;
        settings.levelHeight = levelHeight;
        settings.levelDepth = levelDepth;
        dstManager.AddComponentData(entity, settings);
    }
}
```

* If you ever want to set data before runtime in the Editor ("authoring") and have it exist at runtime as Component data for ECS to use, you will use a conversion workflow similar to above:
  \*
  1. Create the [IComponentData](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_components.html) that you will need
  2. Create an IConvertGameObjectToEntity ([also an Interface, link to Unity docs](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.IConvertGameObjectToEntity.html)) with public fields that match IComponentData
  3. Within the IConvertGameObjectToEntity use Convert() to take the MonoBehaviour data and set it to a Component during the conversion process
  * On the next page we show how to use `[GenerateAuthoringComponent]` to add IComponentData directly on GameObjects
* Click on the "GameSettings" object in the Hierarchy, then in the Inspector click "Add Component" and select SetGameSettingsSystem to add the MonoBehaviour to the GameObject
* Make sure it's added and then navigate back to "SampleScene"

![Create the SetGameSettingsSystem and add it to GameSettings GameObject](/files/-MPqapRBp8uVmQvGJE0t)

* Select "ConvertedSubScene" from the Hierarchy (make sure it's selected) and then click "Reimport" in Inspector
* Navigate to the DOTS Hierarchy window and notice a new Entity with a GameSettingsComponent

![New entity created from our GameSettings GameObject in ConvertedSubScene](/files/7ofVNmZ9kHGvB33KlhUE)

* The new Entity with a GameSettingComponent can also be seen via a new EntityArchetype

![](/files/RxyixwAP5xS9P3Q9QBYk)

Nice! The Sub Scene has followed the Convert() process and we have added the GameSettingsComponent to the converted GameSettings GameObject

{% hint style="info" %}
Remember, the GameSettings GameObject in our ConvertedSubScene automatically gets converted because it is a GameObject **in a Sub Scene**.&#x20;
{% endhint %}

Our SetGameSettingsSystem set on that GameObject has "Convert()" triggered when going through the conversion workflow, which:

1. takes the new Entity (which *used* to be the GameSettings GameObject) and&#x20;
2. adds a GameSettingsComponent to it, then:
3. sets the GameSettingsComponent data to the data set in the Editor

...which results in an Entity with a GameSettingsComponent. We will use this Entity in the next section.

{% hint style="info" %}
**A different way to get GameObject data into an ECS Component**

In the latest ECS release GameObjectConversionSystem can also look for classic Unity components. So you can add data to a GameObject through a normal MonoBehavior component, and then "search" for that MonoBehavior component in an EntityQuery.

Here's the "hello world" of conversion systems, that does a 1:1 conversion of all authoring components of a certain type to their ECS equivalent.

```cs
// Authoring component
class FooAuthoring : MonoBehaviour
{
    public float Value;
}

// Runtime component
struct Foo : IComponentData
{
    public float SquaredValue;
}

// Conversion system, running in the conversion world
class FooConversion : GameObjectConversionSystem
{
    protected override void OnUpdate()
    {
        // Iterate over all authoring components of type FooAuthoring
        Entities.ForEach((FooAuthoring input) =>
        {
            // Get the destination world entity associated with the authoring GameObject
            var entity = GetPrimaryEntity(input);

            // Do the conversion and add the ECS component
            DstEntityManager.AddComponentData(entity, new Foo
            {
                SquaredValue = input.Value * input.Value
            });
        });
    }
}
```

In a `GameObjectConversionSystem`, `ForEach` will not create jobs. It runs on the main thread, without Burst, and this allows accessing classic Unity without restraint. This is also why it doesn't require a call to `.Run()` or `.Schedule()`.

Also note that the entity query looks for classic Unity components, in this case `FooAuthoring` that derives from `MonoBehaviour`. Since those are reference types, they do not require `ref` or `in`.

From [Conversion systems 101 in Unity Docs](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/conversion.html#conversion-systems-101)
{% endhint %}

{% hint style="success" %}
We now have "authored" data on a GameObject that's been converted to Component data on an Entity

* We created GameSettingsComponent.cs as our I**C**omponentData the ("C" in "ECS")
* We created SetGameSettingsSystem.cs which uses Unity's Sub Scene Conversion Workflow
* We created GameSettings GameObject in ConvertedSubScene and added SetGameSettings as a Component
* We navigated to our DOTS Editor windows to see that the new Entity and its GameSettingsComponent have been added to the Entity list
  {% endhint %}

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Setting-up-a-Project-for-ECS>

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Setting-up-a-Project-for-ECS'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Spawn and Move Prefabs

Full workflows and code to programmatically spawn, update, and destroy prefab entities

## What you'll develop on this page

![Preview of result of this section](/files/-MPw5oeiH42Sd4vtBX3q)

Use game settings to programmatically spawn asteroid prefabs to create an asteroid field (cube).

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-Updating-and-Destroying-Asteroids>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Spawning asteroid prefabs

#### First, some background

> An entity prefab is nothing more than an entity with a `Prefab` tag and a `LinkedEntityGroup`. The former identifies the prefab and makes it invisible to all entity queries but the ones who explicitly include prefabs, and the latter links together a set of entities, since entity prefabs can be complex assemblies (equivalent to GameObject hierarchies).
>
> So the following two components are equivalent, one in classic Unity and the other in DOTS.
>
> ```cs
> // Authoring component
> public class PrefabReference : MonoBehaviour
> {
>     public GameObject Prefab;
> }
>
> // Runtime component
> public struct PrefabEntityReference : IComponentData
> {
>     public Entity Prefab;
> }
> ```
>
> By default, the conversion workflow only processes the actual contents of an authoring scene, so a specific mechanism is required to also include prefabs from the asset folder. This is the purpose of the system group `GameObjectDeclareReferencedObjectsGroup`, it runs before the primary entities are created in the destination world, and provides a way of registering prefabs for conversion.
>
> From [ECS Conversion Workflow documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/conversion.html#prefabs) "Prefabs" section

#### Setting up a prefab with ECS

* Right click in the Hierarchy, choose "3D Object" > "Sphere"
* Drag the GameObject into the Scripts and Prefabs folder then delete the GameObject from the hierarchy (right click Delete is better then hitting the delete key)
* Click on the sphere in the Project window, then click "Open Prefab" in the Inspector
* Rename it to "Asteroid"
* Adjust the scale to (2,2,2)
* Remove the "Sphere Collider" component (click three vertical dots menu to find Remove)
  * This is a MonoBehaviour collider, and in the next section, DOTS Physics, we will replace this collider with DOTS collider
* In Scripts and Prefabs Create "AsteroidTag" runtime component (Create > ECS > Runtime Component Type)
  * This will be IComponentData but will not contain any data within it
  * [When creating components with no data, the convention is to append the name with "Tag" to it make clear](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_components.html#tag-components)
    * Although this isn't necessary, it tends to help you remember how components are meant to be used
* AsteroidTag.cs

```
using Unity.Entities;

public struct AsteroidTag : IComponentData
{
}
```

![Create the asteroid prefab and create the AsteroidTag component. Deleted the asteroid from the scene Hierarchy post-gif](/files/-MPtn_1_2cU3ZvsZ2l0R)

* Try to put the AsteroidTag on the asteroid prefab

![Trying to put the AsteroidTag on the Asteroid prefab causes an error](/files/-MPtnj5ad51IR3FoJbQd)

* Trying to put the AsteroidTag on the asteroid prefab causes an error: "Can't add script behavior to AsteroidTag. The script needs to derive from MonoBehaviour."
  * Because the Editor is not ECS, you cannot add IComponentData to a prefab before runtime
  * Even though it "seems" like you should, it is important to remember that the Asteroid is currently a GameObject and so putting a runtime ECS component on it actually doesn't make sense, what we need to do is make AsteroidTag an "authoring component"

> For simple runtime components, the `GenerateAuthoringComponent` attribute can be used to request the automatic creation of an authoring component for a runtime component. You can then add the script containing the runtime component directly to a GameObject within the Editor.
>
> From [ECS Conversion Workflow documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/conversion.html) "Generated authoring components" section

* Add a \[GenerateAuthoringComponent] decorator to the AsteroidTag

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct AsteroidTag : IComponentData
{
}
```

* Now try to put the AsteroidTag on the Asteroid prefab again (select Asteroid in Hierarchy > Click "Add Component" in the Inspector)

![After adding \[GenerateAuthoringComponent\] decorator, you are able to add the AsteroidTag to prefab](/files/-MPtrAll61puCQFXTe18)

* Now we have an Asteroid entity with an AsteroidTag authoring component. This "tag" enables us to "find" asteroid entities, by checking whether or not entities have an AsteroidTag component

How do we "refer" to the "asteroid" prefab when we want to create asteroids? We need a way to "reference" this prefab when writing our ECS.

* Create an empty GameObject in ConvertedSubScene named PrefabCollection
* This GameObject will hold all the references to our prefabs
* When the SubScene converts this GameObject into an entity, we will refer to the PrefabCollection entity's components to reference our prefabs
* Create AsteroidAuthoringComponent (right click Scripts and Prefabs > Create > ECS > Authoring Component Type)

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct AsteroidAuthoringComponent : IComponentData
{
    public Entity Prefab;
}
```

* Go back and select the PrefabCollection GameObject in Hierarchy and add AsteroidAuthoringComponent (click Add Component in Inspector)
* Drag the asteroid prefab from the Project window into the public Prefab field under the script component in Inspector
* Save the SubScene then click "Reimport" in Inspector to make sure assets are reimported into SubScene
* We now have a reference to our asteroid prefab

![Create Prefab collection and add AsteroidAuthoringComponent to it](/files/-MPuidjWf9jIBFVLn3NB)

* We will be using the following workflow to reference prefabs for the remainder of this gitbook:
  * Create a "PrefabAuthoringComponent" with an Entity field
  * Add it to our PrefabCollection GameObject in our SubScene
  * Drag our prefab from the project window into the Entity field of the authoring component
  * We will be able to reference the authoring component to instantiate our prefabs

#### Creating a spawning system

* Navigate to "Scripts and Prefabs", right click and select Create > ECS >  System and name the system "AsteroidSpawnSystem"
  * "System" (the **S** in EC**S**)
* The latest update creates a "bad" system (Unity has not updated the auto-generated systems yet) that is missing the "partial" keyword
  * In AsteroidSpawnSystem change `public class AsteroidSpawnSystem : SystemBase` to `public partial class AsteroidSpawSystem : SystemBase`

> Unity ECS automatically discovers system classes in your project and instantiates them at runtime. It adds each discovered system to one of the default system groups. You can use [system attributes](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/system_update_order.html#attributes) to specify the parent group of a system and the order of that system within the group . If you do not specify a parent, Unity adds the system to the Simulation system group of the default world in a deterministic, but unspecified, order. You can also use an attribute to disable automatic creation.
>
> A system's update loop is driven by its parent [ComponentSystemGroup](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/system_update_order.html). A ComponentSystemGroup is, itself, a specialized kind of system that is responsible for updating its child systems. Groups can be nested. Systems derive their [time](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Core.TimeData.html) data from the [World](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.World.html) they are running in; time is updated by the [UpdateWorldTimeSystem](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.UpdateWorldTimeSystem.html).<br>
>
> From [ECS Systems documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_systems.html)

* Hit the "play" button and check out the Systems window in our DOTS Windows
  * Our AsteroidSpawnSystem is automatically placed into the Simulation System Group
    * We are able to change which SystemGroup our system runs in by adding decorators to our system
  * Click on the AsteroidSpawnSystem and you will see that our system interacts with 2 components, Rotation and Translation
    * These are default components in the boilerplate system that are used by the EntityQuery (the EntityQuery "checks" for these components)

![AsteroidSpawnSystem being automatically placed in SimulationSystemGroup](/files/ukdlUPOiTj5IUJOWwiRc)

* AsteroidSpawnSystem.cs 's boilerplate code is below
  * We can see from the boilerplate code that there is a reference to "Translation" and "Rotation" which was picked up in the Debugger

```
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
public partial class AsteroidSpawnSystem : SystemBase
{
    protected override void OnUpdate()
    {
        // Assign values to local variables captured in your job here, so that it has
        // everything it needs to do its work when it runs later.
        // For example,
        //     float deltaTime = Time.DeltaTime;
        // This declares a new kind of job, which is a unit of work to do.
        // The job is declared as an Entities.ForEach with the target components as parameters,
        // meaning it will process all entities in the world that have both
        // Translation and Rotation components. Change it to process the component
        // types you want.
        Entities.ForEach((ref Translation translation, in Rotation rotation) => {
            // Implement the work to perform for each entity here.
            // You should only access data that is local or that is a
            // field on this job. Note that the 'rotation' parameter is
            // marked as 'in', which means it cannot be modified,
            // but allows this job to run in parallel with other jobs
            // that want to read Rotation component data.
            // For example,
            //     translation.Value += math.mul(rotation.Value, new float3(0, 0, 1)) * deltaTime;
        }).Schedule();
    }
}
```

> Unity ECS provides several types of systems. In general, the systems you write to implement your game behavior and data transformations will extend [SystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.html). The other system classes have specialized purposes. You typically use existing instances of the [EntityCommandBufferSystem](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/entity_command_buffer.html) and [ComponentSystemGroup](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/system_update_order.html) classes.
>
> * [SystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.html) -- the base class to implement when creating systems.
> * [EntityCommandBufferSystem](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/entity_command_buffer.html) -- provides [EntityCommandBuffer](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityCommandBuffer.html) instances for other systems. Each of the default system groups maintains an Entity Command Buffer System at the beginning and end of its list of child systems. This allows you to group structural changes so that they incur fewer [synchronization points](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/sync_points.html) in a frame.
> * [ComponentSystemGroup](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/system_update_order.html) -- provides nested organization and update order for other systems. Unity ECS creates several Component System Groups by default.
> * [GameObjectConversionSystem](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/gp_overview.html) -- converts GameObject-based, in-Editor representations of your game to efficient, entity-based, runtime representations. Game conversion systems run in the Unity Editor.
>
> From [ECS Systems documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_systems.html)

Let's briefly take a look at the system types referenced in the documentation.

**SystemBase** - this is the type of system we will be using most of the time for our runtime game play. The systems of this type will do things like provide movement to asteroids or Spawn/Destroy.

**ComponentSystemGroup** - this is groupings of systems.

![From https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/system\_update\_order.html](/files/FMqUJ0HTFYEKDwrHNmkq)

**GameObjectConversionSystem** - We interacted with systems of this type when working with our GameSettingsComponent and our SetGameSettingsSystem. These are used for hybrid development.

**EntityCommandBufferSystem** - So far we have not used this type of system. The systems in EntityCommandBufferSystem are used as "sync" points for structural changes made to entities. You can see these systems in the "Default System Groups" image above. There is a Begin{SystemGroup}EntityCommandBufferSystem and End{SystemGroup}EntityCommandBufferSystem for each of the 3 main default system groups (InitializationSystemGroup, SimulationSystemGroup, and PresentationSystemGroup). If confused about "structural" changes it is good to refer to [Unity's overview of ECS concepts](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_core.html).

An Entity is a collection of components. Let's say there is an asteroid with a Translation component which is a float3 and denotes the asteroid's position in space. Updating the Entity's Translation component values is **not** a structural change. The Entity still has the same amount and types of components, just different values.

> A unique combination of component types is called an [EntityArchetype](https://docs.unity3d.com/Packages/com.unity.entities@0.16/api/Unity.Entities.EntityArchetype.html). For example, a 3D object might have a component for its world transform, one for its linear movement, one for rotation, and one for its visual representation. Each instance of one of these 3D objects corresponds to a single entity, but because they share the same set of components, ECS classifies them as a single archetype:
>
> <img src="https://docs.unity3d.com/Packages/com.unity.entities@0.16/manual/images/ArchetypeDiagram.png" alt="" data-size="original">
>
> In this diagram, entities A and B share archetype M, while entity C has archetype N.
>
> To smoothly change the archetype of an entity, you can add or remove components at runtime. For example, if you remove the `Renderer` component from entity B, it then moves to archetype N.
>
> From [Core ECS Overview](https://docs.unity3d.com/Packages/com.unity.entities@0.16/manual/ecs_core.html#archetypes)

Let's say we add a "DestroyTag" to the asteroid Entity. That will actually "change" the EntityArchetype it is (because it has a different collection of components). Structural changes are computationally more expensive than just updating values.

> Sync points are caused by operations that you cannot safely perform when there are any other jobs that operate on components. Structural changes to the data in ECS are the primary cause of sync points. All of the following are structural changes:
>
> * Creating entities
> * Deleting entities
> * Adding components to an entity
> * Removing components from an entity
> * Changing the value of shared components
>
> Broadly speaking, any operation that changes the archetype of an entity or causes the order of entities within a chunk to change is a structural change. These structural changes can only be performed on the main thread.
>
> Structural changes not only require a sync point, but they also invalidate all direct references to any component data. This includes instances of [DynamicBuffer](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.DynamicBuffer-1.html) and the result of methods that provide direct access to the components such as [ComponentSystemBase.GetComponentDataFromEntity](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.ComponentSystemBase.GetComponentDataFromEntity.html).
>
> You can use [entity command buffers](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/entity_command_buffer.html) (ECBs) to queue up structural changes instead of immediately performing them. Commands stored in an ECB can be played back at a later point during the frame. This reduces multiple sync points spread across the frame to a single sync point when the ECB is played back.
>
> Each of the standard [ComponentSystemGroup](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.ComponentSystemGroup.html) instances provides a [EntityCommandBufferSystem](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityCommandBuffer.html) as the first and last systems updated in the group. By getting an [ECB](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityCommandBuffer.html) object from one of these standard ECB systems, all structural changes within the group occur at the same point in the frame, resulting in one sync point rather than several. ECBs also allow you to record structural changes within a job. Without an ECB, you can only make structural changes on the main thread. (Even on the main thread, it is typically faster to record commands in an ECB and then play back those commands, than it is to make the structural changes one-by-one using the [EntityManager](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityManager.html) class itself.)
>
> If you cannot use an [EntityCommandBufferSystem](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityCommandBuffer.html) for a task, try to group any systems that make structural changes together in the system execution order. Two systems that both make structural changes only incur one sync point if they update sequentially.
>
> From [ECS Sync points](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/sync_points.html)

{% hint style="info" %}
If sync points and EntityCommandBuffers are making your head hurt, you should really watch Unity's talk on Entity Command Buffers: <https://www.youtube.com/watch?v=SecJibpoTYw> **<--** **Worth the watch!**
{% endhint %}

We will be using the BeginSimulationEntityCommandBuffer in our AsteroidSpawnSystem. This command buffer plays back our structural changes (creating asteroid entities).

We will use a Job.WithCode() to generate our asteroids.

> The [Job.WithCode](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Job.html#Unity_Entities_SystemBase_Job) construction provided by the [SystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.html) class is an easy way to run a function as a single background job. You can also run [Job.WithCode](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Job.html#Unity_Entities_SystemBase_Job) on the main thread and still take advantage of [Burst](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html) compilation to speed up execution.
>
> You cannot pass parameters to the [Job.WithCode](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Job.html#Unity_Entities_SystemBase_Job) lambda function or return a value. Instead, you can capture local variables in your [OnUpdate()](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.OnUpdate.html) function.
>
> When you schedule your job to run in the [C# Job System](https://docs.unity3d.com/Manual/JobSystem.html) using `Schedule()`, there are additional restrictions:
>
> * Captured variables must be declared as [NativeArray](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html) -- or other [native container](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html) -- or a [blittable](https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types) type.
> * To return data, you must write the return value to a captured [native array](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html), even if the data is a single value. (Note that you can write to any captured variable when executing with `Run()`.)
>
> [Job.WithCode](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Job.html#Unity_Entities_SystemBase_Job) provides a set of functions to apply read-only and safety attributes to your captured [native container](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html) variables. For example, you can use `WithReadOnly` to designate that you don't update the container and `WithDisposeOnCompletion` to automatically dispose a container after the job finishes. ([Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities) provides the same functions.)
>
> See [Job.WithCode](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Job.html#Unity_Entities_SystemBase_Job) for more information about these modifiers and attributes.
>
> From [ECS Using Job.WithCode documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_job_withcode.html)

Think of Job.WithCode as a super-hardcore ECS "for loop" in an Update() function.

* Make AsteroidSpawnSystem and paste this code snippet into AsteroidSpawnSystem.cs:

```
using System.Diagnostics;
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;

public partial class AsteroidSpawnSystem : SystemBase
{
    //This will be our query for Asteroids
    private EntityQuery m_AsteroidQuery;

    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimECB;

    //This will be our query to find GameSettingsComponent data to know how many and where to spawn Asteroids
    private EntityQuery m_GameSettingsQuery;

    //This will save our Asteroid prefab to be used to spawn Asteroids
    private Entity m_Prefab;

    protected override void OnCreate()
    {
        //This is an EntityQuery for our Asteroids, they must have an AsteroidTag
        m_AsteroidQuery = GetEntityQuery(ComponentType.ReadWrite<AsteroidTag>());

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimECB = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //This is an EntityQuery for the GameSettingsComponent which will drive how many Asteroids we spawn
        m_GameSettingsQuery = GetEntityQuery(ComponentType.ReadWrite<GameSettingsComponent>());

        //This says "do not go to the OnUpdate method until an entity exists that meets this query"
        //We are using GameObjectConversion to create our GameSettingsComponent so we need to make sure 
        //The conversion process is complete before continuing
        RequireForUpdate(m_GameSettingsQuery);
    }
    
    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_Prefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's AsteroidAuthoringComponent
            //and set m_Prefab to its Prefab value
            m_Prefab = GetSingleton<AsteroidAuthoringComponent>().Prefab;

            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        //Because of how ECS works we must declare local variables that will be used within the job
        //You cannot "GetSingleton<GameSettingsComponent>()" from within the job, must be declared outside
        var settings = GetSingleton<GameSettingsComponent>();

        //Here we create our commandBuffer where we will "record" our structural changes (creating an Asteroid)
        var commandBuffer = m_BeginSimECB.CreateCommandBuffer();

        //This provides the current amount of Asteroids in the EntityQuery
        var count = m_AsteroidQuery.CalculateEntityCountWithoutFiltering();

        //We must declare our prefab as a local variable (ECS funny business)
        var asteroidPrefab = m_Prefab;

        //We will use this to generate random positions
        var rand = new Unity.Mathematics.Random((uint)Stopwatch.GetTimestamp());

        Job
        .WithCode(() => {
            for (int i = count; i < settings.numAsteroids; ++i)
            {
                // this is how much within perimeter asteroids start
                var padding = 0.1f;

                // we are going to have the asteroids start on the perimeter of the level
                // choose the x, y, z coordinate of perimeter
                // so the x value must be from negative levelWidth/2 to positive levelWidth/2 (within padding)
                var xPosition = rand.NextFloat(-1f*((settings.levelWidth)/2-padding), (settings.levelWidth)/2-padding);
                // so the y value must be from negative levelHeight/2 to positive levelHeight/2 (within padding)
                var yPosition = rand.NextFloat(-1f*((settings.levelHeight)/2-padding), (settings.levelHeight)/2-padding);
                // so the z value must be from negative levelDepth/2 to positive levelDepth/2 (within padding)
                var zPosition = rand.NextFloat(-1f*((settings.levelDepth)/2-padding), (settings.levelDepth)/2-padding);
                
                //We now have xPosition, yPostiion, zPosition in the necessary range
                //With "chooseFace" we will decide which face of the cube the Asteroid will spawn on
                var chooseFace = rand.NextFloat(0,6);
                
                //Based on what face was chosen, we x, y or z to a perimeter value
                //(not important to learn ECS, just a way to make an interesting prespawned shape)
                if (chooseFace < 1) {xPosition = -1*((settings.levelWidth)/2-padding);}
                else if (chooseFace < 2) {xPosition = (settings.levelWidth)/2-padding;}
                else if (chooseFace < 3) {yPosition = -1*((settings.levelHeight)/2-padding);}
                else if (chooseFace < 4) {yPosition = (settings.levelHeight)/2-padding;}
                else if (chooseFace < 5) {zPosition = -1*((settings.levelDepth)/2-padding);}
                else if (chooseFace < 6) {zPosition = (settings.levelDepth)/2-padding;}

                //we then create a new translation component with the randomly generated x, y, and z values                
                var pos = new Translation{Value = new float3(xPosition, yPosition, zPosition)};

                //on our command buffer we record creating an entity from our Asteroid prefab
                var e = commandBuffer.Instantiate(asteroidPrefab);

                //we then set the Translation component of the Asteroid prefab equal to our new translation component
                commandBuffer.SetComponent(e, pos);
            }
        }).Schedule();

        //This will add our dependency to be played back on the BeginSimulationEntityCommandBuffer
        m_BeginSimECB.AddJobHandleForProducer(Dependency);
    }
}
```

* Read through the comments to get a line-by-line understanding of what is going on

{% hint style="info" %}
**Important Note:** A quirk of ECS is that you must declare local variables in the OnUpdate method if you want to use that data in a Job that runs in OnUpdate().

This means that if you want to use certain variables in your OnUpdate(), and you already declared them in your OnCreate(), you must **still** also declare variables locally in OnUpdate().

Is there any point to declaring variables in OnCreate()? Yes, there is! If you grab the variable from OnCreate(), you only reach "out" once during runtime. But, if you are calling for the value from OnUpdate(), that means you reach "out" for the local variable on each OnUpdate().
{% endhint %}

* You'll notice the general set-up of an ECS system is broken down into two major parts:
  * **OnCreate**
    * EntityQueries, CommandBuffers, RequireForUpdates
  * **OnUpdate**
    * Setup your local variables
    * Run your Job.WithCode() or Entities.ForEach()

**OnCreate**

* Here we set up EntityQueries on the entities and components we need
  * We will need to know how many Asteroids exist so we can spawn the right amount
  * We need data from our GameSettingsComponent, so we create a query and see if it exists before we continue to our OnUpdate
* Here we also set up our EntityCommandBuffer which will be used to record structural changes

**OnUpdate**

* Here we set our prefab by grabbing the data from our AsteroidAuthoringComponent
* We declare the local variables we will need for our Job.WithCode()
* We create a Translation that is on the perimeter of a cube defined by our levelHeight, levelWidth, and levelDepth in our GameSettingsComponent
* We instantiate a new entity from our prefab and set its Translation component to our translation

{% hint style="info" %}
**Important note:** The reason why we are able to easily pass our GameSettingsComponent data into the Job.WithCode() is because the entity with that data is a **Singleton.** A Singleton is an entity that is the only entity that exists with this particular component.&#x20;

What if we wanted to pass in PlayerComponent data to the Job.WithCode() and there could be multiple players active during the OnUpdate()?

In this case you would use:

`m_PlayersQuery = GetEntityQuery(ComponentType.ReadWrite<PlayerComponent>());`\
`JobHandle playersDep;`\
`var players = m_PlayersQuery.ToComponentDataArrayAsync(Allocator.TempJob, out playersDep);`

* This creates a NativeArray of Player component data
* This array can now be referenced within a Job
* Now there is an additional dependency to create that NativeArray which also must be disposed, this is where it gets tricky
* You will need to [combine dependencies](https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_job_dependencies.html) to ensure there are no race conditions

Using NativeArrays will be discussed in future sections. So why are we confusing you by bringing up this additional information that isn't needed right now? That's because introducing future workflows to you now might help you build a better mental model of how to navigate ECS.
{% endhint %}

{% hint style="info" %}
You might have noticed our AsteroidSpawnSystem ends with a .Schedule() which means [we are running a a single job](https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_job_withcode.html). A great benefit of Unity DOTS is the ability to schedule parallel jobs to make the most of the targeting compute platform. If we wanted to make AsteroidSpawnSystem run parallel jobs we would need to use an IJobFor.

**Note:** To run a parallel job, implement [IJobFor](https://docs.unity3d.com/2020.1/Documentation/Manual/JobSystemCreatingJobs.html), which you can schedule using [ScheduleParallel()](https://docs.unity3d.com/2020.1/Documentation/ScriptReference/Unity.Jobs.IJobForExtensions.ScheduleParallel.html) in the system [OnUpdate()](https://docs.unity3d.com/Packages/com.unity.entities@0.17/api/Unity.Entities.SystemBase.html) function.

We do not use an IJobFor here because it is a bit more involved than a Job.WithCode(), and we are just starting to warm up to ECS! If you are feeling adventurous, you can implement AsteroidJobSystem with an IJobFor and checkout the performance difference between single jobs and parallel jobs for spawning asteroids. If you have interesting results, please reach out to the Moetsi team either [on our Discord](https://discord.com/invite/88j758eUvs) or emailing us at <olenka@moetsi.com> and we will include it in this gitbook!
{% endhint %}

* Hit the "play" button and see the spawned asteroids
  * Not working? Reimport the ConvertedSubScene if there are issues grabbing the AsteroidAuthoringComponent Singleton
* Checkout the DOTS Hierarchy to see all the newly created entities

![DOTS Hierarchy Showing all the newly created Asteroid Prefab Entities](/files/pjiIpY9GoizkerO4VM89)

But where are all our asteroids?

* We can see from the DOTS Hierarchy that the Entities are there but the Translation values of the asteroids are verrrrrry far apart, so we cannot see them and so we need to update our GameSettings
* Let's go to our ConvertedSubScene in the Hierarchy, select Game Settings, and change our levelWidth, levelHeight, and levelDepth to 20 and change the number of asteroids to 20,000 in the Inspector

But we still can't see the asteroids even now when they are closer together, WHAT GIVES?!

The issue is that we are still using the "built in" render pipeline, rather than the Universal Render Pipeline (URP). Unity does not automatically change over. Following Unity's docs let's create a Universal Render Pipeline Asset and then Add the Asset to your Graphics Settings

* #### Creating the Universal Render Pipeline Asset <a href="#creating-the-universal-render-pipeline-asset" id="creating-the-universal-render-pipeline-asset"></a>

  The [Universal Render Pipeline Asset](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@10.9/manual/universalrp-asset.html) controls the global rendering and quality settings of your Project, and creates the rendering pipeline instance. The rendering pipeline instance contains intermediate resources and the render pipeline implementation.

  To create a Universal Render Pipeline Asset:

  1. In the Editor, go to the Project window.
  2. Right-click in the Project window in Assets/, and select **Create > Rendering > Universal Render Pipeline > Pipeline Asset**. Alternatively, navigate to the menu bar at the top, and select **Assets > Create > Rendering > Universal Render Pipeline > Pipeline Asset (Forward Renderer)**.

  You can either leave the default name for the new Universal Render Pipeline Asset, or type a new one.
* **Adding the Asset to your Graphics settings**

  To use URP, you need to add the newly created Universal Render Pipeline Asset to your Graphics settings in Unity. If you don't, Unity still tries to use the Built-in render pipeline.

  To add the Universal Render Pipeline Asset to your Graphics settings:

  1. Navigate to **Edit > Project Settings... > Graphics**.
  2. In the **Scriptable Render Pipeline Settings** field, add the Universal Render Pipeline Asset you created earlier. When you add the Universal Render Pipeline Asset, the available Graphics settings immediately change. Your Project is now using URP.

  \
  [**From Install URP Into A Project**](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@10.9/manual/InstallURPIntoAProject.html)
* Now let's hit play and see those Asteroids!

Why can't we see them?! It is because we are still using a Material from the built in render pipeline. Let's make a material that is compatible with Universal Render Pipeline.

* Right click in Assets/ and Prefabs and choose Material and call it GreyMaterial
* Choose the base map to be a Grey Color

![Our URP GreyMaterial](/files/S0COLZfrPgpmWNEI9Z0w)

* Now let's add it to our AsteroidPrefab

![Adding our URP GreyMaterial to the AsteroidPrefab](/files/URiasFhZ8zum6RBkRWWj)

* Now let's hit play

![Instantiating 20,000 Asteroid prefab entities](/files/-MPvDToUmGVOnqzLqrkj)

* Woah. That is way too many 😬
* Let's change the number of Asteroids to 200

![Change the number of asteroids from 20,000 to 200](/files/-MPvDn2n8NoNIJBxcWRV)

* This looks more like an asteroid field ☑️

{% hint style="success" %}
We now have spawned an Asteroid prefab (entity prefab) using ECS

* we created an Asteroid prefab
* we created a AsteroidTag authoring component
* We placed the AsteroidTag on the prefab
* We created AsteroidSpawnSystem which spawns Asteroids in a cube of a given height, width and depth

Many people come to ECS looking to increase the performance of their games and tools. While what you've built so far is a toy example, take a second to play around with the asteroid counts.

Push the asteroid counts as high as your machine can handle. Get comfortable with exploring which Systems carry the most load.

You've taken your first steps in high concurrency ECS systems! Congratulations!
{% endhint %}

## Adding movement to prefabs

* Create a Velocity component (right click Scripts and Prefabs > Create > ECS > Authoring Component Type, *you get the hang of this now, right?*)
* We will add this to our asteroid prefab and update the AsteroidSpawnSystem to set the Velocity component on our asteroid entity when instantiating&#x20;
  * Just a heads up: we're going to shake things up a bit in the next section, where we use **Unity Physics** for velocity instead
* paste the code snippet below into VelocityComponent.cs:

```
using Unity.Entities;
using Unity.Mathematics;

[GenerateAuthoringComponent]
public struct VelocityComponent : IComponentData
{
    public float3 Value;
}
```

* Then add the VelocityComponent to the asteroid prefab
  * We can add the VelocityComponent to the prefab because it has the \[GenerateAuthoringComponent] decorator

![Creating the VelocityComponent](/files/-MPvLNW8FsU0XfwACYCn)

* We must now update our AsteroidSpawnSystem to initialize the asteroids VelocityComponent
* Add the below code snippet to the end of the Job.WithCode() code block (just before the closing curly brace) in AsteroidSpawnSystem.cs:

```

                //We will now set the VelocityComponent of our asteroids
                //here we generate a random Vector3 with x, y and z between -1 and 1
                var randomVel = new Vector3(rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f));
                //next we normalize it so it has a magnitude of 1
                randomVel.Normalize();
                //now we set the magnitude equal to the game settings
                randomVel = randomVel * settings.asteroidVelocity;
                //here we create a new VelocityComponent with the velocity data
                var vel = new VelocityComponent{Value = new float3(randomVel.x, randomVel.y, randomVel.z)};
                //now we set the velocity component in our asteroid prefab
                commandBuffer.SetComponent(e, vel);
```

* Reimport ConvertedSubScene, hit "play", then go checkout the Entity list of asteroids in the DOTS Hierarchy, now with their new VelocityComponents

![Selected AsteroidPrefab with VelocityComponent visible in the Inspector](/files/VoyIVJRRDAskZLpP992x)

* Now we need to make a system to update Translation values based on VelocityComponent data
* Create MovementSystem in Scripts and Prefabs (Create > ECS > System)
* Create MovementSystem and paste this code snippet into MovementSystem.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;

public partial class MovementSystem : SystemBase
{

    protected override void OnUpdate()
    {
        var deltaTime = Time.DeltaTime;
        Entities
        .ForEach((ref Translation position, in VelocityComponent velocity) =>
        {
            position.Value.xyz += velocity.Value * deltaTime;
        }).ScheduleParallel();
    }
}
```

* Notice that this system is much simpler than AsteroidSpawningSystem
* This is because there are no structural changes to be made and we do not need to use any data beyond the data that is already on the entities we want to update
  * All we want to do in MovementSystem is read data from one component (Velocity), and use it to adjust the data in another component (Translation)

> Use the [Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities) construction provided by the [SystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.html) class as a concise way to define and execute your algorithms over entities and their components. [Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities) executes a lambda function you define over all the entities selected by an [entity query](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityQuery.html).
>
> To execute a job lambda function, you either schedule the job using `Schedule()` and `ScheduleParallel()`, or execute it immediately (on the main thread) with `Run()`. You can use additional methods defined on [Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities) to set the entity query as well as various job options.
>
> ...
>
> When you define the lambda function to use with [Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities), you can declare parameters that the [SystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.html) class uses to pass in information about the current entity when it executes the function.
>
> A typical lambda function looks like:
>
> ```cs
> Entities.ForEach(
>     (Entity entity,
>         int entityInQueryIndex,
>         ref Translation translation,
>         in Movement move) => { /* .. */})
> ```
>
> By default, you can pass up to eight parameters to an Entities.ForEach lambda function. (If you need to pass more parameters, you can [define a custom delegate](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_entities_foreach.html#custom-delegates).) When using the standard delegates, you must group the parameters in the following order:
>
> 1. Parameters passed-by-value first (no parameter modifiers)
> 2. Writable parameters second (`ref` parameter modifier)
> 3. Read-only parameters last (`in` parameter modifier)
>
> All components should use either the `ref` or the `in` parameter modifier keywords. Otherwise, the component struct passed to your function is a copy instead of a reference. This means an extra memory copy for read-only parameters and means that any changes to components you intended to update are silently thrown when the copied struct goes out of scope after the function returns.
>
> From [ECS Using Entities.ForEach documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_entities_foreach.html)

* Our MovementSystem queries all entities that have both a Translation and a Velocity component
  * We put "**ref** Translation position" because we will be **writing** to the Translation component
  * We put "**in** VelocityComponent velocity" because we are only **reading** from the Velocity component
* Wait, where is this "Burst" we have been hearing so much about?
  * Job.WithCode() and Entities.ForEach() are automatically Burst compiled (woo!)
  * Sometimes you must specify .WithoutBurst() for certain workflows (we will see this later)
  * [Burst documentation](https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/index.html) **if you want to read up.**

> You can execute the lambda function on the main thread using `Run()`, as a single job using `Schedule()`, or as a parallel job using `ScheduleParallel()`. These different execution methods have different constraints on how you access data. In addition, [Burst](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html) uses a restricted subset of the C# language, so you need to specify `WithoutBurst()` when using C# features outside this subset (including accessing managed types).
>
> From [ECS Using Entities.ForEach documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_entities_foreach.html)

* If possible use .ScheduleParallel() to make the most of Unity ECS
  * That is the whole point of doing things in this new data-oriented way, performance improvements! 🚀
  * If you're curious, try and create the same AsteroidSpawnSystem using MonoBehaviours to see the performance difference
  * Sometimes you will not be able to use Burst, Schedule(), or ScheduleParallel() based on your workflow (limitations of the technology)

> The following table shows which features are currently supported in [Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities) for the different methods of scheduling available in [SystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.html):

| Supported Feature             | Run                                               | Schedule | ScheduleParallel     |
| ----------------------------- | ------------------------------------------------- | -------- | -------------------- |
| Capture local value type      | x                                                 | x        | x                    |
| Capture local reference type  | x (only WithoutBurst and not in ISystem)          |          |                      |
| Writing to captured variables | x                                                 |          |                      |
| Use field on the system class | x (only WithoutBurst)                             |          |                      |
| Methods on reference types    | x (only WithoutBurst and not in ISystem)          |          |                      |
| Shared Components             | x (only WithoutBurst and not in ISystem)          |          |                      |
| Managed Components            | x (only WithoutBurst and not in ISystem)          |          |                      |
| Structural changes            | x (only WithStructuralChanges and not in ISystem) |          |                      |
| SystemBase.GetComponent       | x                                                 | x        | x                    |
| SystemBase.SetComponent       | x                                                 | x        |                      |
| GetComponentDataFromEntity    | x                                                 | x        | x (only as ReadOnly) |
| HasComponent                  | x                                                 | x        | x                    |
| WithDisposeOnCompletion       | x                                                 | x        | x                    |
| WithScheduleGranularity       |                                                   |          | x                    |

> **Note:** `WithStructuralChanges()` will disable Burst. Do not use this option if you want to achieve high levels of performance in your [Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities) (instead use an [EntityCommandBuffer](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityCommandBuffer.html)).
>
> An [Entities.ForEach](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.SystemBase.Entities.html#Unity_Entities_SystemBase_Entities) construction uses Roslyn source generators to translate the code you write for the construction into correct ECS code. This translation allows you to express the intent of your algorithm without having to include complex, boilerplate code. However, it can mean that some common ways of writing code are not allowed.
>
> The following features are not currently supported:

| Unsupported Feature                                          |
| ------------------------------------------------------------ |
| Dynamic code in .With invocations                            |
| SharedComponent parameters by ref                            |
| Nested Entities.ForEach lambda expressions                   |
| Calling with delegate stored in variable, field or by method |
| SetComponent with lambda parameter type                      |
| GetComponent with writable lambda parameter                  |
| Generic parameters in lambdas                                |
| In systems with generic parameters                           |

> From [ECS Using Entities.ForEach documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_entities_foreach.html#supported-features)

* Hit "play" and see the asteroids move in their random velocities
* Go to the GameSettings in ConvertedSubScene and adjust the Asteroid Velocity to 2 to slow down the asteroids and make them more Space-like
* Reimport the Sub Scene, hit "play", and checkout the Entity Debugger to see how the asteroids' Translation values are changed in real-time

![With MovementSystem our Asteroids now move based on their VelocityComponent values](/files/-MPvnM-n3y2opIQ-hI-v)

{% hint style="success" %}
We now have asteroids prefabs moving in random directions

* We created a VelocityComponent and added it to the asteroid prefab
* We added to the AsteroidSpawnSystem to set random velocities on the asteroids
* We created MovementSystem to take any entity with a Translation and VelocityComponent and adjust the Translation value using the VelocityComponent
  {% endhint %}

## Destroying Prefabs

* We are going to destroy any asteroid prefabs that leave the perimeter of our cube
* We will create an AsteroidsOutOfBoundsSystem that adds a DestroyTag to any asteroid that leaves the cube perimeter
* Create DestroyTag and paste this code snippet into DestroyTag.cs:

```
using Unity.Entities;

public struct DestroyTag : IComponentData
{
}
```

![Creating the DestroyTag](/files/-MPvoOzKYwEGEe8YZyAY)

* Notice we did not add \[GenerateAuthoringComponent] to this tag
  * This is because this is data we add at runtime **not** at authoring time
* Now let's create the AsteroidsOutOfBoundsSystem that will add the DestroyTag to any asteroid that leaves the cube's perimeter
* Create AsteroidsOutOfBoundsSystem and paste this code snippet into AsteroidsOutOfBoundsSystem.cs:

```
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;
//We are adding this system within the FixedStepSimulationGroup
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(EndFixedStepSimulationEntityCommandBufferSystem))] 
public partial class AsteroidsOutOfBoundsSystem : SystemBase
{
    //We are going to use the EndFixedStepSimECB
    //This is because when we use Unity Physics our physics will run in the FixedStepSimulationSystem
    //We are dipping our toes into placing our systems in specific system groups
    //The FixedStepSimGroup has its own EntityCommandBufferSystem we will use to make the structural change
    //of adding the DestroyTag
    private EndFixedStepSimulationEntityCommandBufferSystem m_EndFixedStepSimECB;
    protected override void OnCreate()
    {
        //We grab the EndFixedStepSimECB for our OnUpdate
        m_EndFixedStepSimECB = World.GetOrCreateSystem<EndFixedStepSimulationEntityCommandBufferSystem>();
        //We want to make sure we don't update until we have our GameSettingsComponent
        //because we need the data from this component to know where the perimeter of our cube is
        RequireSingletonForUpdate<GameSettingsComponent>();
    }

    protected override void OnUpdate()
    {
        //We want to run this as parallel jobs so we need to add "AsParallelWriter" when creating
        //our command buffer
        var commandBuffer = m_EndFixedStepSimECB.CreateCommandBuffer().AsParallelWriter();
        //We must declare our local variables that we will use in our job
        var settings = GetSingleton<GameSettingsComponent>();
        //This time we query entities with components by using "WithAll" tag
        //This makes sure that we only grab entities with an AsteroidTag component so we don't affect other entities
        //that might have passed the perimeter of the cube  
        Entities
        .WithAll<AsteroidTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in Translation position) =>
        {
            //We check if the current Translation value is out of bounds
            if (Mathf.Abs(position.Value.x) > settings.levelWidth/2 ||
                Mathf.Abs(position.Value.y) > settings.levelHeight/2 ||
                Mathf.Abs(position.Value.z) > settings.levelDepth/2)
            {
                //If it is out of bounds wee add the DestroyTag component to the entity and return
                commandBuffer.AddComponent(entityInQueryIndex, entity, new DestroyTag());
                return;
            }
        }).ScheduleParallel();
        //We add the dependencies to the CommandBuffer that will be playing back these structural changes (adding a DestroyTag)
        m_EndFixedStepSimECB.AddJobHandleForProducer(Dependency);
    }
}
```

* Notice we are placing this system in a specific SystemGroup, the FixedStepSimulationGroup
  * We want to make sure it updates before the EndFixedStepSimulationEntityCommandBufferSystem because the latter is where recorded structural changes will playback
  * We want to start getting comfortable with placing systems in different groups and utilizing different EntityCommandBufferSystems because not only is it important in ECS in general, but also because we will be using Unity Physics in the next section (which is run in the FixedStepSimulationGroup)
* We use .WithAll\<AsteroidTag>() to query all entities with an AsteroidTag
* We also needed to include "int entityInQueryIndex" in our .ForEach() because when running parallel jobs, we must include the entityInQueryIndexwhen making our changes. It is needed for parallel jobs to work!
  * commandBuffer.AddComponent(**entityInQueryIndex**, entity, new DestroyTag());

> - **`int entityInQueryIndex`** — the index of the entity in the list of all entities selected by the query. Use the entity index value when you have a [native array](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html) that you need to fill with a unique value for each entity. You can use the entityInQueryIndex as the index in that array. The entityInQueryIndex should also be used as the `sortKey` for adding commands to a concurrent [EntityCommandBuffer](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityCommandBuffer.html).
>
> From [Special, named parameters](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_entities_foreach.html)

* Now we need to create a system that destroys any asteroids with a DestroyTag
* Create AsteroidsDestructionSystem and paste this code snippet into AsteroidsDestructionSystem.cs:

```
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;

//We are going to update LATE once all other systems are complete
//because we don't want to destroy the Entity before other systems have
//had a chance to interact with it if they need to
[UpdateInGroup(typeof(LateSimulationSystemGroup))]
public partial class AsteroidsDestructionSystem : SystemBase
{
    private EndSimulationEntityCommandBufferSystem m_EndSimEcb;    

    protected override void OnCreate()
    {
        //We grab the EndSimulationEntityCommandBufferSystem to record our structural changes
        m_EndSimEcb = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        //We add "AsParallelWriter" when we create our command buffer because we want
        //to run our jobs in parallel
        var commandBuffer = m_EndSimEcb.CreateCommandBuffer().AsParallelWriter();

        //We now any entities with a DestroyTag and an AsteroidTag
        //We could just query for a DestroyTag, but we might want to run different processes
        //if different entities are destroyed, so we made this one specifically for Asteroids
        Entities
        .WithAll<DestroyTag, AsteroidTag>()
        .ForEach((Entity entity, int entityInQueryIndex) =>
        {
            commandBuffer.DestroyEntity(entityInQueryIndex, entity);

        }).ScheduleParallel();

        //We then add the dependencies of these jobs to the EndSimulationEntityCOmmandBufferSystem
        //that will be playing back the structural changes recorded in this sytem
        m_EndSimEcb.AddJobHandleForProducer(Dependency);
    
    }
}
```

* We run this system in the LateSimulationSystemGroup
  * This way all systems are able to interact with the entity before we delete it
* We will use the LateSimulationSystemGroup's CommandBufferSystem to playback our recorded structural changes (destroying the asteroid entity)
* Hit "play" and see how the asteroids are destroyed when they pass the perimeter and how the AsteroidSpawnSystem creates new asteroids
* Check out the DOTS Hierarchy and see the AsteroidPrefabs get created and destroyed

### Celebrate your win; Congrats!

You just made a giant cloud of asteroids! Congratulations, you've taken your first steps in high concurrency ECS systems!&#x20;

Many come to ECS looking to increase the performance of their games and tools. While what you've built so far is a toy example, take a second to play around with the asteroid counts. Push the asteroid counts as high as your machine can handle. Get comfortable with exploring which Systems carry the most loa&#x64;**.**

{% hint style="success" %}
We now have an asteroid field

* We created a DestroyTag which is added by the AsteroidsOutOfBoundsSystem when an asteroid leaves the cube perimeter
* We created AsteroidsDestructionSystem that destroys asteroids with a destroy tag
  {% endhint %}

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-Updating-and-Destroying-Asteroids>

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Spawning-Updating-and-Destroying-Asteroids'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Spawn and Move Players

Full workflows and code to spawn and move Player from user input

## What you'll develop on this page

![Player spawns and is able to navigate based on user input](/files/-MQ-sKY3GyLSxmezEpII)

We will create a player prefab and have it spawn when the user inputs commands. The player will move according to user input.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-and-Moving-Player>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Spawning a player

### Creating the Player prefab

We use many of the same steps as spawning an asteroid prefab to spawn a player prefab. First we make our player prefab.

* Create a capsule GameObject in the Hierarchy of SubScene, name it "Player", and drag it into Scripts and Prefabs then delete the cylinder GameObject in the Hierarchy

![Creating our player prefab](/files/-MPygQDkOPEoOfVmMJNU)

* Remove the Capsule Collider component
  * We will add DOTS Physics colliders in the next section
* Add a cube to the Player hierarchy named "Visor"
  * (Double click on the Player prefab to get to the Player hierarchy)
  * Remove the Box Collider component
  * Change the position to (0, 0.5, 0.24)
  * Change the scale to (0.95, 0.25, 0.5)
* Right click on the Assets, click "Create" > "Material"
  * Make the name "Black"
  * Make sure it is a Universal Render Pipeline/Lit shader
  * Double click Base Map and change the value to "000000"
* Click on the Visor in the Player Hierarchy and drag "Black" to Mesh Renderer Material

![Adding a visor to the player prefab](/files/-MPykc8qCQ7_f6gSc5wZ)

* Add a cylinder to the Player hierarchy named "Gun"
  * Remove the Capsule Collider component
  * Change the position to (0.5, 0, 0.5)
  * Change the rotation to (90, 0 , 0)
  * Change the scale to (0.25, 0.5, 0.25)
  * Change the material to "Black"

![Adding a gun to our player prefab](/files/-MPymjP-ChBN5wifeUMt)

* Finally let's add a camera
  * Right click in the Player prefab Hierarchy and create a Camera GameObject
  * Change the position to (0, 2, -5)
* And make sure your Player prefab has a Position of (0, 0, 0)

![Add a Camera GameObject and make sure the Player prefab is at (0, 0, 0)](/files/-MPzQ0TgBNH6_DzdxUPJ)

* Now we need to create a PlayerTag in our Scripts and Prefabs folder to put on our Player prefab
  * We  use this tag to query for our player entity
  * Because we are putting this component data on a prefab *before* runtime we must make it an Authoring component with \[GenerateAuthoringComponent]
* Create PlayerTag and paste this code snippet into PlayerTag.cs:

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct PlayerTag : IComponentData
{
}
```

* Now we will put the PlayerTag component and the VelocityComponent on our Player prefab

![Adding a PlayerTag and VelocityComponent to our Player prefab](/files/-MPzRVXnjxvDNJrwPAcd)

* Finally, we must add our Player prefab to the PrefabCollection GameObject in our ConvertedSubScene like we did with our Asteroid prefab
  * The first step here is to create the PlayerAuthoringComponent
    * code snippet for PlayerAuthoringComponent.cs below:

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct PlayerAuthoringComponent : IComponentData
{
    public Entity Prefab;
}
```

* Next we open our ConvertedSubScene, select PrefabCollection, click Add Component to add PlayerAuthoringComponent to the PrefabCollection GameObject and drag the Player prefab into the the Prefab field
  * Unity might ask you to save your Player prefab when navigating to the Sub Scene if you have not already, hit save
* Save the Sub Scene, open SampleScene and reimport the ConvertedSubScene

![Add the Player prefab to the PrefabCollection GameObject in ConvertedSubScene](/files/-MPzUMnFq-xP8Yy5ngtg)

We now have our Player prefab created and registered with our PrefabCollection so we can reference it in ECS.

### Creating the spawning system

* We are going to create InputSpawnSystem in Scripts and Prefabs that will take in user input and spawn a player prefab if the user presses the spacebar
  * AsteroidSpawnSystem spawns asteroids programmatically based on the GameSettingsComponents values
  * InputSpawnSystem will spawn entities based on user input
  * InputSpawnSystem will have a lot of similarities to AsteroidSpawnSystem because both systems need to set prefabs and use EntityCommandBuffer to record structural changes
  * InputSpawnSystem does not need GameSettingsComponent data so it will have a few less lines in OnCreate()
* Create InputSpawnSystem and paste the code snippet below into InputSpawnSystem.cs:

```
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;

public partial class InputSpawnSystem : SystemBase
{
    //This will be our query for Players
    private EntityQuery m_PlayerQuery;

    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimECB;

    //This will save our Player prefab to be used to spawn Players
    private Entity m_Prefab;

    protected override void OnCreate()
    {
        //This is an EntityQuery for our Players, they must have an PlayerTag
        m_PlayerQuery = GetEntityQuery(ComponentType.ReadWrite<PlayerTag>());

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimECB = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
    }
    
    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_Prefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's PlayerAuthoringComponent
            //and set m_Prefab to its Prefab value
            m_Prefab = GetSingleton<PlayerAuthoringComponent>().Prefab;

            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }
        byte shoot;
        shoot = 0;
        var playerCount = m_PlayerQuery.CalculateEntityCountWithoutFiltering();

        if (Input.GetKey("space"))
        {
            shoot = 1;
        }

        if (shoot == 1 && playerCount < 1)
        {
            EntityManager.Instantiate(m_Prefab);
            return;
        }
    }
}
```

![Add InputSpawnSystem](/files/-MQ-8xnpZA_fe_PvqKPI)

* You will notice the similarities of InputSpawnSystem with AsteroidSpawnSystem, but also a few differences:
  * why aren't we using the EntityCommandBuffer in InputSpawnSystem?!
  * And what the heck is an EntityManager?!
* We are not using the EntityCommandBuffer to demonstrate how to make structural changes during InputSpawnSystem's OnUpdate()
  * That's not to torture you, it's to teach you more ECS functionalities 💪
  * We will continue using the EntityCommandBuffer later on when we spawn bullets 😌

> A [World](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.World.html) organizes entities into isolated groups. A world owns both an [EntityManager](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityManager.html) and a set of [Systems](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_systems.html). Entities created in one world only have meaning in that world, but can be transfered to other worlds (with [EntityManager.MoveEntitiesFrom](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityManager.MoveEntitiesFrom.html)). Systems can only access entities in the same world. You can create as many worlds as you like.
>
> By default Unity creates a default [World](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.World.html) when your application starts up (or you enter **Play Mode**). Unity instantiates all systems (classes that extend [ComponentSystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.ComponentSystemBase.html)) and adds them to this default world. Unity also creates specialized worlds in the Editor. For example, it creates an Editor world for entities and systems that run only in the Editor, not in playmode and also creates conversion worlds for managing the conversion of GameObjects to entities. See [WorldFlags](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.WorldFlags.html) for examples of different types of worlds that can be created.
>
> Use [World.DefaultGameObjectInjectionWorld](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.World.DefaultGameObjectInjectionWorld.html#Unity_Entities_World_DefaultGameObjectInjectionWorld) to access the default world.
>
> From [ECS World documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/world.html)

* You might have noticed that when we hit the "play" button the list of available worlds in our DOTS Windows changes

![](/files/ZevcSclLjEYsWV9LCl8C)![](/files/LPxDOP3KdV6Wstfu7oQd)

* "Editor World" changes to "Default World"

> You can also disable the default World creation entirely by defining the following global symbols:
>
> * `#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD` disables generation of the default runtime World.
> * `#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_EDITOR_WORLD` disables generation of the default Editor World.
> * `#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP` disables generation of both default Worlds.
>
> From [ECS World documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/world.html)

* A World owns a single EntityManager and a set of Systems
* The EntityManager is what we use to interact with Entities if we are not using EntityCommandBuffer

> The EntityManager provides an API to create, read, update, and destroy entities.
>
> A [World](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityManager.World.html#Unity_Entities_EntityManager_World) has one EntityManager, which manages all the entities for that World.
>
> Many EntityManager operations result in *structural changes* that change the layout of entities in memory. Before it can perform such operations, the EntityManager must wait for all running Jobs to complete, an event called a *sync point*. A sync point both blocks the main thread and prevents the application from taking advantage of all available cores as the running Jobs wind down.
>
> Although you cannot prevent sync points entirely, you should avoid them as much as possible. To this end, the ECS framework provides the [EntityCommandBuffer](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityCommandBuffer.html), which allows you to queue structural changes so that they all occur at one time in the frame.
>
> From [ECS EntityManager documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityManager.html)

* Now that we've created our InputSpawnSystem let's hit "play" then "space bar" to spawn our Player prefab

![Spawning the Player prefab by hitting space bar](/files/-MQ-9bZ6UBXzCqZpHhrI)

* Woo, all set! Right?
  * Not quite, why isn't the camera updated to the Player prefab's Camera GameObject?
* The Camera is a GameObject but is being instantiated through ECS... a little wonky
* To fix this we need to add `HYBRID_ENTITIES_CAMERA_CONVERSION` to the Player Settings
  * Navigate to "File", choose "Build settings", then "Player Settings", then "Player" on the left, expand the drop down menu for "Other Settings", and scroll down to "Scripting Define Symbols". Add `HYBRID_ENTITIES_CAMERA_CONVERSION`
    * Hit "Apply"
* This will cause a warning on the Build Settings window

![Add HYBRID\_ENTITIES\_CAMERA\_CONVERSION to Scripting Define Symbols](/files/-MQ-WuqaOzIck-egIRra)

* Wait until the warning on the Build Settings window goes away
* Now hit "play" and press "space bar" again to see our new camera view
  * Through testing we have found that sometimes this change does not immediately "take"
  * In that case, go to "Assets" and choose "Reimport All"

![Spawning a Player prefab now switches to the Camera GameObject attached to the Player](/files/-MQ-XEcv2Zj8hOD7zraM)

{% hint style="success" %}
We can now spawn our player from user input

* We created our player prefab
* We added a PlayerTag and VelocityComponent to the prefab
* We created InputSystem which takes in inputs and spawns our player
  {% endhint %}

## Moving a Player

* We are going to create InputMovementSystem in Scripts and Prefabs to take in "WASD" and mouse input to change the rotation and velocity of our player
  * We could implement this functionality into the existing InputSpawnSystem (and name the system something else) but we are separating to make the code easier to understand
* Create InputMovementSystem and paste the code snippet below into InputMovementSystem.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

public partial class InputMovementSystem : SystemBase
{
    protected override void OnCreate()
    {
        //We will use playerForce from the GameSettingsComponent to adjust velocity
        RequireSingletonForUpdate<GameSettingsComponent>();
    }

    protected override void OnUpdate()
    {
        //we must declare our local variables to be able to use them in the .ForEach() below
        var gameSettings = GetSingleton<GameSettingsComponent>();
        var deltaTime = Time.DeltaTime;

        //we will control thrust with WASD"
        byte right, left, thrust, reverseThrust;
        right = left = thrust = reverseThrust = 0;

        //we will use the mouse to change rotation
        float mouseX = 0;
        float mouseY = 0;

        //we grab "WASD" for thrusting
        if (Input.GetKey("d"))
        {
            right = 1;
        }
        if (Input.GetKey("a"))
        {
            left = 1;
        }
        if (Input.GetKey("w"))
        {
            thrust = 1;
        }
        if (Input.GetKey("s"))
        {
            reverseThrust = 1;
        }
        //we will activate rotating with mouse when the right button is clicked
        if (Input.GetMouseButton(1))
        {
            mouseX = Input.GetAxis("Mouse X");
            mouseY = Input.GetAxis("Mouse Y");

        }

        Entities
        .WithAll<PlayerTag>()
        .ForEach((Entity entity, ref Rotation rotation, ref VelocityComponent velocity) =>
        {
            if (right == 1)
            {   //thrust to the right of where the player is facing
                velocity.Value += (math.mul(rotation.Value, new float3(1,0,0)).xyz) * gameSettings.playerForce * deltaTime;
            }
            if (left == 1)
            {   //thrust to the left of where the player is facing
                velocity.Value += (math.mul(rotation.Value, new float3(-1,0,0)).xyz) * gameSettings.playerForce * deltaTime;
            }
            if (thrust == 1)
            {   //thrust forward of where the player is facing
                velocity.Value += (math.mul(rotation.Value, new float3(0,0,1)).xyz) * gameSettings.playerForce * deltaTime;
            }
            if (reverseThrust == 1)
            {   //thrust backwards of where the player is facing
                velocity.Value += (math.mul(rotation.Value, new float3(0,0,-1)).xyz) *  gameSettings.playerForce * deltaTime;
            }
            if (mouseX != 0 || mouseY != 0)
            {   //move the mouse
                //here we have "hardwired" the look speed, we could have included this in the GameSettingsComponent to make it configurable
                float lookSpeedH = 2f;
                float lookSpeedV = 2f;

                //
                Quaternion currentQuaternion = rotation.Value; 
                float yaw = currentQuaternion.eulerAngles.y;
                float pitch = currentQuaternion.eulerAngles.x;

                //MOVING WITH MOUSE
                yaw += lookSpeedH * mouseX;
                pitch -= lookSpeedV * mouseY;
                Quaternion newQuaternion = Quaternion.identity;
                newQuaternion.eulerAngles = new Vector3(pitch,yaw, 0);
                rotation.Value = newQuaternion;
            }
        }).ScheduleParallel();
    }
}
```

* Hit "play" and once the game is loaded press "space bar" to spawn your player, hold down the right button and move your mouse to change rotation and hit "w" to add forward thrust

![](/files/-MQ-quasFHGw8m2t-anw)

* We didn't need to make a second MovementSystem for our player entity because the MovementSystem works on **any** entity that has a Translation and VelocityComponent
  * Nice benefit of ECS
* It is a bit fast, let's change the Player Force to 10
* Go into ConvertedSubScene and change Player Force from 50 to 10
* Save and return to SampleScene and reimport ConvertedSubScene and hit play and try again

![Player moving with better thrust speeds](/files/-MQ-r647oiei5Ja0ncUk)

{% hint style="success" %}
We can now move our player from user input

* We updated our InputSystem to read more user inputs
* We can adjust the rotation and velocity of our player entity with user inputs
  {% endhint %}

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-and-Moving-Player>

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Spawning-and-Moving-Player'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Spawn Bullets and Destroy Player

Full workflows and code to spawn bullets and self-destruct Player

## What you'll develop on this page

![Player is able to spawn bullets and self-destruct](/files/-MQ2pqOMGmfusQcGzTak)

We will spawn bullets and self-destruct the player from user inputs.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Shooting-and-Destroying-Player>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Spawning a bullet

* In order to spawn a bullet, we need to know where exactly we want it spawned
* We are going to update our Player prefab with a BulletSpawn GameObject
  * First, open the Player prefab and add an empty GameObject to the Hierarchy named Bullet Spawn
  * Change the position to (0.5, 0 , 1)

{% hint style="info" %}
We *could*  simply "remember" the offset (0.5, 0, 1) by implementing it in our bullet spawn system (via writing it into the script). The issue with this approach is that every time the Player prefab is updated, you'd need to remember to go back and update this code in the bullet spawn system (easy to remember on a small simple project like this, but harder to do on complex projects). Instead, we want to link the GameObject position to component data on the player entity. This way if the Player prefab is updated and the Bullet Spawn GameObject is moved around, the code does not have to change.
{% endhint %}

* Let's create the BulletSpawnOffsetComponent and paste the below code snippet into BulletSpawnOffsetComponent.cs:

```
using Unity.Entities;
using Unity.Mathematics;

public struct BulletSpawnOffsetComponent : IComponentData
{
    public float3 Value;
}
```

* This is the component that will be added (via 'Add Component') to the player entity to store the Bullet Spawn offset

![Creating BulletSpawn GameObject and BulletSpawnOffsetComponent](/files/-MQ0NG2YJ3yjygUsqc8-)

* Next, we need to use IConvertGameObjectToEntity to run a process that takes in the BulletSpawn GameObject Transform and sets BulletSpawnOffsetComponent to that value
* &#x20;Let's create the SetBulletSpawnOffset and paste the below code snippet to SetBulletSpawnOffset.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

public class SetBulletSpawnOffset : UnityEngine.MonoBehaviour, IConvertGameObjectToEntity
{
    public GameObject bulletSpawn;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        var bulletOffset = default(BulletSpawnOffsetComponent);

        var offsetVector = bulletSpawn.transform.position;
        bulletOffset.Value = new float3(offsetVector.x, offsetVector.y, offsetVector.z);        

        dstManager.AddComponentData(entity, bulletOffset);
    }
}
```

![Create SetBulletSpawnOffset and check out the DOTS Windows to see player entity](/files/-MQ0OyMxu104ncxORfAP)

* Next we open our Player prefab, put SetBulletSpawnOffset on our Player prefab (via Add Component), then drag the Bullet Spawn GameObject in Hierachy into the bulletSpawn field in the Inspector window when Player is selected
* Hit "play" and then navigate to the DOTS Windows to check out the Player archetype by finding the Archetype that contains the BulletSpawnOffsetComponent

![DOTS Windows showing the Player Archetype](/files/8dDsUNOmbBxFvAxTeWiJ)

* Now let's find the Player entity in the DOTS Hierarchy tab and check out the component values in the inspector
  * You will see the Bullet Spawn Offset Component and the values of .5, 0, 1

![Player Entity in the Editor Inspector Window with component values](/files/ZbZaPIEsKigefIuHq1ki)

* We will use this value in InputSpawnSystem to spawn our bullet
* Now we must create a Bullet prefab
* We will follow the same process as we did for asteroids and players
* Create Bullet prefab
  * In the SampleScene Hierarchy, Create a 3D object > Sphere GameObject named "Bullet," drag it into the Scripts and Prefabs project folder
  * Once Bullet has been dragged into the folder, delete the Bullet GameObject from the Hierarchy
  * Open the Bullet prefab and change the scale to (0.1, 0.1, 0.1)
  * Change the material to "Black"
  * Remove the Sphere Collider component
* Create a BulletTag component and add it to the Bullet prefab
  * paste the below code snippet into BulletTag.cs:

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct BulletTag : IComponentData
{
}
```

![Create Bullet prefab and add BulletTag](/files/-MQ0WjaUjuET95uXu1qI)

* Now we are going to create a new Authoring component we did not have for asteroids or players, named BulletAgeComponent, and put it on the Bullet prefab by clicking "Add Component" while Bullet is selected in Hierarchy
  * This component will be used to delete bullets after a specific amount of time
    * Why the time limit? Not sure about you, but we don't want bullets to live forever in the game and use up resources
* Paste the code snippet below into BulletAgeComponent.cs:

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct BulletAgeComponent : IComponentData
{
    public BulletAgeComponent(float maxAge)
    {
        this.maxAge = maxAge;
        age = 0;
    }

    public float age;
    public float maxAge;

}
```

* Add the BulletAgeComponent to the Bullet prefab and make maxAge = 5 by typing in 5 to the Max Age field
  * This will set the lifetime of a bullet to 5 seconds

![Create the BulletAgeComponent and Add it to the Bullet prefab with a maxAge of 5](/files/-MQ0WuLmtXVg4LC2p70A)

* Next we'll create Bullet**Authoring**Component to be put on the PrefabCollection in ConvertedSubScene
* Paste the below code snippet into BulletAuthoringComponent.cs:

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct BulletAuthoringComponent : IComponentData
{
    public Entity Prefab;
}
```

* Navigate to ConvertedSubScene, add the BulletAuthoringComponent to our PrefabCollection GameObject by clicking "Add Component" in Inspector&#x20;
* Drag the Bullet Prefab into the "Prefab" field in the Bullet Authoring Component in Inspector
* save, and navigate to SampleScene, and reimport ConvertedSubScene

![Create the BulletAuthoringComponent and add it to our PrefabCollection GameObject](/files/-MQ0X4zwmrkaKUGuFOgr)

* Next, we add the VelocityComponent to to the Bullet so it can travel

![Adding the VelocityComponent to the Bullet Prefab](/files/-MQ0Y2yKroRK5bIvGBOV)

* Now we we are ready to update InputSpawnSystem to spawn a Bullet
* Paste the below code snippet into InputSpawnSystem.cs:&#x20;

```
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;

public partial class InputSpawnSystem : SystemBase
{
    //This will be our query for Players
    private EntityQuery m_PlayerQuery;

    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimECB;

    //This will save our Player prefab to be used to spawn Players
    private Entity m_PlayerPrefab;

    //This will save our Bullet prefab to be used to spawn Bullets
    private Entity m_BulletPrefab;

    //We are going to use this to rate limit bullets per second
    //We could have included this in the game settings, no "ECS reason" not to
    private float m_PerSecond = 10f;
    private float m_NextTime = 0;

    protected override void OnCreate()
    {
        //This is an EntityQuery for our Players, they must have an PlayerTag
        m_PlayerQuery = GetEntityQuery(ComponentType.ReadWrite<PlayerTag>());

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimECB = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We need the GameSettingsComponent to grab the bullet velocity
        //When there is only 1 instance of a component (like GamesSettingsComponent) we can use "RequireSingletonForUpdate"
        RequireSingletonForUpdate<GameSettingsComponent>();
    }
    
    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_PlayerPrefab == Entity.Null || m_BulletPrefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's PlayerAuthoringCOmponent
            //and set m_PlayerPrefab to its Prefab value
            m_PlayerPrefab = GetSingleton<PlayerAuthoringComponent>().Prefab;
            m_BulletPrefab = GetSingleton<BulletAuthoringComponent>().Prefab;

            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        byte shoot;
        shoot = 0;
        var playerCount = m_PlayerQuery.CalculateEntityCountWithoutFiltering();

        if (Input.GetKey("space"))
        {
            shoot = 1;
        }

        //If we have pressed the space bar and there is less than 1 player, create a new player
        //This will be false after we create our first player
        if (shoot == 1 && playerCount < 1)
        {
            var entity = EntityManager.Instantiate(m_PlayerPrefab);
            return;
        }

        var commandBuffer = m_BeginSimECB.CreateCommandBuffer().AsParallelWriter();
        //We must declare our local variables before the .ForEach()
        var gameSettings = GetSingleton<GameSettingsComponent>();
        var bulletPrefab = m_BulletPrefab;

        //we are going to implement rate limiting for shooting
        var canShoot = false;
        if (UnityEngine.Time.time >= m_NextTime)
        {
            canShoot = true;
            m_NextTime += (1/m_PerSecond);
        }

        Entities
        .WithAll<PlayerTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in Translation position, in Rotation rotation,
                in VelocityComponent velocity, in BulletSpawnOffsetComponent bulletOffset) =>
        {
            //If we don't have space bar pressed we don't have anything to do
            if (shoot != 1 || !canShoot)
            {
                return;
            }
            
            // We create the bullet here
            var bulletEntity = commandBuffer.Instantiate(entityInQueryIndex, bulletPrefab);
            
            //we set the bullets position as the player's position + the bullet spawn offset
            //math.mul(rotation.Value,bulletOffset.Value) finds the position of the bullet offset in the given rotation
            //think of it as finding the LocalToParent of the bullet offset (because the offset needs to be rotated in the players direction)
            var newPosition = new Translation {Value = position.Value + math.mul(rotation.Value, bulletOffset.Value).xyz};
            commandBuffer.SetComponent(entityInQueryIndex, bulletEntity, newPosition);


            // bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) takes linear direction of where facing and multiplies by velocity
            // adding to the players physics Velocity makes sure that it takes into account the already existing player velocity (so if shoot backwards while moving forwards it stays in place)
            var vel = new VelocityComponent {Value = (gameSettings.bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) + velocity.Value};

            commandBuffer.SetComponent(entityInQueryIndex, bulletEntity, vel);

        }).ScheduleParallel();
        
        m_BeginSimECB.AddJobHandleForProducer(Dependency);
    }
}
```

* We just updated the rate in InputSpawnSystem to limit bullet generation to 10 per second
  * Otherwise bullets will be generated as fast as the .ForEach() can be run
* Let's hit "play", spawn our player and shoot some bullets and check it out

![After updating InputSpawnSystem we are able to spawn Bullet prefabs](/files/-MQ2h26jxBn8utBCikX3)

* Again, because our bullets have a Translation and VelocityComponent, our MovementSystem acts on them as well
* The bullets are traveling too fast to make out
* Navigate to ConvertedSubScene, and go to GameSettings to change the BulletVelocity to 20 in Inspector
* Save, return to SampleScene, reimport the ConvertedSubScene, hit play, spawn your player, and shoot around again

![Change Bullet Velocity to 200 in GameSettings](/files/-MQ2hAgKp15SJMVF7hZk)

* Much better, but we still need to add a system that destroys bullets when they pass their max age value we set in the BulletAgeComponent
* Paste the code snippet below into BulletAgeSystem.cs:

```
using Unity.Entities;

public partial class BulletAgeSystem : SystemBase
{
    //We will be using the BeginSimulationEntityCommandBuffer to record our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        //We create our CommandBuffer and add .AsParallelWriter() because we will be scheduling parallel jobs
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();

        //We must declare local variables before using them in the job below
        var deltaTime = Time.DeltaTime;

        //Our query writes to the BulletAgeComponent
        //The reason we don't need to add .WithAll<BulletTag>() here is because referencing the BulletAgeComponent
        //requires the Entities to have a BulletAgeComponent and only Bullets have those
        Entities.ForEach((Entity entity, int entityInQueryIndex, ref BulletAgeComponent age) =>
        {
            age.age += deltaTime;
            if (age.age > age.maxAge)
                commandBuffer.DestroyEntity(entityInQueryIndex, entity);

        }).ScheduleParallel();
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

* After creating BulletAgeSystem, hit "play", spawn a player, spawn bullets, and checkout the BulletAgeSystem at work

![BulletAgeSystem works and destroys bullets after their max age](/files/-MQ2ki237y1_eMKL4jvp)

{% hint style="success" %}
We can now spawn a bullet from user input

* We created our bullet prefab and added it to PrefabCollection
* We added a BulletTag, VelocityComponent, and BulletAge component to the bullet prefab
* We created a BulletAgeSystem to destroy bullets at the end of their life
* We added to the InputSystem to spawn bullet prefabs
  {% endhint %}

## Player self-destruction

* We are now going going to add the ability for the player to self-destruct if the "p" button is pressed
  * Because the player is floating in space it is easy to get lost and so this will provide a way to bail and return to the origin
* First we need to update InputSpawnSystem to add a DestroyTag to the player when "p" is pressed
* Update InputSpawnSystem.cs with the code below:

```
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;

public partial class InputSpawnSystem : SystemBase
{
    //This will be our query for Players
    private EntityQuery m_PlayerQuery;

    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimECB;

    //This will save our Player prefab to be used to spawn Players
    private Entity m_PlayerPrefab;

    //This will save our Bullet prefab to be used to spawn Players
    private Entity m_BulletPrefab;

    //We are going to use this to rate limit bullets per second
    //We could have included this in the game settings, no "ECS reason" not to
    private float m_PerSecond = 10f;
    private float m_NextTime = 0;

    protected override void OnCreate()
    {
        //This is an EntityQuery for our Players, they must have an PlayerTag
        m_PlayerQuery = GetEntityQuery(ComponentType.ReadWrite<PlayerTag>());

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimECB = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We need the GameSettingsComponent to grab the bullet velocity
        //When there is only 1 instance of a component (like GamesSettingsComponent) we can use "RequireSingletonForUpdate"
        RequireSingletonForUpdate<GameSettingsComponent>();
    }
    
    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_PlayerPrefab == Entity.Null || m_BulletPrefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's PlayerAuthoringCOmponent
            //and set m_PlayerPrefab to its Prefab value
            m_PlayerPrefab = GetSingleton<PlayerAuthoringComponent>().Prefab;
            m_BulletPrefab = GetSingleton<BulletAuthoringComponent>().Prefab;

            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        byte shoot, selfDestruct;
        shoot = selfDestruct = 0;
        var playerCount = m_PlayerQuery.CalculateEntityCountWithoutFiltering();

        if (Input.GetKey("space"))
        {
            shoot = 1;
        }
        if (Input.GetKey("p"))
        {
            selfDestruct = 1;
        }

        //If we have pressed the space bar and there is less than 1 player, create a new player
        //This will be false after we create our first player
        if (shoot == 1 && playerCount < 1)
        {
            var entity = EntityManager.Instantiate(m_PlayerPrefab);
            return;
        }

        var commandBuffer = m_BeginSimECB.CreateCommandBuffer().AsParallelWriter();
        //We must declare our local variables before the .ForEach()
        var gameSettings = GetSingleton<GameSettingsComponent>();
        var bulletPrefab = m_BulletPrefab;

        //we are going to implement rate limiting for shooting
        var canShoot = false;
        if (UnityEngine.Time.time >= m_NextTime)
        {
            canShoot = true;
            m_NextTime += (1/m_PerSecond);
        }

        Entities
        .WithAll<PlayerTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in Translation position, in Rotation rotation,
                in VelocityComponent velocity, in BulletSpawnOffsetComponent bulletOffset) =>
        {
            //If self-destruct was pressed we will add a DestroyTag to the player entity
            if(selfDestruct == 1)
            {
                commandBuffer.AddComponent(entityInQueryIndex, entity, new DestroyTag {});
            }
            //If we don't have space bar pressed we don't have anything to do
            if (shoot != 1 || !canShoot)
            {
                return;
            }
            
            // We create the bullet here
            var bulletEntity = commandBuffer.Instantiate(entityInQueryIndex, bulletPrefab);
            
            //we set the bullets position as the player's position + the bullet spawn offset
            //math.mul(rotation.Value,bulletOffset.Value) finds the position of the bullet offset in the given rotation
            //think of it as finding the LocalToParent of the bullet offset (because the offset needs to be rotated in the players direction)
            var newPosition = new Translation {Value = position.Value + math.mul(rotation.Value, bulletOffset.Value).xyz};
            commandBuffer.SetComponent(entityInQueryIndex, bulletEntity, newPosition);


            // bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) takes linear direction of where facing and multiplies by velocity
            // adding to the players physics Velocity makes sure that it takes into account the already existing player velocity (so if shoot backwards while moving forwards it stays in place)
            var vel = new VelocityComponent {Value = (gameSettings.bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) + velocity.Value};

            commandBuffer.SetComponent(entityInQueryIndex, bulletEntity, vel);

        }).ScheduleParallel();
        
        m_BeginSimECB.AddJobHandleForProducer(Dependency);
    }
}
```

* Now we need a PlayerDestructionSystem that will destroy the player entities
* Create PlayerDestructionSystem and paste the code snippet below into PlayerDestructionSystem.cs:

```
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;

//We are going to update LATE once all other systems are complete
//because we don't want to destroy the Entity before other systems have
//had a chance to interact with it if they need to
[UpdateInGroup(typeof(LateSimulationSystemGroup))]
public partial class PlayerDestructionSystem : SystemBase
{
    private EndSimulationEntityCommandBufferSystem m_EndSimEcb;    

    protected override void OnCreate()
    {
        //We grab the EndSimulationEntityCommandBufferSystem to record our structural changes
        m_EndSimEcb = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    
    protected override void OnUpdate()
    {
        //We add "AsParallelWriter" when we create our command buffer because we want
        //to run our jobs in parallel
        var commandBuffer = m_EndSimEcb.CreateCommandBuffer().AsParallelWriter();

        //We now any entities with a DestroyTag and an PlayerTag
        //We could just query for a DestroyTag, but we might want to run different processes
        //if different entities are destroyed, so we made this one specifically for Players
        Entities
        .WithAll<DestroyTag, PlayerTag>()
        .ForEach((Entity entity, int entityInQueryIndex) =>
        {
            commandBuffer.DestroyEntity(entityInQueryIndex, entity);

        }).WithBurst().ScheduleParallel();

        //We then add the dependencies of these jobs to the EndSimulationEntityCOmmandBufferSystem
        //that will be playing back the structural changes recorded in this sytem
        m_EndSimEcb.AddJobHandleForProducer(Dependency);
    
    }
}
```

* Notice that the PlayerDestructionSystem is nearly identical to the AsteroidDestructionSystem
  * If they are so similar, then why not just make a "DestructionSystem" that destroys anything with a destroy tag?
    * The reason is because this gitbook has a NetCode section where Player destruction needs to follow a player-specific process, so that's why we set it up this way here
  * Then why not make a "GeneralDestructionSystem" that has .WithNone\<PlayerTag>() and .WithAll\<DestroyTag>() to destroy all entities that need to be destroyed that aren't player entities?
    * First off, the purpose of this gitbook is not to illustrate excellent game architecture, but to show the "how" of putting different Unity technologies together. But even so, a single destruction system is bad software engineering for ECS. Instead, it's better to learn how to make tight, focused Systems that touch exactly as much as they're supposed to. Building a mega-huge Destruction System would make it hard to compartmentalize.&#x20;
* Hit "play", spawn your player, move around, then hit "p" to self-destruct

![Update InputSpawnSystem and add PlayerDestructionSystem](/files/-MQ2ob_vgjKTxBqtFMpd)

{% hint style="success" %}
We now can hit "p" to self-destruct

* We updated InputSpawnSystem to add a DestroyTag when "p" is pressed
* We created PlayerDestructionSystem to destroy our player entity
  {% endhint %}

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-Updating-and-Destroying-Asteroids>

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Shooting-and-Destroying-Player'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## One more (extremely rad that shows off DOTS) thing...

> Fast enter play-mode in Unity is not the default because most projects are game object based and game object based projects have a tendency to use tons of static variables for state. And doing that by default makes it so that a domain reload is required to reset all the static state before entering playmode.\
> \
> By design, everything we do in DOTS avoids this pattern. Multiple worlds, per world singletons etc.\
> They exist so that it becomes trivial to turn on the faster enter play mode option.\
> \
> This is the recommended setting in a DOTS based project:\
> ![upload\_2020-10-8\_10-52-42.png](https://forum.unity.com/attachments/upload_2020-10-8_10-52-42-png.713184/)
>
> From [Joachim (the founder of Unity)](https://forum.unity.com/threads/protip-super-fast-enter-playmode-times-when-working-in-dots.984543/)

* Because this gitbook is a DOTS-based project we can enable "Enter Play Mode Settings Options"
* First, hit "play" and notice how long it takes to load the game
* Now navigate to "File", choose "Build Settings", then "Player Settings...", then "Editor", then scroll to the bottom to "Play Mode Settings" and enable "Enter Play Mode Settings Options"
* Save and then navigate back to SampleScene and hit "play"

![Enable "Enter Play Mode Settings Options" to speed up load of "play"](/files/-MQ3DDCo3Om2ET5FMvj1)

* Holy moly! Start up is less than a second!
  * Why didn't we make this clear at the beginning of the gitbook?
  * To learn it, you must earn it 🙃
  * (actually because we needed to understand how DOTS worked to make sense of Joachim's post and not just assume it is a magic toggle)


# Publish Builds in Unity ECS

Workflow for deploying project using Unity's new Build Configurations

## What you'll develop on this page

![Run the project from a Build Configuration file](/files/-MQ3TVQRov-6vRMGEfuL)

We will be able to deploy our project using Unity's new build pipeline.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Building-the-Project/>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Creating a build configuration

#### First, some background:

Before DOTS, the way to build a project was to go to "File" and choose "Build Settings" and set up the build.

![Non-DOTS process to build the project](/files/-MQ300fezbptvtdgotOU)

But now with SubScenes, this process no longer works. We need to create Build Configurations and build our projects from there. This is because we are using a new build pipeline built for DOTS projects. The previous method is for MonoBehaviour projects. We will be using a hybrid approach, but because we use DOTS, we must use this new build process.

![Demonstrating the option to create an "Empty Build Configuration"](/files/-MQ32trZU9wD23O-RQsG)

These Build Configurations have some great benefits where you can define a "base" Build Configuration, and have platform specific builds inherit from the base build. So if you add a new scene to your project, rather than having to add the new scene to every Build Configuration for each platform, you can add the new scene to your "base" Build Configuration and it will be included in each inherited Build Configuration.

* We are going to add a new line to our package manifest

```
"com.unity.platforms": "0.50.0-preview.4",
```

* We can now see new options to create Build Configurations when we right-click and hit "create"

![Adding the platform package to our manifest.json](/files/-MQ34sJDrQj-It145Vgz)

![Now the new Build Configuration options are visible under "Build"](/files/-MQ351CCLqA_wEr280P6)

* Create a BuildSettings folder with in the Assets folder
* Navigate to the BuildSettings folder and create an "Empty Build Configuration" and name it "Base"
  * This is the "base" Build Configuration that platform-specific builds will inherit from
* Add a "General Settings" component to Base
  * In General Settings, update the Company Name and Product Name fields
* And a "Scene List" component
  * Drop down the Scene Infos menu and click "Add Element"
  * Drag in SampleScene into the Scene field under Element name, and check Auto Load
* Hit "apply" to save the changes

![Creating BuildSettings folder and the Base Build Configuration](/files/-MQ3NPZ1K9WsmqlzHLUp)

This is our "base" information that we will apply to all different builds for different platforms. (We promise that will make sense in a second).

* Now right click in /BuildSettings, choose "create", then "Build" and create a new "Empty Build Configuration"
  * Name it "Windows" or "MacOS" based on your development environment
* Select the newly created Build Configuration and click "Add Component", select "Classic Build Profile" and choose Windows/Mac from the dropdown
* &#x20;Click "+ Add Configuration" under Shared Configurations at the top of the Inspector than drag "Base" into the Shared Configuration field
* You will see that we have inherited the General Settings and Scene List from Base
* Hit "apply" at the bottom of Inspector and then click "Build and Run" at the top of the Inspector

![Creating our MacOS Build Configuration then hitting Build and Run](/files/-MQ3OQ0ojxiIjI0lFAYS)

![Our built project](/files/-MQ3PxgnO_qJuoCrjjI0)

* Great, now we know how to build a Base Build Configuration and inherit to platform-specific Builds
* Rather than having to make these ourselves for each and every platform, let's instead grab all the pre-made Build Configurations that Unity has created
* Delete our "Base" and MacOS/Windows Build Configuration from our Build Settings folder and replace them with our downloaded Build Configurations
  * Download each of the Build Configuration files in [Unity's EntityComponentSystemSamples repo](< https://github.com/Unity-Technologies/EntityComponentSystemSamples>) by cloning the repo or downloading a zip, etc. and dragging the files into the BuildSettings folder in your Project folder
    * FYI: If you downloaded the full Unity ECS Samples repo, the BuildConfigurations files are under ECSSamples > Assets > BuildConfigurations
* Update the Product Name and Company Name fields in "BaseBuildConfiguration" General Settings and drag in SampleScene to Scene under Scene List
* Now select a build configuration for your development platform
  * If you are developing on Mac you would choose "macOS-Build"
  * Click "+ Add Configuration" under Shared Configurations at the top of the Inspector
  * Drag "BaseBuildConfiguration" into the Shared Configuration field (drag gently, so that macOS-Build stays selected)&#x20;
    * You will see that we have inherited the General Settings and Scene List from Base
    * Hit "apply" at the bottom of Inspector and then click "Build and Run" at the top of the Inspector
* Hit "Build and Run" in Inspector
  * The first time you hit build and run you may encounter an error, don't worry that is Unity getting ready to build, hit it again and you will be set

![Updating to Unity's sample Build Configurations](/files/-MQ3TFX-o2mm9HeVgUIC)

![Running our project built from Unity's sample Build Configurations](/files/-MQ3TB_d_q0oqvKvUXIJ)

{% hint style="success" %}
We can now build for specific target platforms

* We created a BuildSettings folder in Assets and added Build Configurations
* We updated our Base Build Configuration and applied so all platform-specific Build Configurations would be updated
  {% endhint %}

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Building-the-Project/>

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Building-the-Project'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Intro to Unity DOTS Physics

Full workflows and code on how to get started with Unity DOTS Physics

## What you'll develop in this DOTS Physics section

![Shooting red bullets to destroy asteroids, powered by DOTS Physics](/files/-MQ5CjtRdXTKkq4oc39V)

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Bullets-to-Destroy>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

### Functionalities included

* Unity DOTS Physics
  * Using FixedStepSimulationGroup for custom systems
* Physics Body
  * Adjusting PhysicsVelocity programmatically
* Physics Shape
  * Updating shape type to prefab
* Physics Categories
  * Custom categories for collisions and interactions
* Triggers on collisions
  * Changing stateless triggers to buffer of stateful triggers
* Acting on triggers to change materials and destroy entities

## Unity DOTS Physics

### Unity Resources

Unity's 2019 overview of physics in DOTS: <https://www.youtube.com/watch?v=tI9QfqQ9ATA> **Recommended to watch.**

Unity's 2020 update of physics: <https://www.youtube.com/watch?v=n_5RGdF7Doo> **If interested in the latest and greatest.**

Link to Unity's DOTS Physics samples: <https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/UnityPhysicsSamples/Documentation/samples.md> **Good place to see the contents of the Physics Samples without opening Unity Editor (ReadMe is missing some samples that are contained in the repo so a little misleading)**

Unity's DOTS Physics v0.50.0-preview\.43 documentation overview: <https://docs.unity3d.com/Packages/com.unity.physics@0.50/manual/getting_started.html>. **Key to get an understanding**

Unity DOTS Physics forum: <https://forum.unity.com/forums/dots-physics.422/> **Helpful to find answers to questions.**

{% hint style="info" %}
🔑 **Major Key Alert** 🔑

A good way to approach implementing DOTS Physics is by checking out the DOTS Physics samples and finding an example similar to what you're looking to implement, and then copying, pasting, and modifying to your needs. This is the approach we will use in this section.

The current Physics documentation is difficult and trying to build these functionalities from scratch is a Sisyphean task. It's much simpler to grab code from Unity's samples since there's a lot of "boilerplate" for a majority of the Physics interactions.

Checkout how Code Monkey follows this technique in his "Getting Started with Unity DOTS Physics" video: <https://www.youtube.com/watch?v=B3SFWm9gkL8>
{% endhint %}

## Setting up for this gitbook section on DOTS Physics

* Go to <https://github.com/Unity-Technologies/EntityComponentSystemSamples> and download the repository for Unity's ECS Samples (if you haven't already)

  * Or you could use:

  `git clone https://github.com/Unity-Technologies/EntityComponentSystemSamples.git`

  * in terminal to download the repository
* Open the downloaded repository navigate to PhysicsSamples > Assets > Demos > 2. Setup >  2d. Events > 2d1. Events - Triggers.unity
  * This will warn you that our Unity version (2020 LTS) is different than the sample version
  * This is okay and will not cause issues so click Continue

![Download Unity ECS Samples repo](/files/-MQ3lOOW9IcekNtjfu23)

![Showing Unity Physics sample](/files/-MQ3oX00H8gPWbSxNzDM)

* This is a good sample because it shows both triggers and collisions
  * Breaking the translucent red cubes triggers gravity to switch
  * The balls collide with each other
* Navigate into the "2d1. Triggers" folder and open "2d1a. Triggers - Change Material"

![Navigating to Change Material scene](/files/-MQ3p5huStS_yJtLz_nK)

* We will base our Physics updates to our project on this sample
* The balls change colors when passing through the colored prisms and collide with the block objects
* Our bullets will pass through and change the material of asteroids and players (similar to how the balls pass through the prisms)
* Bullets passing through an asteroid or player will also trigger adding a DestroyTag to those entities which will cause them to be destroyed
* Select different prefab GameObjects in the Hierarchy (ex: "Floor" under Physics Scene Basic Elements), and notice the Physics Shape component in the Inspector
  * Under "Collision Filter" in the Material section (still in the Physics Shape component) check out the "Belongs To" and "Collides With" fields
    * If you can't find Collision Filter, expand the Physics Shape component section
  * Different GameObjects in the Hierarchy have different values set for "Belongs To" and "Collides With"
  * This is how we tell Unity Physics what entities interact with each other
* Scroll to the bottom of the "Collides With" drop down list and select "Edit Physics Category Names" to be taken to PhysicsCategoryNames in the main Assets folder
* Take a look at the different categories for all the samples in the repo

![Navigating through GameObjects in the scene Hierarchy and taking a look at Collision Filter fields](/files/-MQ3tJoDJD-O3PH0nf2O)

* We can see that in DOTS Physics, the Physics Shape component on our prefabs will decide what the prefab interacts with
* Also in the Physics Shape component under "Material" there is a field called "Collision Response" with 4 options:
  * Collide (bang together, which our players and asteroids will do)
  * Collide and Raise Collision Events (bang together and cause an event; we will not be using this in this gitbook only because we do not want to over-complicate things by doing too much)
  * Raise Trigger Events (not bang together but cause a trigger event, like our bullets will do to players and asteroids)
  * None (nothing happens from collisions)

**To best prepare for the following DOTS Physics code-alongs, we recommend you complete the following check-list:**

* [ ] Watch a Physics explanation video(s)
* [ ] Read through Unity's DOTS Physics overview documentation
* [ ] Download and open Unity's Physics samples in the repo you just downloaded above
* [ ] Navigate sample scene Hierarchies and check out the different Physics Body and Physics Shape settings to get a feel of different set ups

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Use DOTS Physics for Prefabs, Players, and Bullets

Workflows and code to update previous ECS section to use DOTS Physics

## What will be developed on this page

![Asteroids, player and bullets using DOTS Physics](/files/-MQ4UHu_jFIMudk2WVcM)

We will update our player, asteroids and bullets prefabs to run using DOTS Physics.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Adding-Physics-To-Asteroids-Players-and-Bullets>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Adding DOTS Physics to asteroids

#### First, some background:

The key "ingredients" of working with Unity Physics in this gitbook are:\
\
**Physics Shape** (This will drive how/if a prefab causes triggers/collisions)\
**Physics Body** (This will drive motion with things like gravity and linear/angular velocity)\
**PhysicsCategoryNames** (to help us define which prefabs interact with each other)

> Since Unity Physics is purely based on DOTS, rigid bodies are represented with component data on the Entities. The simplified **Physics Body** and **Physics Shape** view that you have in the Editor is actually composed of multiple data components under the hood at runtime. This allows more efficient access and to save space for static bodies which do not require some of the data.
>
> The current set of data components for a rigid body is as follows:

| Component              | Description                                                                                                                                                                              |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PhysicsCollider`      | The shape of the body. Needed for any bodies that can collide.                                                                                                                           |
| `PhysicsWorldIndex`    | Shared component required on any Entity that is involved in physics simulation (body or joint). Its Value denotes the index of physics world that the Entity belongs to (0 for default). |
| `PhysicsVelocity`      | The current linear and angular velocities of a dynamic body. Needed for any body that can move.                                                                                          |
| `PhysicsMass`          | The current mass properties (center of mass and inertia) of a dynamic body. Assumed to be infinite mass if not present.                                                                  |
| `PhysicsDamping`       | The amount of damping to apply to the motion of a dynamic body. Assumed to be zero if not present.                                                                                       |
| `PhysicsGravityFactor` | The scalar for how much gravity should affect a dynamic body. Assumed to be 1 if not present.                                                                                            |
| `PhysicsCustomData`    | Custom flags applied to the body. They can be used for certain collision event applications. Assumed to be zero if not present.                                                          |

> All physics bodies require components from `Unity.Transforms` in order to represent their position and orientation in world space. Physics ignores any scale of rigid bodies. Any scale applied to converted GameObjects is baked into a `CompositeScale` component (to preserve the scale of the render mesh at bake time) and the `PhysicsCollider` component (to approximate the scale of the physics geometry at bake time).
>
> Dynamic bodies (i.e., those with `PhysicsVelocity`) require `Translation` and `Rotation` components. Their values are presumed to be in world space. As such, dynamic bodies are unparented during entity conversion.
>
> Static bodies (i.e., those with `PhysicsCollider` but without `PhysicsVelocity`) require at least one of either `Translation`, `Rotation`, and/or `LocalToWorld`. For static bodies without a `Parent`, physics can read their `Translation` and `Rotation` values directly, as they are presumed to be in world space. World space transformations are decomposed from `LocalToWorld` if the body has a `Parent`, using whatever the current value is (which may be based on the results of the transform systems at the end of the previous frame). For best performance and up-to-date results, it is recommended that static bodies do not have a `Parent`.
>
> From [DOTS Physics Documentation](https://docs.unity3d.com/Packages/com.unity.physics@0.50/manual/getting_started.html)

Run a Unity Sample and take a look at the DOTS Windows to better understand how DOTS Physics works.

![View of DOTS Systems window when running demo](/files/zbt8ZoWG7e081iw1ZZ6U)

> You can usually consider the physics simulation as a monolithic process whose inputs are components described in [core components](https://docs.unity3d.com/Packages/com.unity.physics@0.50/manual/core_components.html) and outputs are updated `Translation`, `Rotation` and `PhysicsVelocity` components. However, internally, the Physics step is actually broken down into smaller subsections, the output of each becomes the input to the next. Unity Physics currently gives you the ability to read and modify this data, if it is necessary for your gameplay use cases.
>
> From [DOTS Physics Modifying simulation behavior documentation](https://docs.unity3d.com/Packages/com.unity.physics@0.50/manual/simulation_modification.html)

![12:03 "Overview of physics in DOTS - Unite Copenhagen" video](/files/-MQ5jiOI_1cdzG82364x)

In the diagram above (presented at Unity's DOTS Physics talk at their Unite conference) you can see what takes place between Build Physics World and Export Physics world. The simulation first runs through the Collision World and Dynamics World.&#x20;

**Although we will use DOTS Physics** as the simulation backend in this gitbook, it is also possible to use Havok Physics as the back-end; [check out the Unite talk above](https://www.youtube.com/watch?v=tI9QfqQ9ATA) if you're interested in learning more.

![13:58 "Overview of physics in DOTS - Unite Copenhagen" video](/files/-MQ5k-mMoN-md_H7e2cq)

The main run-time components in the list above are the components that are worked on in DOTS Physics simulations. These components are *read* *from* BuildPhysicsWorld then *written* *to* in ExportPhysicsWorld.

![14:55 "Overview of physics in DOTS - Unite Copenhagen" video](/files/-MQ5k8jVxAo25eeX38wb)

> So in terms of the actual main runtime systems, we've got the BuildPhysicsWorld, StepPhysicsWorld, ExportPhysicsWorld, and then this final sort of catch-all EndFramePhysics system. And one common feature of all these is that they have this sort of final job handle exposed. So, the idea is that if you have some job that you want to execute -- let's say after you built the Physics World -- you may want to be querying against the static geometry, and that can happen in parallel with the simulation because you know that the stuff that you're querying against isn't going to be moving. You just need to make sure you not only update after the BuildPhysicsWorld but that your job has a dependency on the BuildPhysicsWorld final job handle. As I alluded to before, the two key products of the BuildPhysicsWorld step are this CollisionWorld and the DynamicsWorld.
>
> Video transcription from [14:55 "Overview of physics in DOTS - Unite Copenhagen" video](https://www.youtube.com/watch?v=tI9QfqQ9ATA\&t=14m55s)

As you probably understood from reading the video transcription above, it is possible to take advantage of how DOTS Physics sets up for simulation and schedule jobs, which have dependencies on any of those main run-time systems.

In the next section we will implement TriggerEventConversionSystem which uses the FinalSimulationJobHandle of the StepPhysicsWorld. The output of this system is then added as an InputDependency to the EndFramePhysics system.

![16:42 "Overview of physics in DOTS - Unite Copenhagen" video](/files/-MQ5k_8Q_O6UEskUPPcK)

> So there's also some more advanced stuff in here depending upon how deep you want to go. If you think about cutting up the Physics system into (1) Broad Phase (just getting overlapping pairs), (2) Narrow Phase (where you're generating contact points), and the (3) Solver (where you're integrating and applying constraints and so forth), we have different custom job types where you can actually sort of inject in-between these systems to make modifications. This is really important if you're doing, let's say, a racing game. for example. You might need to do some modifications at a very low level in order to achieve the types of behaviors that you would expect in those scenarios. For example, you can create an IBodyPairsJob that would execute in between the Broad Phase and Narrow phase, so you could maybe filter out pairs of bodies that otherwise would overlap because something about your game state has changed. Or, between the Narrow Phase and the Solver, you can modify contact points. So you could say, "oh, I happen to know the bodies that have this particular tag. I need to modify the normals of the contact to some value," and so that's something that you can do before it's passed the Solver. Anytime after the Solver has completed, but before the end of your frame, you can schedule collision event jobs or trigger event jobs, so that's what you would expect. It's just responding to different contact or overlap events and having different aspects of gameplay or audio effects or things like that play out.
>
> Video transcription from [16:42 "Overview of physics in DOTS - Unite Copenhagen"](https://www.youtube.com/watch?v=tI9QfqQ9ATA\&t=16m42s)

TriggerEventConversionSystem uses the ITriggerEventsJob interface (pretty hardcore!) We take the output of the Solver's trigger events and update them to "stateful" trigger events (more on that in the next section).

Quick recap:

* DOTS Physics takes in relevant ECS component data for BuildPhysicsWorld
* Physics Shape and Physics Body are actually collections of those components
  * PhysicsCollider, PhysicsVelocity, PhysicsMass, PhysicsDamping, PhysicsGravityFactor, PhysicsCustomData
* Between BuildPhysicsWorld and ExportPhysics world there are intermediate results that we can plug into
  * Like we will with TriggerEventConversionSystem in the next section
* At the end, the results of the simulation are written in ExportPhysicsWorld to the relevant components
  * Our simulation back-end is Unity DOTS Physics

But what about Physics Category Names that was mentioned at the beginning of this section?

![24:05 "Overview of physics in DOTS - Unite Copenhagen" video](/files/-MQ5mVNLp_dDmUWIVfN4)

> So, before with classic GameObject-based Physics, an individual GameObject could only belong to one layer. We're using these layers for a lot of other things.The UI system uses it for ray-casting, the rendering system uses it for culling, and so you can very quickly run out of layers to set up collision behaviors that you would need. It's also problematic that if something is a member of a particular layer it's opting into all the behaviors of that layer. So by saying "this collider represents a body part" it then has all the characteristics of all body parts. So if you had a particular character where you said well this specific body part on this character it needs to behave like a body part for the most part but it also needs to react to this other thing, or it needs to ignore this other thing that's different from body parts. So in order to express this now we have these collision filters where you can specify the categories an object belongs to and the categories it's going to collide with. So instead of being a member in a single category, a single layer, you can be a member in any number of these 32 categories. Respectively, you can collide with any number of those 32 categories. So when we have an overlapping pair of bodies we basically are comparing "does the membership of this one body intersect with the collision response of the other body" and then vice versa. If they both have a match then we're gonna generate a collision response and if you've opted into collision events you would get those as well.
>
> Video transcription from [24:05 "Overview of physics in DOTS - Unite Copenhagen" ](https://www.youtube.com/watch?v=tI9QfqQ9ATA\&t=24m05s)

Physics Category Names are how we can define what objects collide with each other and whether or not they raise events.

#### Now let's implement:

* Very first step to Set up Physics is to add the following to our manifest.json:

```
    "com.unity.physics": "0.50.0-preview.43",
```

![Adding Physics package to our project through manifest.json](/files/-MQ41cBcDL5VjVO2VzhN)

* Next, we will add our physics Category Names
* Within the Assets folder right click, choose "Create", select "DOTS", "Physics", and "Physics Category Names"
  * Don't see "DOTS" when you right-click? You might need to Assets > Reimport All again 😩
* Then add the following categories to the list:
  * Trigger
  * Asteroid
  * Bullet
  * Player

![Creating our Physics Category Names file and adding our categories](/files/-MQ41lOHZrxnDzfUPX2P)

* These categories are named after our 3 prefabs as well as a "trigger" category
  * Trigger will be used when a bullet passes through an asteroid or player (i.e. this event will raise a trigger)
* Now let's open our Asteroid Prefab and add a Physics Shape component and Physics Body component to the prefab
* Update Physics Shape
  * Shape Type = Sphere
  * Friction = 0
  * Restitution = 1
  * Belongs to (under Collision Filter) = Asteroid
    * you may need to first select "nothing" to clear the selections, and then select Asteroid
  * Collides With = Asteroid, Bullet, Player
* Update Physics Body
  * Gravity Factor = 0
* Remove the VelocityComponent (Script) from the prefab

![Updating Asteroid prefab](/files/-MQ4B02DjB9Q4BqYcXT7)

{% hint style="info" %}
There is nothing inherently wrong with having undefined Physics Categories selected in "Belongs To" and "Collides With" in under "Collision Filter" in Physics Shape. Just to make this gitbook a bit cleaner, we decided to specify our defined categories. Rest assured, there would be no weird behavior if undefined physics categories were left selected.
{% endhint %}

* Now we will update AsteroidSpawnSystem to initialize the PhysicsVelocity component rather than the VelocityComponent
* Paste this code snippet below into AsteroidSpawnSystem.cs:

```
using System.Diagnostics;
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;
using Unity.Physics;

public partial class AsteroidSpawnSystem : SystemBase
{
    //This will be our query for Asteroids
    private EntityQuery m_AsteroidQuery;

    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimECB;

    //This will be our query to find GameSettingsComponent data to know how many and where to spawn Asteroids
    private EntityQuery m_GameSettingsQuery;

    //This will save our Asteroid prefab to be used to spawn Asteroids
    private Entity m_Prefab;

    protected override void OnCreate()
    {
        //This is an EntityQuery for our Asteroids, they must have an AsteroidTag
        m_AsteroidQuery = GetEntityQuery(ComponentType.ReadWrite<AsteroidTag>());

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimECB = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //This is an EntityQuery for the GameSettingsComponent which will drive how many Asteroids we spawn
        m_GameSettingsQuery = GetEntityQuery(ComponentType.ReadWrite<GameSettingsComponent>());

        //This says "do not go to the OnUpdate method until an entity exists that meets this query"
        //We are using GameObjectConversion to create our GameSettingsComponent so we need to make sure 
        //The conversion process is complete before continuing
        RequireForUpdate(m_GameSettingsQuery);
    }
    
    [BurstCompile]
    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_Prefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's AsteroidAuthoringComponent
            //and set m_Prefab to its Prefab value
            m_Prefab = GetSingleton<AsteroidAuthoringComponent>().Prefab;

            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        //Because of how ECS works we must declare local variables that will be used within the job
        //You cannot "GetSingleton<GameSettingsComponent>()" from within the job, must be declared outside
        var settings = GetSingleton<GameSettingsComponent>();

        //Here we create our commandBuffer where we will "record" our structural changes (creating an Asteroid)
        var commandBuffer = m_BeginSimECB.CreateCommandBuffer();

        //This provides the current amount of Asteroids in the EntityQuery
        var count = m_AsteroidQuery.CalculateEntityCountWithoutFiltering();

        //We must declare our prefab as a local variable (ECS funny business)
        var asteroidPrefab = m_Prefab;

        //We will use this to generate random positions
        var rand = new Unity.Mathematics.Random((uint)Stopwatch.GetTimestamp());

        Job
        .WithCode(() => {
            for (int i = count; i < settings.numAsteroids; ++i)
            {
                // this is how much within perimeter asteroids start
                var padding = 0.1f;

                // we are going to have the asteroids start on the perimeter of the level
                // choose the x, y, z coordinate of perimeter
                // so the x value must be from negative levelWidth/2 to positive levelWidth/2 (within padding)
                var xPosition = rand.NextFloat(-1f*((settings.levelWidth)/2-padding), (settings.levelWidth)/2-padding);
                // so the y value must be from negative levelHeight/2 to positive levelHeight/2 (within padding)
                var yPosition = rand.NextFloat(-1f*((settings.levelHeight)/2-padding), (settings.levelHeight)/2-padding);
                // so the z value must be from negative levelDepth/2 to positive levelDepth/2 (within padding)
                var zPosition = rand.NextFloat(-1f*((settings.levelDepth)/2-padding), (settings.levelDepth)/2-padding);
                
                //We now have xPosition, yPostiion, zPosition in the necessary range
                //With "chooseFace" we will decide which face of the cube the Asteroid will spawn on
                var chooseFace = rand.NextFloat(0,6);
                
                //Based on what face was chosen, we x, y or z to a perimeter value
                //(not important to learn ECS, just a way to make an interesting prespawned shape)
                if (chooseFace < 1) {xPosition = -1*((settings.levelWidth)/2-padding);}
                else if (chooseFace < 2) {xPosition = (settings.levelWidth)/2-padding;}
                else if (chooseFace < 3) {yPosition = -1*((settings.levelHeight)/2-padding);}
                else if (chooseFace < 4) {yPosition = (settings.levelHeight)/2-padding;}
                else if (chooseFace < 5) {zPosition = -1*((settings.levelDepth)/2-padding);}
                else if (chooseFace < 6) {zPosition = (settings.levelDepth)/2-padding;}

                //we then create a new translation component with the randomly generated x, y, and z values                
                var pos = new Translation{Value = new float3(xPosition, yPosition, zPosition)};

                //on our command buffer we record creating an entity from our Asteroid prefab
                var e = commandBuffer.Instantiate(asteroidPrefab);

                //we then set the Translation component of the Asteroid prefab equal to our new translation component
                commandBuffer.SetComponent(e, pos);

                //We will now set the PhysicsVelocity of our asteroids
                //here we generate a random Vector3 with x, y and z between -1 and 1
                var randomVel = new Vector3(rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f));
                //next we normalize it so it has a magnitude of 1
                randomVel.Normalize();
                //now we set the magnitude equal to the game settings
                randomVel = randomVel * settings.asteroidVelocity;
                //here we create a new VelocityComponent with the velocity data
                var vel = new PhysicsVelocity{Linear = new float3(randomVel.x, randomVel.y, randomVel.z)};
                //now we set the velocity component in our asteroid prefab
                commandBuffer.SetComponent(e, vel);

            }
        }).Schedule();

        //This will add our dependency to be played back on the BeginSimulationEntityCommandBuffer
        m_BeginSimECB.AddJobHandleForProducer(Dependency);
    }
}
```

* Navigate to SampleScene, reimport ConvertedSubScene and hit "play"
  * Is it wonky? Here's a couple (potentially) helpful debug tips:
    * &#x20;If the asteroids seem to overpopulate the screen, then check to make sure that you didn't delete the Destroy Tag component off the asteroid prefab
    * If you've lost the camera and/or sight of the player, you might need to check to make sure `HYBRID_ENTITIES_CAMERA_CONVERSION` is still added to the Player Settings
      * Navigate to "File", choose "Build settings", then "Player Settings", then "Player" on the left, expand the drop down menu for "Other Settings", and scroll down to "Scripting Define Symbols". If it's not already there, add `HYBRID_ENTITIES_CAMERA_CONVERSION`&#x20;

![Updating AsteroidSpawnSystem and seeing asteroids with DOTS Physics](/files/-MQ4Bte26USZ3VnSavn_)

{% hint style="success" %}
We now have updated our asteroid prefab to run using DOTS Physics

* We added the Physics package
* We created our Physics Category Names
* We added a Physics Shape and Physics Body to our Asteroid prefab
* We updated our AsteroidSpawnSystem to initialize asteroids with PhysicsVelocity
* We removed the VelocityComponent from the Asteroid prefab
  {% endhint %}

## Adding DOTS Physics to Player

* We need to update our Player prefab similar to how we updated our Asteroid prefab
* Now let's open our Player Prefab and add a Physics Shape component and Physics Body component to the prefab
* Update Physics Shape
  * Shape Type = Capsule
    * Notice how when we initially switch to Capsule the shape tries to encompass all GameObjects; we will update this so only the capsule portion is contained within the Physics shape
  * Radius = 0.5
  * Center = (0, 0, 0,)
  * Friction = 0
  * Restitution = 1
  * Belongs to = Player
  * Collides With = Asteroid, Bullet, Player
* Update Physics Body
  * GravityFactor = 0
* Remove the VelocityComponent from the prefab

![Updating the Player prefab](/files/-MQ4FZX_ro1lh6k06flM)

* Now we need to update our InputMovementSystem so we update the PhysicsVelocity rather than the VelocityComponent when we add thrust
* Paste the code snippet below into InputMovementSystem.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using Unity.Physics;

public partial class InputMovementSystem : SystemBase
{
    protected override void OnCreate()
    {
        //We will use playerForce from the GameSettingsComponent to adjust velocity
        RequireSingletonForUpdate<GameSettingsComponent>();
    }

    protected override void OnUpdate()
    {
        //we must declare our local variables to be able to use them in the .ForEach() below
        var gameSettings = GetSingleton<GameSettingsComponent>();
        var deltaTime = Time.DeltaTime;

        //we will control thrust with WASD"
        byte right, left, thrust, reverseThrust;
        right = left = thrust = reverseThrust = 0;

        //we will use the mouse to change rotation
        float mouseX = 0;
        float mouseY = 0;

        //we grab "WASD" for thrusting
        if (Input.GetKey("d"))
        {
            right = 1;
        }
        if (Input.GetKey("a"))
        {
            left = 1;
        }
        if (Input.GetKey("w"))
        {
            thrust = 1;
        }
        if (Input.GetKey("s"))
        {
            reverseThrust = 1;
        }
        //we will activate rotating with mouse when the right button is clicked
        if (Input.GetMouseButton(1))
        {
            mouseX = Input.GetAxis("Mouse X");
            mouseY = Input.GetAxis("Mouse Y");

        }

        Entities
        .WithAll<PlayerTag>()
        .ForEach((Entity entity, ref Rotation rotation, ref PhysicsVelocity velocity) =>
        {
            if (right == 1)
            {   //thrust to the right of where the player is facing
                velocity.Linear += (math.mul(rotation.Value, new float3(1,0,0)).xyz) * gameSettings.playerForce * deltaTime;
            }
            if (left == 1)
            {   //thrust to the left of where the player is facing
                velocity.Linear += (math.mul(rotation.Value, new float3(-1,0,0)).xyz) * gameSettings.playerForce * deltaTime;
            }
            if (thrust == 1)
            {   //thrust forward of where the player is facing
                velocity.Linear += (math.mul(rotation.Value, new float3(0,0,1)).xyz) * gameSettings.playerForce * deltaTime;
            }
            if (reverseThrust == 1)
            {   //thrust backwards of where the player is facing
                velocity.Linear += (math.mul(rotation.Value, new float3(0,0,-1)).xyz) *  gameSettings.playerForce * deltaTime;
            }
            if (mouseX != 0 || mouseY != 0)
            {   //move the mouse
                //here we have "hardwired" the look speed, we could have included this in the GameSettingsComponent to make it configurable
                float lookSpeedH = 2f;
                float lookSpeedV = 2f;

                //
                Quaternion currentQuaternion = rotation.Value; 
                float yaw = currentQuaternion.eulerAngles.y;
                float pitch = currentQuaternion.eulerAngles.x;

                //MOVING WITH MOUSE
                yaw += lookSpeedH * mouseX;
                pitch -= lookSpeedV * mouseY;
                Quaternion newQuaternion = Quaternion.identity;
                newQuaternion.eulerAngles = new Vector3(pitch,yaw, 0);
                rotation.Value = newQuaternion;
            }
        }).ScheduleParallel();
    }
}
```

* After you have updated the InputMovementSystem, navigate to SampleScene, reimport ConvertedSubScene and hit "play"

![Moving our player after updating InputMovementSystem to adjust the PhysicsVelocity](/files/-MQ4IA--7eToLT1GM4Sf)

* You might notice that you are unable to spawn bullets
  * This is because our .ForEach() in InputSpawnSystem runs a query on entities with a VelocityComponent
  * Because our player entity no longer has a VelocityComponent, it does not appear as an entity in the .ForEach()
  * Not to worry, we will update this when we add DOTS Physics to the bullet (next section)
* Also, it's a bit tight in the scene. Let's update our Game Settings to have a width, height and depth of 40
* Return to SampleScene, reimport ConvertedSubScene, and hit "play"

![Updating our game to have more room for navigation](/files/-MQ4IWlHYbFrlJOJZMoC)

{% hint style="success" %}
&#x20;Our Player prefab is now updated with DOTS Physics

* We updated our Player prefab with a Physics Shape and Physics Body
* We updated InputMovementSystem to adjust PhysicsVelocity (rather than the VelocityComponent)
  {% endhint %}

## Adding DOTS Physics to Bullet prefabs

* We need to update our Bullet prefab similar to how we updated our Asteroid and Player prefabs
* So let's open our Bullet Prefab and add a Physics Shape component and Physics Body component to the prefab
* Update Physics Shape
  * Shape Type = sphere
  * Friction = 0
  * Restitution = 1
  * Collision Response = **Raise Trigger Events**
  * Belongs to = Bullet
  * Collides With = **Trigger**, Asteroid, Bullet, Player
* Update Physics Body
  * Gravity Factor = 0
* Remove the VelocityComponent from the prefab

![](/files/-MQ4Kuc2OaNF_TNoMVKX)

* Now we need to update our InputSpawnSystem to initialize the bullet PhysicsVelocity instead of its VelocityComponent (which we just deleted)
* Paste the code snippet below into InputSpawnSystem.cs:

```
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;
using Unity.Physics;

public partial class InputSpawnSystem : SystemBase
{
    //This will be our query for Players
    private EntityQuery m_PlayerQuery;

    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimECB;

    //This will save our Player prefab to be used to spawn Players
    private Entity m_PlayerPrefab;

    //This will save our Bullet prefab to be used to spawn Players
    private Entity m_BulletPrefab;

    //We are going to use this to rate limit bullets per second
    //We could have included this in the game settings, no "ECS reason" not to
    private float m_PerSecond = 10f;
    private float m_NextTime = 0;

    protected override void OnCreate()
    {
        //This is an EntityQuery for our Players, they must have an PlayerTag
        m_PlayerQuery = GetEntityQuery(ComponentType.ReadWrite<PlayerTag>());

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimECB = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We need the GameSettingsComponent to grab the bullet velocity
        //When there is only 1 instance of a component (like GamesSettingsComponent) we can use "RequireSingletonForUpdate"
        RequireSingletonForUpdate<GameSettingsComponent>();
    }
    
    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_PlayerPrefab == Entity.Null || m_BulletPrefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's PlayerAuthoringCOmponent
            //and set m_PlayerPrefab to its Prefab value
            m_PlayerPrefab = GetSingleton<PlayerAuthoringComponent>().Prefab;
            m_BulletPrefab = GetSingleton<BulletAuthoringComponent>().Prefab;

            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        byte shoot, selfDestruct;
        shoot = selfDestruct = 0;
        var playerCount = m_PlayerQuery.CalculateEntityCountWithoutFiltering();

        if (Input.GetKey("space"))
        {
            shoot = 1;
        }
        if (Input.GetKey("p"))
        {
            selfDestruct = 1;
        }

        //If we have pressed the space bar and there is less than 1 player, create a new player
        //This will be false after we create our first player
        if (shoot == 1 && playerCount < 1)
        {
            var entity = EntityManager.Instantiate(m_PlayerPrefab);
            return;
        }

        var commandBuffer = m_BeginSimECB.CreateCommandBuffer().AsParallelWriter();
        //We must declare our local variables before the .ForEach()
        var gameSettings = GetSingleton<GameSettingsComponent>();
        var bulletPrefab = m_BulletPrefab;

        //we are going to implement rate limiting for shooting
        var canShoot = false;
        if (UnityEngine.Time.time >= m_NextTime)
        {
            canShoot = true;
            m_NextTime += (1/m_PerSecond);
        }

        Entities
        .WithAll<PlayerTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in Translation position, in Rotation rotation,
                in PhysicsVelocity velocity, in BulletSpawnOffsetComponent bulletOffset) =>
        {
            //If self destruct was pressed we will add a DestroyTag to the player entity
            if(selfDestruct == 1)
            {
                commandBuffer.AddComponent(entityInQueryIndex, entity, new DestroyTag {});
            }
            //If we don't have space bar pressed we don't have anything to do
            if (shoot != 1 || !canShoot)
            {
                return;
            }
            
            // We create the bullet here
            var bulletEntity = commandBuffer.Instantiate(entityInQueryIndex, bulletPrefab);
            
            //we set the bullets position as the player's position + the bullet spawn offset
            //math.mul(rotation.Value,bulletOffset.Value) finds the position of the bullet offset in the given rotation
            //think of it as finding the LocalToParent of the bullet offset (because the offset needs to be rotated in the players direction)
            var newPosition = new Translation {Value = position.Value + math.mul(rotation.Value, bulletOffset.Value).xyz};
            commandBuffer.SetComponent(entityInQueryIndex, bulletEntity, newPosition);


            // bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) takes linear direction of where facing and multiplies by velocity
            // adding to the players physics Velocity makes sure that it takes into account the already existing player velocity (so if shoot backwards while moving forwards it stays in place)
            var vel = new PhysicsVelocity {Linear = (gameSettings.bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) + velocity.Linear};

            commandBuffer.SetComponent(entityInQueryIndex, bulletEntity, vel);

        }).ScheduleParallel();
        
        m_BeginSimECB.AddJobHandleForProducer(Dependency);
    }
}
```

* Once you have updated InputSpawnSystem navigate back to SampleScene, reimport ConvertedSubScene, then hit "play"

![](/files/-MQ4MrphSovWcfzx4lvc)

* Great, now our prefabs work with Unity DOTS Physics!
  * DOTS Physics is accomplishing that what VelocityComponent and MovementSystem were doing before: taking in ECS data, running a simulation, then writing out the results
* Because we set our bullet Collision Response to "Raise Trigger Events," the bullets do not collide with asteroids or players
  * They are space bullets and they penetrate right through entities like [Trisolarian droplets](https://aliens.fandom.com/wiki/Trisolaran)&#x20;
* We can now delete VelocityComponent and MovementSystem from our project completely
  * No gif here to show you how; we believe in you 💪

{% hint style="success" %}
Our bullet prefab is now updated with DOTS Physics&#x20;

* we updated our Bullet prefab with a Physics Shape and Physics Body component
* We updated the InputSpawnSystem to initialize PhysicsVelocity
  {% endhint %}

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Adding-Physics-To-Asteroids-Players-and-Bullets>

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Adding-Physics-To-Asteroids-Players-and-Bullets'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Use DOTS Physics for Collisions

Code and workflows to update collisions that change material and destroy prefab entities using DOTS Physics

## What will be developed on this page

![](/files/-MQ5COhvOVITJX8axjCP)

We will update our project so that bullet collisions cause the material of the colliding entity to change and for the entity to be destroyed

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Bullets-to-Destroy>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Adding trigger events

#### First, some background:

We have mostly used .ForEach() and Job.WithCode() in this project. At their core, both of these interfaces run "jobs." When we implement our trigger events for bullet collisions we will also be using jobs, but some of the code will be using the core job interface. In some of the files we will be examining you can see the "job" struct.

* Open the downloaded EntityComponentSystemSamples repo and navigate to Demos > 2. Setup > 2d. Events > Scripts > Stateful and Demos > 2. Setup > 2d. Events > 2d1. Triggers > Scripts
* There are 6 files that we are going to base our bullet interactions on
  * in /2d. Events/Scripts/Stateful
    * 1\. IStatefulSimulationEvent.cs
    * 2\. StatefulSimulationEventBuffers.cs
    * 3\. StatefulTriggerEvent.cs
    * 4\. StatefulTriggerEventBufferAuthoring.cs
    * 5\. StatefulTriggerEventBufferSystem.cs
  * in /2d. Events/2d1. Triggers/Scripts
    * 6\. TriggerVolumeChangeMaterialAuthoring.cs

The first 5 files purpose is to take DOTS Physics TriggerEvents (which are stateless) and convert them to StatefulTriggerEvents. "Stateless" means that Unity Physics doesn't know what happened "before" ("before" = the previous "state"). So when 2 Physics bodies interact, there is no iterface to know if they "just" interacted this frame, or if they have already interacted.&#x20;

Classic MonoBehavior physics provided stateful methods like "[OnTriggerEnter](https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnTriggerEnter.html)" and "[OnTriggerStay](https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnTriggerStay.html)" and "[OnTriggerExit](https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnTriggerExit.html)". The first 5 files in /2d. Events/Scripts/Stateful job is to provide the same type of functionality when using DOTS Physics. The ability to know when an interaction is Enter/Stay/Exit.

* The states are defined in  IStatefulSimulationEvent.cs as an enum "StatefulEventState"
  * Undefined
  * Enter
  * Stay
  * Exit
* StatefulTriggerEvent.cs defines the new StatefulTriggerEvent interface which is very similar to TriggerEvent but with the addition of the StatefulEventState
* StatefulTriggerEventBufferAuthoring.cs is used to for Authoring and is meant to be placed on prefabs to provide it a component that can hold a buffer of StatefulTriggerEvents
* StatefulSimulationEventBuffers.cs provides jobs used by StatefulTriggerEventBufferSystem.cs to convert TriggerEvents into StatefulEventStates and place them in the buffer provided by StatefulTriggerEventBufferAuthoring.cs
* TriggerVolumeChangeMaterialAuthoring.cs uses StatefulTriggerEvents on the prefabs to change materials based on interactions

Let's take a deeper look into StatefulTriggerEventBufferSystem.cs to understand a bit more about Dependencies and scheduling. [Unity Physics Upgrade guide for 0.7](https://docs.unity3d.com/Packages/com.unity.physics@0.50/changelog/CHANGELOG.html#070-preview3---2021-02-24) can help us understand what is going on.

> * `RegisterPhysicsRuntimeSystemReadOnly()` and `RegisterPhysicsRuntimeSystemReadWrite()` (both registered as extensions of `SystemBase`) should be used to manage physics data dependencies instead of the old `AddInputDependency()` and `GetOutputDependency()` approach. Users should declare their `UpdateBefore` and `UpdateAfter` systems as before and additionally only call one of the two new functions in their system's `OnStartRunning()`, which will be enough to get an automatic update of the `Dependency` property without the need for manually combining the dependencies as before. Note that things have not changed if you want to read or write physics runtime data directly in your system's `OnUpdate()` - in that case, you still need to ensure that jobs from previous systems touching physics runtime data are complete, by completing the `Dependency`. Also note that `BuildPhysicsWorld.AddInputDependencyToComplete()` still remains needed for jobs that need to finish before any changes are made to the PhysicsWorld, if your system is not scheduled in between other 2 physics systems that will do that for you.

* StatefulTriggerEventBufferSystem

  * This is the system that takes the intermediate results of the Solver (which are stateless) and assigns them state (enter, stay, and exit)
  * You can see that (following the instructions from the v0.7 upgrade guide) we are declaring when this system should be run, after running the StepPhysicsWorld and before EndFramePhysicsSystem

    ```
    [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    [UpdateAfter(typeof(StepPhysicsWorld))]
    [UpdateBefore(typeof(EndFramePhysicsSystem))]
    ```
  * We also call RegisterPhysicsRuntimeSystemReadOnly() as described in the upgrade guide

  ```
      protected override void OnStartRunning()
      {
          base.OnStartRunning();
          this.RegisterPhysicsRuntimeSystemReadOnly();
      }
  ```

  * We can see in StatefulTriggerEventBufferSystem's OnUpdate() that we also follow the note from the v0.7 upgrade guide that "if you want to read or write physics runtime data directly in your system's `OnUpdate()` - in that case, you still need to ensure that jobs from previous systems touching physics runtime data are complete, by completing the `Dependency`"

  ```
          Dependency = new StatefulEventCollectionJobs.CollectTriggerEvents
          {
              TriggerEvents = currentEvents
          }.Schedule(m_StepPhysicsWorld.Simulation, Dependency);
  ```

  * We schedule a StatefulEventCollectionJobs.CollectTriggerEvents job (which itself we define as dependent on  StepPhysicsWorld.Simulation) and add the resulting dependency to StatefulTriggerEventBufferSystem's OnUpdate() job
    * That means this system is dependent on the results of that system to perform its operations
    * This makes sense because we need to grab the results of the Solver (which occurs during StepPhysicsWorld.Simulation) to get the trigger events
    * Otherwise we would be trying to convert TriggerEvents to StatefulTriggerEvents before the TriggerEvents have been calculated (no good! can't convert something that doesn't exist yet!)
  * You can see the requirements for for TriggerEvents to be transformed to StatefulTriggerEvents and stored in a Dynamic Buffer at the top of StatefulSimulationEventBuffers.cs
    * 1\) 'Raise Trigger Events' option of the 'Collision Response' on PhysicsShapeAuthoring on the entity that should raise trigger events
    * 2\) Add a StatefulTriggerEventBufferAuthoring component to that entity
    * 3\) If this is desired on a Character Controller, tick "RaiseTriggerEvents" on CharacterControllerAuthoring (and skip (1) and (2))
  * We already chose "Raise Trigger Events" on our bullet prefab so we are set there, and we will add StatefulTriggerEventBufferAuthoring on the bullet

* Now take a look at TriggerVolumeChangeMaterialAuthoring.cs

![Taking a look at TriggerVolumeChangeMaterialAuthoring from Unity DOTS Physics sample repo](/files/-MQ4annLroM42cqHOdjt)

* This file also has an implementation of the IConvertGameObjectToEntity interface
  * This adds the "TriggerVolumeChangeMaterial" Component (defined at the top of the file) to the converted GameObject
  * It will grab whatever GameObject we drag onto the public field "ReferenceGameObject" in TriggerVolumeChangeMaterialAuthoring and set it as the ReferenceEntity field of the TriggerVolumeChangeMaterial Component
  * Or, if the public field is left null,  IConvertGameObjectToEntity will set the reference entity as the GameObject too
  * This ReferenceEntity is used further down to reset the material to the same material as the ReferenceEntity
* Let's keep moving down the file...
* TriggerVolumeChangeMaterialSystem
  * There are a couple of decorators at the top of the file
    * \[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
      * FixedStepSimulationSystemGroup is the system group we reviewed in the previous section
    * \[UpdateAfter(typeof(StatefulTriggerEventBufferSystem))]
      * StatefulTriggerEventBufferSystemis the system from the previous file we just reviewed
    * This makes sense because we want to perform our actions in the simulation and after we have created our stateful triggers
  * This makes sense because we need the stateful triggers produced by TriggerEventConversionSystem to exist if we want to act on them
  * Now, in the OnUpdate() is where we find the code that causes the material to change

![Taking a look at TriggerVolumeChangeMaterialAuthoring](/files/-MQ4oJb8SViiMeHqFwmR)

* By taking a look at the code that runs when shapes stop intersecting, you can see the purpose of the ReferenceEntity field
  * The ReferenceEntity's RenderMesh is used
  * Once the intersection stops, the RenderMesh is set equal to the ReferenceEntity's RenderMesh
    * This is why the ball turns back to its original color in the sample
* We are going to update this code so that when a bullet first intersects with any entity, the other entity will have its material changed to the same material as the bullet
* "On exit" we will add a DestroyTag to the entity
* We do not need the "ReferenceEntity" because we will not be changing material back on exit. Instead, we will be able to remove TriggerVolumeChangeMaterial and TriggerChangeMaterialAndDestroyAuthoring

#### Now, let's implement:

* Copy and paste the 5 files from /2d. Events/Scripts/Stateful to /ScriptsAndPrefabs
  * 1\. IStatefulSimulationEvent.cs
  * 2\. StatefulSimulationEventBuffers.cs
  * 3\. StatefulTriggerEvent.cs
  * 4\. StatefulTriggerEventBufferAuthoring.cs
  * 5\. StatefulTriggerEventBufferSystem.cs
* as well as 3 additional files (these are used for CollisionEvents which we are not concerned with but are also referenced by our systems so they are necessary)
  * 6\. StatefulCollisionEvent.cs
  * 7\. StatefulCollisionEventBufferAuthoring.cs
  * 8\. StatefulCollisionEventBufferSystem.cs

With these 8 files we have what we need to create StatefulEventTriggers on our prefabs :muscle:

Now let's make some modifications to TriggerVolumeChangeMaterialAuthoring.cs so that bullets change the color of the Asteroid when they intersect and that the Asteroid is given a DestroyTag when the bullet exists.

* Create ChangeMaterialAndDestroySystem and paste the code snippet below into ChangeMaterialAndDestroySystem.cs:

```
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics.Stateful;
using Unity.Rendering;
using UnityEngine;

//We did not need the ReferenceEntity so we deleted the IConvertGameObjectToEntity interface
//and the TriggerVolumeChangeMaterial component

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateAfter(typeof(StatefulTriggerEventBufferSystem))]
public partial class ChangeMaterialAndDestroySystem : SystemBase
{
    private EndFixedStepSimulationEntityCommandBufferSystem m_CommandBufferSystem;

    private EntityQueryMask m_NonTriggerMask;

    protected override void OnCreate()
    {
        m_CommandBufferSystem = World.GetOrCreateSystem<EndFixedStepSimulationEntityCommandBufferSystem>();
        m_NonTriggerMask = EntityManager.GetEntityQueryMask(
            GetEntityQuery(new EntityQueryDesc
            {
                None = new ComponentType[]
                {
                    typeof(StatefulTriggerEvent)
                }
            })
        );
    }

    protected override void OnUpdate()
    {
        var commandBuffer = m_CommandBufferSystem.CreateCommandBuffer();

        // Need this extra variable here so that it can
        // be captured by Entities.ForEach loop below
        var nonTriggerMask = m_NonTriggerMask;

        Entities
            .WithName("ChangeMaterialOnTriggerEnter")
            .WithoutBurst()
            .ForEach((Entity e, ref DynamicBuffer<StatefulTriggerEvent> triggerEventBuffer) =>
            {
                for (int i = 0; i < triggerEventBuffer.Length; i++)
                {
                    var triggerEvent = triggerEventBuffer[i];
                    var otherEntity = triggerEvent.GetOtherEntity(e);

                    // exclude other triggers and processed events
                    if (triggerEvent.State == StatefulEventState.Stay || !nonTriggerMask.Matches(otherEntity))
                    {
                        continue;
                    }

                    if (triggerEvent.State == StatefulEventState.Enter)
                    {
                        var volumeRenderMesh = EntityManager.GetSharedComponentData<RenderMesh>(e);
                        var overlappingRenderMesh = EntityManager.GetSharedComponentData<RenderMesh>(otherEntity);
                        overlappingRenderMesh.material = volumeRenderMesh.material;

                        commandBuffer.SetSharedComponent(otherEntity, overlappingRenderMesh);
                    }
                    //The following is what happens on exit
                    else
                    {
                        commandBuffer.AddComponent(otherEntity, new DestroyTag {});
                    }
                }
            }).Run();

        m_CommandBufferSystem.AddJobHandleForProducer(Dependency);
    }
}
```

* Next, open the Bullet prefab and add StatefulTriggerEventBufferAuthoring
* Navigate to SampleScene, reimport ConvertedSubScene
* Hit "play" and shoot around

![Adding StatefulTriggerEventBufferAuthoring and ChangeMaterialAndDestroySystem ](/files/-MQ590MX3_WuVISfPjBO)

![](/files/-MQ59B7tfTt_EiaUL-aB)

* Woo hoo! We have a working collision trigger system 👍
  * In our testing we found that occasionally it is necessary to "Reimport All" assets for the changes to take  (go to "Assets" then select "Reimport All")
* The changing of material is... underwhelming
* Let's update the Bullet prefab to have a Red material
* In the Asset folder, right-click, choose "Create", select "Material" and name it "Red"
* Select the Red material, go to Base Map in the Inspector, and change it to a red color and save
* Select the Bullet prefab and change the Mesh Renderer material to Red
* Hit save, navigate to SampleScene, and reimport the ConvertedSubScene

![Updating the Bullet prefab to be red](/files/-MQ59cnx5mRRw9zkvb7D)

* Now hit "play" and checkout the difference

![Shooting red bullets to destroy asteroids](/files/-MQ5CE14DMHqI5Zi9CbB)

* Now it's a bit more exciting, right?! 😬
  * *Remember,* the purpose of this gitbook is to give a "how" of using Unity's DOTS packages, not to make an exciting game 🥺

{% hint style="success" %}
We now know how to make collisions using DOTS Physics

* We navigated through DOTS Physics samples to find a sample that matched our needs
* We checked out the sample scene to get an idea of the different components
* We read through Physics Samples to get an idea of what changes we wanted to make
* We copied 8 files needed to create StatefulEventTriggers and created ChangeMaterialAndDestroySystem in our project
* We added the StatefulTriggerEventBufferAuthoring to our Bullet prefab and updated the prefab to be red
  {% endhint %}

Github branch link:&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Updating-Bullets-to-Destroy'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Intro to DOTS NetCode

Full workflows and code on how to get started with Unity's DOTS NetCode

## What you'll develop in this in the NetCode section

![Spawning and destroying ghosted objects and interacting with Thin Clients](/files/-MQhJO6O9nxAOiX3wnaq)

We will spawn and destroy ghost entities and interact with Thin Clients through PlayMode Tools.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Bullets-and-Destruction>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

### Functionalities included

* Navigating multiple ECS Worlds
* Creating a ECS NetworkConnectionEntity (NCE) on the client and server
  * Creating a socket connection using NetworkStreamReceiveSystem
  * Use GhostDistanceImportance component
* Sending RPCs between server and client
  * Sending data with RPCs
  * Using InvokeExecute on receiving NCE to make updates
* Loading a game on the client
  * Using NetworkStreamInGame component
* Updating the CommandTargetComponent on a NetworkConnectionEntity
  * Setting targetEntity field to ICommandData buffer
* Creating networked entities ("Ghosts")
  * Ghost Authoring Component
    * Updating Supported Ghost Modes
* Server-spawned entities
* Sending client inputs with ICommandData
  * Predicted responses to ICommandData by using ClientSimulationSystemGroup.ServerTick
* Responding to ICommandData on both the client and server using GhostPredictionSystemGroup.ShouldPredict()
* Client-predicted entities and predicted-spawned entities
  * Adding PredictedGhostSpawnRequestComponent
* Ghost classification systems
  * Traversing GhostSpawnBuffer to locate predicted spawn entity
* Proper NetCode entity destruction
  * Server-side destruction
  * Using ISystemStateComponent component as a call back to clean up destroyed players

## Unity DOTS NetCode

Unity's DOTS NetCode is a dedicated server with client prediction networking model. If that sentence made complete sense because you are aware of multiplayer game networking terminology and architectures you can skip the rest of this page and go right to the next section called "Create a Socket Connection."Otherwise if you don't, we *highly* recommend that you read through this section and read the content we link to throughout to get an understanding of multiplayer game networking. This code-along will go much more smoothly for you 🙂🙃&#x20;

**First, start by reading**[ Unity's blog post on their decision to move to NetCode network architecture, "Navigating Unity's multiplayer NetCode transition."](https://blogs.unity3d.com/2019/06/13/navigating-unitys-multiplayer-netcode-transition/)

{% hint style="info" %}
Moetsi builds Reality Models, which can have thousands of networked objects in an environment. Although Unity's NetCode takes effort, the juice is worth the squeeze when building an XR experience with hundreds of live local players streaming the environment to remote players.&#x20;
{% endhint %}

**Next, read this** [break-down of key networking terms](<https://www.reddit.com/r/Overwatch/comments/3u5kfg/everything_you_need_to_know_about_tick_rate/ >) for a good overview of key concepts.

> **Netcode**
>
> A blanket term used to describe the network programming of a game. It's basically a meaningless blanket term used to describe the network component of game programming. It is not a technical term.
>
> From [reddit post](https://www.reddit.com/r/Overwatch/comments/3u5kfg/everything_you_need_to_know_about_tick_rate/)

The quote is important to note to demonstrate that Unity NetCode is just Unity's implementation of multiplayer networking. It is not a technical term.

**Watch this** [video explaining lag compensation and interpolation delay](< https://www.youtube.com/watch?v=6EwaW2iz4iA>).

**Read Gabriel Gambetta (the 🐐)'s** [Four-part post on Client-Server game architecture](https://www.gabrielgambetta.com/client-server-game-architecture.html). **Read all four parts. Seriously terrific.**

![From Gabriel Gambetta's blog series, a must-read.](/files/-MQ9wf85Mh8xMTd8a4JY)

**Watch** [all of Timothy Ford's talk on ECS and NetCode](https://www.youtube.com/watch?v=W3aieHjyNvw\&t=24m15s) to get a good understanding of what problems are being solved. **If you&#x20;*****must*****&#x20;skip-out, at least just watch from 24:15 - 33:05.**

![Visual representation of "client being ahead of server" from Timothy Ford's talk](/files/-MQEBvThMmuaJJCAmz0C)

{% hint style="danger" %}
NetCode is no joke

Do not continue until "predicted-client" makes sense to you.

[Watch Timothy Ford's talk from 24:15 to 33:05, **seriously.**](https://www.youtube.com/watch?v=W3aieHjyNvw\&t=24m15s)
{% endhint %}

In this NetCode section of the gitbook, we will update the project we've been building in this gitbook so that instead of our client immediately acting on player input (like in InputMovementSystem and InputSpawnSystem), we will **record** "Commands" (which are basically Components of player input) and then send these Commands to the server to be played back.

In order for there to be **immediate** responsiveness on the client-side while the client is waiting for server commands, the client will also respond to these inputs by "predicting" what will happen (this is what's meant by "predicted-client" across all of the blog posts/videos we shared above).

Once the server runs the simulation, it will send back the results in "snapshots" of "ghosted" entities. "Ghost" is the Unity term for entities whose state the server sends to the client (a networked object). The server keeps track of all Ghosts and then sends "snapshots" of their Component's "Ghost Fields." Don't worry, you'll get used to all of this terminology.&#x20;

In the updates we are about to make to our Project in the next sections of this gitbook, the asteroid, player and bullet entities will become Ghosts. Their state will be sent by the server to the clients via what Unity calls "snapshots." Once the clients receive the snapshots, the asteroid entities will have their state updated.

## **In this NetCode section, the focus will be on NetCode principles**

The previous sections focused more heavily on how to implement ECS in general. In this section, we are assuming that you completed the previous section and/or that you have general grasp of Unity ECS. This section's focus will be on Unity's specific NetCode implementation.

## Unity resources

Unity documentation for NetCode 0.6.0-preview\.7: <https://docs.unity3d.com/Packages/com.unity.netcode@0.6/manual/index.html> **Refer to this for more information.**

Unity samples for NetCode: <https://github.com/Unity-Technologies/multiplayer> **The Asteroids sample is what this gitbook is based off of.**

Unity thread for NetCode: <https://forum.unity.com/threads/dots-multiplayer-discussion.694669/> **The moderators from Unity are responsive here.**

**To best prepare for the following DOTS NetCode code-alongs, we recommend that you complete the following check-list:**

* [ ] [Watch Timothy Ford's talk](https://www.youtube.com/watch?v=W3aieHjyNvw\&t=24m15s), at least from 24:15 - 33:05
* [ ] Understand authoritative server client-predicted networking model
* [ ] Have a general understanding of Unity ECS and are familiar with the previous two sections

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

{% hint style="warning" %}
**NOTE IF STARTING FROM THIS SECTION (and you haven't done of the previous sections)**

![](/files/HwZsUM3HBRrO4YmPqbi1)You will need to add this to the Player Settings!
{% endhint %}


# Create a Network Connection using DOTS NetCode

Workflows and code to create a server/client socket connection using NetCode

## What you'll develop on this page

![Make a Client/server socket connection to create a Network Connection Entity configured from launch objects](/files/-MRpZljVjeE9Gx0qzQn6)

In our project, we make a configurable client/server socket connection using Unity NetCode.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Creating-a-Socket-Connection>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Creating a socket connection

#### First, some background:

If you think back to the "Spawning and Moving Player Prefabs" page in the ECS section of this gitbook, our project has been using the default World creation provided by Unity ECS.

> A [World](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.World.html) organizes entities into isolated groups. A world owns both an [EntityManager](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityManager.html) and a set of [Systems](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_systems.html). Entities created in one world only have meaning in that world, but can be transfered to other worlds (with [EntityManager.MoveEntitiesFrom](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.EntityManager.MoveEntitiesFrom.html)). Systems can only access entities in the same world. You can create as many worlds as you like.
>
> By default Unity creates a default [World](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.World.html) when your application starts up (or you enter **Play Mode**). Unity instantiates all systems (classes that extend [ComponentSystemBase](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.ComponentSystemBase.html)) and adds them to this default world. Unity also creates specialized worlds in the Editor. For example, it creates an Editor world for entities and systems that run only in the Editor, not in playmode and also creates conversion worlds for managing the conversion of GameObjects to entities. See [WorldFlags](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.WorldFlags.html) for examples of different types of worlds that can be created.
>
> Use [World.DefaultGameObjectInjectionWorld](https://docs.unity3d.com/Packages/com.unity.entities@0.50/api/Unity.Entities.World.DefaultGameObjectInjectionWorld.html#Unity_Entities_World_DefaultGameObjectInjectionWorld) to access the default world.<br>
>
> From [ECS Worlds documentation](https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/world.html)

If we take a look at our DOTS Windows we see that we are only running a single world called "Default World".

![View of the DOTS Windows when our project is running](/files/OLPzu7bhMfSgocrUz4xt)

Now that we are using NetCode in our Project, we will start working in "Server World" and "Client World."

> NetCode has a separation of client and server logic, and both the client and server logic are in separate Worlds (the client World, and the server World), based on the [hierarchical update system](https://docs.unity3d.com/Packages/com.unity.entities@latest/index.html?subfolder=/manual/system_update_order.html) of Unity’s Entity Component System (ECS).
>
> By default, NetCode places systems in both client and server Worlds, but not in the default World.
>
> **NOTE**
>
> Systems that update in the `PresentationSystemGroup` are only added to the client World.
>
> To override this default behavior, use the [UpdateInWorld](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.UpdateInWorld.html) attribute, or the `UpdateInGroup` attribute with an explicit client or server system group. The available explicit client server groups are as follows:
>
> * [ClientInitializationSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientInitializationSystemGroup.html)
> * [ServerInitializationSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ServerInitializationSystemGroup.html)
> * [ClientAndServerInitializationSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientAndServerInitializationSystemGroup.html)
> * [ClientSimulationSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientSimulationSystemGroup.html)
> * [ServerSimulationSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ServerSimulationSystemGroup.html)
> * [ClientAndServerSimulationSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientAndServerSimulationSystemGroup.html)
> * [ClientPresentationSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientPresentationSystemGroup.html)
>
> **NOTE**
>
> There is no server presentation system group.
>
> ...

> The default bootstrap creates client server Worlds automatically at startup. It populates them with the systems defined in the attributes you have set. This is useful when you are working in the Editor, but in a standalone game, you might want to delay the World creation so you can use the same executable as both a client and server.
>
> To do this, you can create a class that extends `ClientServerBootstrap` to override the default bootstrap. Implement `Initialize` and create the default World. To create the client and server worlds manually, call `ClientServerBootstrap.CreateClientWorld(defaultWorld, "WorldName");` or `ClientServerBootstrap.CreateServerWorld(defaultWorld, "WorldName");`.
>
> From [NetCode Client server Worlds documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/client-server-worlds.html)

When we add the NetCode package to our project its default is to create a Client and Server world, which we can override by using ClientServerBootstrap.

At the end of this code-along, user inputs will be ingested in "Client World" and stored as "Commands," which are then sent and played back in the "Server World" as well as played back in the "Client World" to do prediction.

The server is the authoritative source of the game. So if the client "predicts" a response to an input and the server disagrees, the server wins.

![Client can predict some movements, but the Server has final say](/files/-MRooplSU0mJLOSNvB0N)

It is important to note that because Client and Server worlds get automatically created with NetCode, our project will no longer function as expected. All our systems and components will "automatically" put our ECS into client and server worlds.

So when our server creates asteroids and sends them to the client, the asteroids will appear "frozen" to the client. This is because although a server-spawned entity will reach the client, we haven't *updated* our Asteroid prefab to also transmit its updated location, so that's why they'll appear frozen.

From the "Overview" page we know that Unity is running an authoritative server client-predicted network architecture.

#### Now, let's implement:

* First, In the Scripts and Prefabs folder create three new folders:
  * Client (where we will store files only the client needs)
  * Mixed (where we will store files both the client and server need)
  * Server (where we will store files only the server needs)
  * This is just to help with organization. Our Scripts and Prefabs folder is already hard to navigate, and as we add even more it will soon become a mess
    * As we continue to update or add more files, let's move our existing files into these folders
    * This will also help us understand what components and systems are run by the server versus the client

{% hint style="info" %}
Hardcore developers use [assembly definition files](https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html) along with Client/Mixed/Server folder separation so that they can deploy Client, Mixed, or Server only builds.

This is also helpful because when the client-only portion of the code base is adjusted the entire project does not need to be rebuilt, only the client-only code, which helps with development time.

If you're interested in being hardcore, check out [Unity's Asteroids sample](https://github.com/Unity-Technologies/multiplayer/tree/master/sampleproject/Assets/Samples/Asteroids) to see how they use assembly definition files to separate out code and logic. Their approach is a bit overkill for this gitbook, so to keep it simple our approach here is just to separate out the files to just give an idea of what files are expected to be run by the client/server.&#x20;
{% endhint %}

![Adding Client, Mixed and Server folders to Scripts and Prefabs](/files/-MQEDPCho5HDxU6a3G6G)

* Now lets add NetCode to our manifest.json file (found in the Project Folder)

```
"com.unity.netcode": "0.50.1-preview.19",
```

![Adding NetCode package to our project through manifest.json](/files/-MRpDpL2xoxdrf7PrX5e)

* You will [get an error](https://forum.unity.com/threads/cannot-implement-buffer-buffer-interfaces-at-the-same-time.1261100/) that reads:

![Error when adding NetCode after using the DOTS Physics sample to create StatefulEventTrigger](/files/TxySzGKhqyWDqvegQe0U)

* This is because NetCode currently does not like if we implement a generic IBufferElementData (which we did to create StatefulEventTriggers)
* We will edit:
  * StatefulCollisionEvent.cs
    * `public struct StatefulCollisionEvent : IBufferElementData, IStatefulSimulationEvent` to
    * `public struct StatefulCollisionEvent : IStatefulSimulationEvent`
  * and StatefulTriggerEvent.cs
    * `public struct StatefulTriggerEvent : IBufferElementData, IStatefulSimulationEvent` to
    * `public struct StatefulTriggerEvent : IStatefulSimulationEvent`
* Make those changes to the definitions at line 7/8, hit save, and return to Editor
  * The error will be gone
* Let's hit play and take a look at the DOTS Window and checkout the Worlds

![Taking a look at the DOTS Windows after adding NetCode to Project](/files/1SejxS94T3Yz9IAQCrx7)

* We can see that in addition to Default World we now have "ClientWorld0" and "ServerWorld"
  * The reason we have "ClientWorld0" (with a number appended at the end) is because the Unity Editor supports ["PlayMode Tools" for NetCode](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/client-server-worlds.html) and you can "simulate" having multiple clients in playmode
  * Unity appends the client # to the end of the world
    * So if there were 2 clients set in "PlayMode Tools" we would see "ClientWorld0" and "ClientWorld1"
* Click through Default World, ClientWorld0 and ServerWorld and check out the different systems in each of the worlds

![Expand this slide for an overview of the difference between Worlds](/files/xjOvSd9gzVJnD6fCOHXl)

* Take time and review the image above; understand which systems are run in ClientWorld0 and which are run in ServerWorld
  * Seriously, it will save you a lot of heartache if you get comfortable with the difference SystemGroup setups between Server and Client worlds
* We found it best to always imagine that ServerWorld and ClientWorld are run on entirely different machines (which happens in server-only builds where clients connect to a dedicated server)
  * When initially starting out with NetCode it is easy to sometimes forget this and think that a component created in ClientWorld should be available to be acted on in ServerWorld
  * It gets *especially* tricky during development because both the client and server are on the same machine (in the editor) so it "feels" like the data should be available
* Notice that there are some systems that are only available on the client like "GhostInputSystem"
  * This system is where we will place our updated "InputMovementSystem"
  * Only client creates inputs, which is why the ServerWorld does not have this system
* Another system is the PresentationSystemGroup which does not exist on the server
  * It doesn't exist on the server because the server doesn't need to render data in a presentation layer' the server is just the authoritative store of "state"

Alright, let's get into it!

* Create ClientServerConnectionControl in Scripts and Prefabs
  * Keep this file in Scripts and Prefabs because it contains systems for both client and server (we'll move it into a better folder later)
    * We *could*  split this file into two separate files (because there is a server-specific system and a client-specific system in the file) but for the sake of starting out easy in this gitbook, we're keeping it in one file
* Paste the code snippet below into ClientServerConnectionControl.cs:

```
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Networking.Transport;
using Unity.NetCode;
using UnityEngine;
using Unity;
using System;

#if UNITY_EDITOR
using Unity.NetCode.Editor;
#endif


//ServerConnectionControl is run in ServerWorld and starts listening on a port
//The port is provided by the ServerDataComponent
[UpdateInWorld(TargetWorld.Server)]
public partial class ServerConnectionControl : SystemBase
{
    private ushort m_GamePort = 5001;

    private struct InitializeServerComponent : IComponentData
    {   
    }

    protected override void OnCreate()
    {
        // We require the InitializeServerComponent to be created before OnUpdate runs
        RequireSingletonForUpdate<InitializeServerComponent>();

        //We create a component which will get immediatly destroyed so this system runs once
        EntityManager.CreateEntity(typeof(InitializeServerComponent));
        
    }

    protected override void OnUpdate()
    {
        //We destroy the InitializeServerComponent so this system only runs once
        EntityManager.DestroyEntity(GetSingletonEntity<InitializeServerComponent>());

        // This is used to split up the game's "world" into sections ("tiles")
        // The client is in a "tile" and networked objects are in "tiles"
        // the client is streamed data based on tiles that are near them
        //https://docs.unity3d.com/Packages/com.unity.netcode@0.5/manual/ghost-snapshots.html
        //check out "Distance based importance" in the link above
        var grid = EntityManager.CreateEntity();
        EntityManager.AddComponentData(grid, new GhostDistanceImportance
        {
            ScaleImportanceByDistance = GhostDistanceImportance.DefaultScaleFunctionPointer,
            TileSize = new int3(80, 80, 80),
            TileCenter = new int3(0, 0, 0),
            TileBorderWidth = new float3(1f, 1f, 1f)
        });

        //Here is where the server creates a port and listens
        NetworkEndPoint ep = NetworkEndPoint.AnyIpv4;
        ep.Port = m_GamePort;
        World.GetExistingSystem<NetworkStreamReceiveSystem>().Listen(ep);
        Debug.Log("Server is listening on port: " + m_GamePort.ToString());
    }
}

//ServerConnectionControl is run in ServerWorld and starts listening on a port
//The port is provided by the ServerDataComponent
[UpdateInWorld(TargetWorld.Client)]
public partial class ClientConnectionControl : SystemBase
{
    public string m_ConnectToServerIp = "127.0.0.1";
    public ushort m_GamePort = 5001;

    private struct InitializeClientComponent : IComponentData
    {   
    }

    protected override void OnCreate()
    {
        // We require the component to be created before OnUpdate runs
        RequireSingletonForUpdate<InitializeClientComponent>();

        //We create a component which will get immediatly destroyed so this system runs once
        EntityManager.CreateEntity(typeof(InitializeClientComponent));
    }

    protected override void OnUpdate()
    {
        // As soon as this runs, the component is destroyed so it doesn't happen twice
        EntityManager.DestroyEntity(GetSingletonEntity<InitializeClientComponent>());

        NetworkEndPoint ep = NetworkEndPoint.Parse(m_ConnectToServerIp, m_GamePort);
        World.GetExistingSystem<NetworkStreamReceiveSystem>().Connect(ep);
        Debug.Log("Client connecting to ip: " + m_ConnectToServerIp + " and port: " + m_GamePort.ToString());
    }
}
```

* We can see within the file there is ServerConnectionControl and ClientConnectionControl
  * ServerConnectionControl only runs in the Server world (check it out in the DOTS Windows) because of the decoration at the top of the system
  * ClientConnectionControl only runs in the Client world (check it out in the DOTS Windows) because of the decoration at the top of the system
* We can see that in ServerConnectionControl we create an Entity and add a "GhostImportanceDistance" component

> #### Distance based importance <a href="#distance-based-importance" id="distance-based-importance"></a>
>
> You can use a custom function to scale the importance per chunk. For example, if a singleton entity with the `GhostDistanceImportance` component on it exists on the server, the netcode makes sure that all the ghosts in the World are split into groups based on the tile size in that singleton.
>
> You must add a `GhostConnectionPosition` component to each connection to determine which tile the connection should prioritize. This `GhostSendSystem` passes this information to the `ScaleImportanceByDistance` in `GhostDistanceImportance` which then uses it to scale the importance of a chunk based on its distance in tiles or any other metric you define in your code.
>
> From [NetCode "Distance based importance" documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/ghost-snapshots.html#ghost-component-variants-types-and-serialization)

* GhostImportanceDistance is a powerful functionality available in NetCode
  * This allows clients to selectively receive Snapshot data based on their proximity to different ghosts
  * What does this mean in context of a game? In a large-scale map, does a player really need to get the Snapshot data of a grenade thrown on the other side of the map *just* as fast as the Snapshot data of a grenade thrown right in front of them?&#x20;
    * Probably not.
  * In Moetsi's case, we build city-scale live Reality Models
    * So sending each client all the data of all network objects in the model is not feasible and also unnecessary
    * GhostDistanceImportance allows for the most relevant Snapshot data be sent automatically, which is great stuff!
* Both in ServerConnectionControl and ClientConnectionControl we use NetworkStreamReceiveSystem to Listen/Connect
  * The server operating in ServerWorld "listens" on the defined port
  * The client operating in ClientWorld "connects" to the defined IP address and port

![Creating ClientServerConnectionControl](/files/-MRpGES83mJLvlNY4rCc)

* Let's hit play and see what happens

![Hitting play after creating ClientServerConnectionControl](/files/-MRpGK3xeQl155r4CxqI)

* We can see that our systems ran and logged their output
* But, what is up with the frozen asteroids?!
  * So when we create these asteroids, both the server and client instantiate (because both client and server are running AsteroidSpawnSystem (which you can see yourself by checking the Systems for both worlds in the DOTS Windows)
  * The server is "authoritative," so it decides where the asteroids go, but *currently* in our Project, we are not yet sending game Snapshot data to the client
    * So the client creates Asteroids, but because we haven't done "NetCode magic" to make the client run the physics on these Asteroids their positions do not get updated, they require updates from the Server called "Snapshots"
  * In order to send Snapshot data, the client must go "in game" by adding a special NetCode component, "NetworkStreamInGame"
  * Then the client will receive updates
  * We will do this in the next section "Loading a Game"
* Hit play then checkout the DOTS Windows, select ServerWorld, and select the NetworkConnection (1) entity to see it in the Inspector

![Finding the NetworkConnectionEntity (NCE) in ServerWorld](/files/nMXb1haPJ2Q9SHBJaJUf)

* This is the "NetworkConnectionEntity" (NCE)
  * this is **not** a Unity term, but it is the term we will be using in this gitbook to describe the entity created after making a client/server connection
  * &#x20;When the server makes a connection with a client it creates a NetworkConnectionEntity for each client it connects with
    * So a server will have as many NCEs as it has connected clients
* Let's now navigate to "ClientWorld0" and find the NCE
* We can see the client also has a NCE

![Finding the NetworkConnectionEntity (NCE) in ClientWorld0](/files/IfwrhVho44tpVDVsPctC)

* Navigate to the "Multiplayer" menu at the top and select "PlayMode Tools"

> *PlayMode Tools*

| **Property**                                  | **Description**                                                                                                                                                                                                                                                                                  |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **PlayMode Type**                             | Choose to make Play Mode either **Client** only, **Server** only, or **Client & Server**.                                                                                                                                                                                                        |
| **Num Thin Clients**                          | Set the number of thin clients. Thin clients cannot be presented, and never spawn any entities it receives from the server. However, they can generate fake input to send to the server to simulate a realistic load.                                                                            |
| **Client send/recv delay**                    | Use this property to emulate high ping. Specify a time (in ms) to delay each outgoing and incoming network packet by.                                                                                                                                                                            |
| **Client send/recv jitter**                   | Use this property to add a random value to the delay, which makes the delay a value between the delay you have set plus or minus the jitter value. For example, if you set **Client send/recv delay** to 45 and **Client send/recv jitter** to 5, you will get a random value between 40 and 50. |
| **Client package drop**                       | Use this property to simulate bad connections where not all packets arrive. Specify a value (as a percentage) and NetCode discards that percentage of packets from the total it receives. For example, set the value to 5 and NetCode discards 5% of all incoming and outgoing packets.          |
| **Client auto connect address (Client only)** | Specify which server a client should connect to. This field only appears if you set **PlayMode Type** to **Client**. The user code needs to read this value and connect because the connection flows are in user code.                                                                           |

> When you enter Play Mode, from this window you can also disconnect clients and choose which client Unity should present if there are multiple. When you change a client that Unity is presenting, it stops calling the update on the `ClientPresentationSystemGroup` for the Worlds which it should no longer present. As such, your code needs to be able to handle this situation, or your presentation code won’t run and all rendering objects you’ve created still exist.
>
> From [NetCode Client server Worlds documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/client-server-worlds.html)

* We are going to change the "Num Thin Clients" to 3
* Hit play and navigate to the Entity Debugger
* Notice that there are now 4 client worlds being handled by the Editor (one for each client)
  * ClientWorld0
  * ClientWorld1
  * ClientWorld2
  * ClientWorld3
  * It is important to note that in an actual deployed project, the server would not have a client world for every client connected
    * This behavior is just in the Editor when using PlayMode Tools (to help with creating games)
* Navigate to ServerWorld in the DOTS Hierarchy and checkout the 4 NCEs
  * A NCE for every connection with a client

![ServerWorld's 4 NCEs visible in DOTS Hierarchy (one for each connection to a client)](/files/7IGNtd9FNCVgWmQ1FiOQ)

* Click through the NCEs and see how the NetworkIdComponent starts at 1 and increases by 1 for every client connection
* Now navigate to ClientWorld0 and checkout the NCE

![ClientWorld0 still having only 1 NCE after we added 3 thin clients from PlayMode Tools](/files/3Rusvntt40XgJCOUlsKu)

* Notice there is only 1 NCE in ClientWorld0 because there is only 1 NCE on the client
  * Clients only have a connection to the server; they do not have connections to other clients
  * Servers have connections to each and every client
* Because we will be working with NCEs very heavily it is worth taking a harder look at what components and values the inspector shows for an NCE immediately upon connection

![Overview of the server NCE](/files/P4UuEEqnvXoWNXvyvkSO)

* Change the "Num Thin Clients" back to 0
  * No gif here, we believe in you 💪

{% hint style="success" %}
We now have a client/server socket connection which creates an NCE on both the client and server

* We added the NetCode package to manifest.json
* We created ClientServerConnectionControl which results in a NCE on both the client and server
  {% endhint %}

## Creating a configurable socket connection

#### First, some background:

* Now we are going to make things more complicated...
  * For good reason!
    * We need to prepare for the upcoming "Multiplayer section" where we will have the ability to select whether we join a game as client-only or we host a game and are a client-server
* Right now the server IP address and port are hardwired into our systems
  * They are defined in ServerConnectionControl and ClientConnectionControl
* We will now update this so that the IP address and port are provided by GameObjects
* We will be adding four GameObjects to SampleScene
  * ClientServerInfo (with ClientServerInfo script)
  * ClientServerConnectionHandler (with ClientServerConnectionHandler script)
  * ClientLaunchObject (with ClientLaunchObjectData script)
  * ServerLaunchObject (with ServerLaunchObjectData script)
* ClientServerInfo
  * This is where we will set what port our game should run on
  * We will update it with the IP address provided by the ClientLaunchObject
  * Think of it as a store of client and server info
* ClientServerConnectionHandler
  * Its script will look for GameObjects with "LaunchObject" tags and take data from those GameObjects to create entities, which will trigger our ClientServerConnectionControl
* ClientLaunchObjectData
  * This will have a "LaunchObject" tag and store the IP address the client is connecting to
* ServerGameObject
  * This will have a "LaunchObject" tag
* We will update our ClientServerConnectionControl to ingest the component data created by ClientServerConnectionHandler
* We will also create these 4 new components:
  * ClientDataComponent (will provide the IP address and port to ClientConnectionControl)
  * InitializeClientComponent (will trigger ClientConnectionControl to run)
  * ServerDataComponent (will provide the port to ServerConnectionControl)
  * InitializeServerComponent (will trigger ServerConnectionControl to run)

![Overview of our updated socket connection flow](/files/-MRXl2h6k3PGYDZuYRIx)

* Note that the "source of truth" of what IP address our client will connect to is on the "ClientLaunchObject"
  * This is how we be able to configure what server to connect to from our "Navigation" scene, later on in the Multiplayer section of this gitbook
* Note that the "source of truth" of what port our server listens on and client connects to is in "ClientServerInfo"
  * This was an opinionated choice by Moetsi specifically for this gitbook
  * We *could* provide the port in the ServerLaunchObject and ClientLaunchObject as well
  * But we decided that the port number will be set "before" runtime (aka baked into your build)
    * If you want your ports to be dynamic, go ahead and do you!

#### Now, let's implement:

* Right-click on the Hierarchy in SampleScene and create an empty GameObject named ClientServerInfo
* Create a new script named ClientServerInfo
* Paste the code snippet below into ClientServerInfo.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using System;
 
public class ClientServerInfo : MonoBehaviour
{
    public bool IsServer = false;
    public bool IsClient = false;
    public string ConnectToServerIp;
    public ushort GamePort = 5001;
}
```

![Creating the ClientServerInfo GameObject and script](/files/-MQJP8wr1Rt6LQ_XT2lj)

* Click "Add Component" in Inspector on ClientServerInfo and add the ClientServerInfo script
* Navigate to SampleScene, right click in the Hierarchy and create an empty GameObject and name it ClientLaunchObject
  * Add a tag called "LaunchObject"
    * You do this by selecting the drop down menu next to "Tag" in the Inspector when ClientLaunchObject is highlighted in Hierarchy and choosing the last option "Add Tag...". Hit the + button and type in the name of the tag ("LaunchObject" for this one)
* Create a new script called ClientLaunchObjectData
* Paste the code snippet below into ClientLaunchObjectData.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;

public class ClientLaunchObjectData : MonoBehaviour
{
    public string IPAddress = "127.0.0.1";
}

```

![Create ClientLaunchObject GameObject and ClientLaunchObjectData script](/files/-MQJPIRHv0cw6SVrvbVq)

* Click "Add Component" in Inspector on ClientLaunchObject and add ClientLaunchObjectData script
* Navigate to SampleScene, right click in the Hierarchy and create an empty GameObject and name it ServerLaunchObject
  * add the "LaunchObject" tag (the tag you just made)
* Create a new script called ServerLaunchObjectData
* Paste the code snippet below into ServerLaunchObjectData.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;

public class ServerLaunchObjectData : MonoBehaviour
{
}
```

![](/files/-MQJRVnKhVXPdU9vb6V7)

* Click "Add Component" in Inspector on ServerLaunchObject and add the ServerLaunchObjectData script
* Right click in the Hierarchy in SampleScene and create an empty GameObject called ClientServerConnectionHandler
* Create a new script called ClientServerConnectionHandler
* Paste the code snippet below into ClientServerConnectionHandler.cs:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;

public class ClientServerConnectionHandler : MonoBehaviour
{
    //this is the store of server/client info
    public ClientServerInfo ClientServerInfo;

    // these are the launch objects from Navigation scene that tells what to set up
    private GameObject[] launchObjects;

    void Awake()
    {
        launchObjects = GameObject.FindGameObjectsWithTag("LaunchObject");
        foreach(GameObject launchObject in launchObjects)
        {
            //  
            // checks for server launch object
            // does set up for the server for listening to connections and player scores
            //
            if(launchObject.GetComponent<ServerLaunchObjectData>() != null)
            {
                //sets the gameobject server data (mono)
                ClientServerInfo.IsServer = true;
                
                //sets the component server data in server world(dots)
                //ClientServerConnectionControl (server) will run in server world
                //it will pick up this component and use it to listen on the port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ServerSimulationSystemGroup
                    //we move forward (because that is the server world)
                    if (world.GetExistingSystem<ServerSimulationSystemGroup>() != null)
                    {
                        var ServerDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ServerDataEntity, new ServerDataComponent
                        {
                            GamePort = ClientServerInfo.GamePort
                        });
                        //create component that allows server initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeServerComponent));
                    }
                }
            }

            // 
            // checks for client launch object
            //  does set up for client for dots and mono
            // 
            if(launchObject.GetComponent<ClientLaunchObjectData>() != null)
            {
                //sets the gameobject data in ClientServerInfo (mono)
                //sets the gameobject data in ClientServerInfo (mono)
                ClientServerInfo.IsClient = true;
                ClientServerInfo.ConnectToServerIp = launchObject.GetComponent<ClientLaunchObjectData>().IPAddress;                

                //sets the component client data in server world(dots)
                //ClientServerConnectionControl (client) will run in client world
                //it will pick up this component and use it connect to IP and port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ClientSimulationSystemGroup
                    //we move forward (because that is the client world)
                    if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                    {
                        var ClientDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ClientDataEntity, new ClientDataComponent
                        {
                            ConnectToServerIp = ClientServerInfo.ConnectToServerIp,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //create component that allows client initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeClientComponent));
                    }
                }
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
```

![](/files/-MQJRMQPXVElqXWshD35)

* You will get a 4 errors because ClientServerConnectionHandler is referencing components we haven't created yet, so let's make our 4 additional components
* Create ClientDataComponent and paste in this code snippet:

```
using System;
using Unity.Entities;
using Unity.Collections;

public struct ClientDataComponent : IComponentData
{
    //Must used "FixedStringNBytes" instead of string in IComponentData
    //This is a DOTS requirement because IComponentData must be a struct
    public FixedString64Bytes ConnectToServerIp;
    public ushort GamePort;
}
```

* InitializeClientComponent

```
using Unity.Entities;

public struct InitializeClientComponent : IComponentData
{   
}
```

* ServerDataComponent

```
using Unity.Entities;
using Unity.Collections;


 public struct ServerDataComponent : IComponentData
{
    public ushort GamePort;
}
```

* InitializeServerComponent

```
using Unity.Entities;

public struct InitializeServerComponent : IComponentData
{
    
}
```

![Creating ClientDataComponent, InitializeClientComponent, ServerDataComponent, InitializeServerComponent](/files/-MQJWrEPbR0P9bbujbsw)

* Now we must update ClientServerConnectionControl to use these new components
* This file already exists so just paste over current code with the code snippet below into ClientServerConnectionControl:

```
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Networking.Transport;
using Unity.NetCode;
using UnityEngine;
using Unity;
using System;

#if UNITY_EDITOR
using Unity.NetCode.Editor;
#endif


//ServerConnectionControl is run in ServerWorld and starts listening on a port
//The port is provided by the ServerDataComponent
[UpdateInWorld(TargetWorld.Server)]
public partial class ServerConnectionControl : SystemBase
{
    private ushort m_GamePort;

    protected override void OnCreate()
    {
        // We require the InitializeServerComponent to be created before OnUpdate runs
        RequireSingletonForUpdate<InitializeServerComponent>();
        
    }

    protected override void OnUpdate()
    {
        //load up data to be used OnUpdate
        var serverDataEntity = GetSingletonEntity<ServerDataComponent>();
        var serverData = EntityManager.GetComponentData<ServerDataComponent>(serverDataEntity);
        m_GamePort = serverData.GamePort;

        //We destroy the InitializeServerComponent so this system only runs once
        EntityManager.DestroyEntity(GetSingletonEntity<InitializeServerComponent>());

        // This is used to split up the game's "world" into sections ("tiles")
        // The client is in a "tile" and networked objects are in "tiles"
        // the client is streamed data based on tiles that are near them
        //https://docs.unity3d.com/Packages/com.unity.netcode@0.5/manual/ghost-snapshots.html
        //check out "Distance based importance" in the link above
        var grid = EntityManager.CreateEntity();
        EntityManager.AddComponentData(grid, new GhostDistanceImportance
        {
            ScaleImportanceByDistance = GhostDistanceImportance.DefaultScaleFunctionPointer,
            TileSize = new int3(80, 80, 80),
            TileCenter = new int3(0, 0, 0),
            TileBorderWidth = new float3(1f, 1f, 1f)
        });

        //Here is where the server creates a port and listens
        NetworkEndPoint ep = NetworkEndPoint.AnyIpv4;
        ep.Port = m_GamePort;
        World.GetExistingSystem<NetworkStreamReceiveSystem>().Listen(ep);
        Debug.Log("Server is listening on port: " + m_GamePort.ToString());
    }
}

//ClientConnectionControl is run in ClientWorld and connects to an IP address and port
//The IP address and port is provided by the ClientDataComponent
[UpdateInWorld(TargetWorld.Client)]
public partial class ClientConnectionControl : SystemBase
{
    private string m_ConnectToServerIp;
    private ushort m_GamePort;

    protected override void OnCreate()
    {
        // We require the component to be created before OnUpdate runs
        RequireSingletonForUpdate<InitializeClientComponent>();

    }

    protected override void OnUpdate()
    {
        //load up data to be used OnUpdate
        var clientDataEntity = GetSingletonEntity<ClientDataComponent>();
        var clientData = EntityManager.GetComponentData<ClientDataComponent>(clientDataEntity);
        
        m_ConnectToServerIp = clientData.ConnectToServerIp.ToString();
        m_GamePort = clientData.GamePort;

        // As soon as this runs, the component is destroyed so it doesn't happen twice
        EntityManager.DestroyEntity(GetSingletonEntity<InitializeClientComponent>());

        NetworkEndPoint ep = NetworkEndPoint.Parse(m_ConnectToServerIp, m_GamePort);
        World.GetExistingSystem<NetworkStreamReceiveSystem>().Connect(ep);
        Debug.Log("Client connecting to ip: " + m_ConnectToServerIp + " and port: " + m_GamePort.ToString());
    }
}
```

![Updating ClientServerConnectionControl](/files/-MQJXyUULDOxfnqp5YHi)

* Finally let's add ClientServerInfo as the reference in ClientServerConnectionHandler (drag the ClientServerInfo GameObject into the Client Server Info field in the Client Server Connection Handler in Inspector)
* Hit play

![](/files/-MRpZ_M7xbUWwT9aPPP2)

* Our client and server make a connection and update ClientServerInfo
* Finally, let's do some housekeeping and create a new folder called "Multiplayer Setup" in Scripts and Prefabs
* Drag the following files into "Multiplayer Setup" folder:
  * ClientServerInfo
  * ClientLaunchObjectData
  * ServerLaunchObjectData
  * ClientServerConnectionHandler
  * ClientDataComponent
  * InitializeClientComponent
  * ServerDataComponent
  * InitializeServerComponent
  * ClientServerConnectionControl
* No gif here, we believe in you 💪

{% hint style="success" %}
We can now trigger a client server connection through GameObjects with LaunchObject tags

* We created 4 new GameObjects in the scene
  * ClientServerInfo
  * ClientLaunchObject
  * ServerLaunchObject
  * ClientServerConnectionHandler
* We created 4 new scripts
  * ClientServerInfo
  * ClientLaunchObjectData
  * ServerLaunchObjectData
  * ClientServerConnectionHandler
* We created 4 new components
  * ClientDataComponent
  * InitializeClientComponent
  * ServerDataComponent
  * InitializeServerComponent
    {% endhint %}

Github branch link:&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Creating-a-Socket-Connection'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Load a Game using DOTS NetCode

Code and workflow to send server game data to client and have the client confirm loading the data

## What you'll develop on this page

![Server sending updates and NCEs updated as expected](/files/-MRphm26QznBP9p--Lig)

We will send server game data to the client; the client will load the data and send back another RPC.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Loading-a-Game>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Loading a game

#### First, some background:

When a socket connection is made in NetCode the server and client both have the ability to send each other RPCs.

> NetCode uses a limited form of RPCs to handle events. A job on the sending side can issue RPCs, and they then execute on a job on the receiving side. This limits what you can do in an RPC; such as what data you can read and modify, and what calls you are allowed to make from the engine. For more information on the Job System see the Unity User Manual documentation on the [C# Job System](https://docs.unity3d.com/2019.3/Documentation/Manual/JobSystem.html).
>
> To make the system a bit more flexible, you can use the flow of creating an entity that contains specific netcode components such as `SendRpcCommandRequestComponent` and `ReceiveRpcCommandRequestComponent`, which this page outlines.
>
> ...
>
> To send the command you need to create an entity and add the command and the special component [SendRpcCommandRequestComponent](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.SendRpcCommandRequestComponent.html) to it. This component has a member called `TargetConnection` that refers to the remote connection you want to send this command to.
>
> **NOTE**
>
> If `TargetConnection` is set to `Entity.Null` you will broadcast the message. On a client you don't have to set this value because you will only send to the server.
>
> ...
>
> The RpcSystem automatically finds all of the requests, sends them, and then deletes the send request. On the remote side they show up as entities with the same `IRpcCommand` and a `ReceiveRpcCommandRequestComponent` which you can use to identify which connection the request was received from.
>
> ...
>
> The code generation for RPCs is optional, if you do not wish to use it you need to create a component and a serializer. These can be the same struct or two different ones. To create a single struct which is both the component and the serializer you would need to add:
>
> ```
> [BurstCompile]
> public struct OurRpcCommand : IComponentData, IRpcCommandSerializer<OurRpcCommand>
> {
>     public void Serialize(ref DataStreamWriter writer, in OurRpcCommand data)
>     {
>     }
>
>     public void Deserialize(ref DataStreamReader reader, ref OurRpcCommand data)
>     {
>     }
>
>     public PortableFunctionPointer<RpcExecutor.ExecuteDelegate> CompileExecute()
>     {
>     }
>
>     [BurstCompile(DisableDirectCall = true)]
>     private static void InvokeExecute(ref RpcExecutor.Parameters parameters)
>     {
>     }
>
>     static PortableFunctionPointer<RpcExecutor.ExecuteDelegate> InvokeExecuteFunctionPointer = new PortableFunctionPointer<RpcExecutor.ExecuteDelegate>(InvokeExecute);
> }
> ```
>
> The [IRpcCommandSerializer](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.IRpcCommandSerializer.html) interface has three methods: **Serialize, Deserialize**, and **CompileExecute**. **Serialize** and **Deserialize** store the data in a packet, while **CompileExecute** uses Burst to create a `FunctionPointer`. The function it compiles takes a [RpcExecutor.Parameters](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.RpcExecutor.Parameters.html) by ref that contains:
>
> * `DataStreamReader` reader
> * `Entity` connection
> * `EntityCommandBuffer.Concurrent` commandBuffer
> * `int` jobIndex
>
> Because the function is static, it needs to use `Deserialize` to read the struct data before it executes the RPC. The RPC then either uses the command buffer to modify the connection entity, or uses it to create a new request entity for more complex tasks. It then applies the command in a separate system at a later time. This means that you don’t need to perform any additional operations to receive an RPC; its `Execute` method is called on the receiving end automatically.
>
> From [NetCode RPCs documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/rpcs.html)

Not sure how you feel about it, but that RPCs documentation is TOUGH 🤯

Basically here's the gist: once you have a client/server connection you can send RPCs back and forth.

If you want to send RPCs with "just" data, NetCode has solid code generation that makes it pretty easy to do. We will do that in this project when the server sends the client the game settings.

If you want your RPCs to also execute code when they get to their intended receiver, then boilerplate code from the (confusing) explanation is involved. You'll see how this works at the point in our project when the client sends back "I have loaded the game" confirmation to the server and the client-sent RPC automatically updates the the server's NCE.

There are 2 more data streams that can be sent between NCEs: (1) Snapshots and (2) Commands. Snapshots send state data and Commands send inputs.

Currently (at this point in the project), by creating a NCE, we have "unlocked" RPCs. Once we put a special NetCode component, "NetworkStreamInGame",  on the NCE it signals to NetCode to begin sending game data. Then Snapshots and Commands will be unlocked.

Part of the flow of loading the level will be to send the GameSettings data to the client, the second part is to add the NetworkStreamInGame component to the NCE on both the client and server. Because the GameSettings data is set from the fields on the GameSettings GameObject in the Sub Scene, which is accessible to both the client and server, it is actually unnecessary to send GameSettings from the server to the client (the server would be sending the client data that the client already knows).

However, in our project we implement this data transfer even though it is unnecessary (currently) to show an example of how to send data. In the Multiplayer section we will be sending data that the client actually does **not** know.

#### Now let's implement

![Overview of 'Loading a game' flow we implement in this section](/files/-MQcOPrcAm2fPBnuPwG2)

* We are going to implement a flow where the server sends an RPC to a newly connected client and the client responds by sending back an RPC
* Let's start by creating a folder in "Mixed" called "Commands" where we will store our RPCs
  * The RPCs need to be in the Mixed folder because both the server and client utilize these RPCs
* Make another folder in Mixed called "Components"
  * In this folder we will put the components that both the server and client touch
* Put the GameSettingsComponent in Mixed/Components
  * Both the server and client use the GameSettingsComponent in this flow (which is why it is in Mixed)

![Creating Mixed/Commands and Mixed/Components and moving in GameSettingsComponent](/files/-MQOb-HjQnXOcviRgJDM)

* Now create PlayerSpawningStateComponent and put it in **Server**/Components&#x20;
  * So make a Components folder in Server folder :)
  * PlayerSpawningStateComponent will be used by the server to know when a player is spawning
* Paste the code snippet below into PlayerSpawningStateComponentData.cs:

```
using Unity.Entities;
using Unity.NetCode;

public struct PlayerSpawningStateComponent : IComponentData
{
    public int IsSpawning;
}
```

![Creating PlayerSpawningStateComponent (gif out of date, put it in Server/Components not Mixed/Components)](/files/-MQOe7G8ft0HRFtxeX1H)

* Now in Mixed/Commands create two 2 RPCs
* First one is SendClientGameRpc
* Paste the code snippet below in SendClientGameRpc.cs:

```
using AOT;
using Unity.Burst;
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Entities;
using Unity.Collections;
using System.Collections;
using System;

public struct SendClientGameRpc : IRpcCommand
{
    public int levelWidth;
    public int levelHeight;
    public int levelDepth;
    public float playerForce;
    public float bulletVelocity;
}
```

* Second one is SendServerGameLoadedRpc
* Paste the code snippet below in SendServerGameLoadedRpc.cs:

```
using AOT;
using Unity.Burst;
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Entities;
using UnityEngine;
using Unity.Collections;

[BurstCompile]
public struct SendServerGameLoadedRpc : IComponentData, IRpcCommandSerializer<SendServerGameLoadedRpc>
{
    //Necessary boilerplate
    public void Serialize(ref DataStreamWriter writer, in RpcSerializerState state, in SendServerGameLoadedRpc data)
    {
    }
    //Necessary boilerplate
    public void Deserialize(ref DataStreamReader reader, in RpcDeserializerState state, ref SendServerGameLoadedRpc data)
    {
    }

    [BurstCompile]
    [MonoPInvokeCallback(typeof(RpcExecutor.ExecuteDelegate))]
    private static void InvokeExecute(ref RpcExecutor.Parameters parameters)
    {
        //Within here is where
        var rpcData = default(SendServerGameLoadedRpc);

        //Here we deserialize the received data
        rpcData.Deserialize(ref parameters.Reader, parameters.DeserializerState, ref rpcData);

        //Here we add 3 components to the NCE
        //The first, PlayerSpawningStateComonent will be used during our player spawn flow
        parameters.CommandBuffer.AddComponent(parameters.JobIndex, parameters.Connection, new PlayerSpawningStateComponent());
        //NetworkStreamInGame must be added to an NCE to start receiving Snapshots
        parameters.CommandBuffer.AddComponent(parameters.JobIndex, parameters.Connection, default(NetworkStreamInGame));
        //GhostConnectionPosition is added to be used in conjunction with GhostDistanceImportance (from the socket section)
        parameters.CommandBuffer.AddComponent(parameters.JobIndex, parameters.Connection, default(GhostConnectionPosition));

        //We add a log that we will remove later to show that this RPC has been executed
        //iOS will crash if Debug.Log is used within an RPC so we will remove this in the ARFoundation section
        Debug.Log("Server acted on confirmed game load");
    }

    //Necessary boilerplate
    static PortableFunctionPointer<RpcExecutor.ExecuteDelegate> InvokeExecuteFunctionPointer =
        new PortableFunctionPointer<RpcExecutor.ExecuteDelegate>(InvokeExecute);
    public PortableFunctionPointer<RpcExecutor.ExecuteDelegate> CompileExecute()
    {
        return InvokeExecuteFunctionPointer;
    }
}

//Necessary boilerplate
class SendServerGameLoadedRpcCommandRequestSystem : RpcCommandRequestSystem<SendServerGameLoadedRpc, SendServerGameLoadedRpc>
{
    [BurstCompile]
    protected struct SendRpc : IJobEntityBatch
    {
        public SendRpcData data;
        public void Execute(ArchetypeChunk chunk, int orderIndex)
        {
            data.Execute(chunk, orderIndex);
        }
    }
    protected override void OnUpdate()
    {
        var sendJob = new SendRpc{data = InitJobData()};
        ScheduleJobData(sendJob);
    }
}
```

* You'll notice that SendServerGameLoadedRpc is more complicated than SendClientGameRpc
  * SendClientGameRpc is used to send data from the server to the client. There's no other funny business going on, just an RPC being used as a way to send data
  * SendServerGameLoadedRpc does not send any data. Instead, when it reaches the server the InvokeExecute method is called, which updates the receiving NCE
    * The RPC is able to update the NCE itself, without us needing to implement a system to get it done

![Creating two RPCs in Mixed/Commands](/files/-MQOinya5vyTTJxtkpbu)

* Create a folder in Server named "Systems" and within that folder create ServerSendGameSystem
* Paste this code snippet below into ServerSendGameSystem.cs:

```
using Unity.Entities;
using Unity.Jobs;
using Unity.Collections;
using Unity.NetCode;
using UnityEngine;

//This component is only used by this system so we define it in this file
public struct SentClientGameRpcTag : IComponentData
{
}

//This system should only be run by the server (because the server sends the game settings)
//By sepcifying to update in group ServerSimulationSystemGroup it also specifies that it must
//be run by the server
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
[UpdateBefore(typeof(RpcSystem))]
public partial class ServerSendGameSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_Barrier;

    protected override void OnCreate()
    {
        m_Barrier = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
        RequireSingletonForUpdate<GameSettingsComponent>();
    }

    protected override void OnUpdate()
    {
        var commandBuffer = m_Barrier.CreateCommandBuffer();

        var serverData = GetSingleton<GameSettingsComponent>();

        Entities
        .WithNone<SentClientGameRpcTag>()
        .ForEach((Entity entity, in NetworkIdComponent netId) =>
        {
            commandBuffer.AddComponent(entity, new SentClientGameRpcTag());
            var req = commandBuffer.CreateEntity();
            commandBuffer.AddComponent(req, new SendClientGameRpc
            {
                levelWidth = serverData.levelWidth,
                levelHeight = serverData.levelHeight,
                levelDepth = serverData.levelDepth,
                playerForce = serverData.playerForce,
                bulletVelocity = serverData.bulletVelocity,
            });

            commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent {TargetConnection = entity});
        }).Schedule();

        m_Barrier.AddJobHandleForProducer(Dependency);
    }
}
```

![Creating ServerSendGameSystem in Server/Systems](/files/-MQOmUXH4Sg9Kcp5g_MN)

* Create a folder in Client named "Systems" and within that folder create ClientLoadGameSystem
* Paste the code snippet below into ClientLoadGameSystem:

```
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;

//This will only run on the client because it updates in ClientSimulationSystemGroup (which the server does not have)
[UpdateInGroup(typeof(ClientSimulationSystemGroup))]
[UpdateBefore(typeof(RpcSystem))]
public partial class ClientLoadGameSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    protected override void OnCreate()
    {
        //We will be using the BeginSimECB
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //Requiring the ReceiveRpcCommandRequestComponent ensures that update is only run when an NCE exists
        RequireForUpdate(GetEntityQuery(ComponentType.ReadOnly<SendClientGameRpc>(), ComponentType.ReadOnly<ReceiveRpcCommandRequestComponent>()));   
        //This is just here to make sure the Sub Scene is streamed in before the client sets up the level data
        RequireSingletonForUpdate<GameSettingsComponent>();
    }

    protected override void OnUpdate()
    {

        //We must declare our local variables before using them within a job (.ForEach)
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        var rpcFromEntity = GetBufferFromEntity<OutgoingRpcDataStreamBufferComponent>();
        var gameSettingsEntity = GetSingletonEntity<GameSettingsComponent>();
        var getGameSettingsComponentData = GetComponentDataFromEntity<GameSettingsComponent>();

        Entities
        .ForEach((Entity entity, in SendClientGameRpc request, in ReceiveRpcCommandRequestComponent requestSource) =>
        {
            //This destroys the incoming RPC so the code is only run once
            commandBuffer.DestroyEntity(entity);

            //Check for disconnects before moving forward
            if (!rpcFromEntity.HasComponent(requestSource.SourceConnection))
                return;

            //Set the game size (unnecessary right now but we are including it to show how it is done)
            getGameSettingsComponentData[gameSettingsEntity] = new GameSettingsComponent
            {
                levelWidth = request.levelWidth,
                levelHeight = request.levelHeight,
                levelDepth = request.levelDepth,
                playerForce = request.playerForce,
                bulletVelocity = request.bulletVelocity
            };

            //These update the NCE with NetworkStreamInGame (required to start receiving snapshots)
            commandBuffer.AddComponent(requestSource.SourceConnection, default(NetworkStreamInGame));
            
            //This tells the server "I loaded the level"
            //First we create an entity called levelReq that will have 2 necessary components
            //Next we add the RPC we want to send (SendServerGameLoadedRpc) and then we add
            //SendRpcCommandRequestComponent with our TargetConnection being the NCE with the server (which will send it to the server)
            var levelReq = commandBuffer.CreateEntity();
            commandBuffer.AddComponent(levelReq, new SendServerGameLoadedRpc());
            commandBuffer.AddComponent(levelReq, new SendRpcCommandRequestComponent {TargetConnection = requestSource.SourceConnection});

            Debug.Log("Client loaded game");
        }).Schedule();

        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}

```

![Creating ClientLoadGameSystem in Client/Systems](/files/-MQOmrz4lTXLZp9KlKiJ)

* Let's hit play and then go take a look the NCE in Server World in the DOTS Hierarchy
* Checkout the "NetworkStreamInGame" component on the NCE
  * "Network Stream In Game" listed under tags

![Snapshots being sent and NCE is updated as expected](/files/AJ3JYGZV3E32C2sNPBuC)

* We can see the component has been added on the Client as well

![Client NCE has "Network Stream In Game" listed under tags](/files/pgwm5ZzJXC4gKXjYo5ji)

* Our asteroids now have movement because we are "in game" (which we signaled by adding the "NetworkStreamInGame" component on the NCE)
  * The server can send Snapshots of the asteroids
* We see our NCEs have been updated by our RPCs and systems

{% hint style="success" %}
We can now load data on the client sent from the server through an RPC

* We created a new component
  * PlayerSpawningStateComponent
* We created 2 RPCs
  * SendClientGameRpc
  * SendServerGameLoadedRpc
* We created 2 systems
  * ServerSendGameSystem
  * ClientLoadGameSystem
    {% endhint %}

Github branch link:&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Loading-a-Game'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# DOTS NetCode and Prefabs

Code and workflows to turn asteroid entity prefabs into NetCode "ghosts"

## What you'll develop on this page

![Asteroid prefab server spawning and destroying](/files/-MQPmPXwlwdg3j4udzuz)

We will update our Asteroid prefab, effectively "turning it into" a NetCode ghost so that spawning and destroying are handled by the server.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Asteroids>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Asteroids spawn and movement

#### First, some background:

NetCode refers to networked entities as "Ghosts" 👻. Ghosts must be declared before runtime, there is no way to currently update our ghost list from inside a system; it must be done through authoring.

Add a "GhostAuthoringComponent" to the prefabs (this is a special NetCode component). You can think of this as "registering" our Asteroid, Player, and Bullet prefab.

> ## Ghost snapshots <a href="#ghost-snapshots" id="ghost-snapshots"></a>
>
> A ghost is a networked object that the server simulates. During every frame, the server sends a snapshot of the current state of all ghosts to the client. The client presents them, but cannot directly control or affect them because the server owns them.
>
> The ghost snapshot system synchronizes entities which exist on the server to all clients. To make it perform properly, the server processes per ECS chunk rather than per entity. On the receiving side the processing is done per entity. This is because it is not possible to process per chunk on both sides, and the server has more connections than clients.
>
> ### Ghost authoring component <a href="#ghost-authoring-component" id="ghost-authoring-component"></a>
>
> The ghost authoring component is based on specifying ghosts as Prefabs with the **GhostAuthoringComponent** on them. The **GhostAuthoringComponent** has a small editor which you can use to configure how NetCode synchronizes the Prefab.
>
> ![Ghost Authoring Component](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/images/ghost-config.png)*Gh*\
> *Ghost Authoring Component*
>
> You must set the **Name**, **Importance**, **Supported Ghost Mode**, **Default Ghost Mode** and **Optimization Mode** property on each ghost. Unity uses the **Importance** property to control which entities are sent when there is not enough bandwidth to send all. A higher value makes it more likely that the ghost will be sent.
>
> You can select from three different **Supported Ghost Mode** types:
>
> * **All** - this ghost supports both being interpolated and predicted.
> * **Interpolated** - this ghost only supports being interpolated, it cannot be spawned as a predicted ghost.
> * **Predicted** - this ghost only supports being predicted, it cannot be spawned as a interpolated ghost.
>
> You can select from three different **Default Ghost Mode** types:
>
> * **Interpolated** - all ghosts Unity receives from the server are treated as interpolated.
> * **Predicted** - all ghosts Unity receives from the server are treated as predicted.
> * **Owner predicted** - the ghost is predicted for the client that owns it, and interpolated for all other clients. When you select this property, you must also add a **GhostOwnerComponent** and set its **NetworkId** field in your code. Unity compares this field to each clients’ network ID to find the correct owner.
>
> You can select from two different **Optimization Mode** types:
>
> * **Dynamic** - the ghost will be optimized for having small snapshot size both when changing and when not changing.
> * **Static** - the ghost will not be optimized for having small snapshot size when changing, but it will not be sent at all when it is not changing.
>
> To override the default client instantiation you can create a classification system updating after **ClientSimulationSystemGroup** and before **GhostSpawnClassificationSystem** which goes through the **GhostSpawnBuffer** buffer on the singleton entity with **GhostSpawnQueueComponent** and change the **SpawnType**.
>
> Unity uses attributes in C# to configure which components and fields are synchronized as part of a ghost. You can see the current configuration in the **GhostAuthoringComponent** by selecting **Update component list**, but you cannot modify it from the inspector.
>
> To change which versions of a Prefab a component is available on you use **PrefabType** in a **GhostComponentAttribute** on the component. **PrefabType** can be on of the these types:
>
> * **InterpolatedClient** - the component is only available on clients where the ghost is interpolated.
> * **PredictedClient** - the component is only available on clients where the ghost is predicted.
> * **Client** - the component is only available on the clients, both when the ghost is predicted and interpolated.
> * **Server** - the component is only available on the server.
> * **AllPredicted** - the component is only available on the server and on clients where the ghost is predicted.
> * **All** - the component is available on the server and all clients.
>
> For example, if you add `[GhostComponent(PrefabType=GhostPrefabType.Client)]` to RenderMesh, the ghost won’t have a RenderMesh when it is instantiated on the server, but it will have it when instantiated on the client.
>
> A component can set **OwnerPredictedSendType** in the **GhostComponentAttribute** to control which clients the component is sent to when it is owner predicted. The available modes are:
>
> * **Interpolated** - the component is only sent to clients which are interpolating the ghost.
> * **Predicted** - the component is only sent to clients which are predicting the ghost.
> * **All** - the component is sent to all clients.
>
> If a component is not sent to a client NetCode will not modify the component on the client which did not receive it.
>
> A component can also set **SendDataForChildEntity** to true or false in order to control if the component it sent when it is part of a child entity of a ghost with multiple entities.
>
> A component can also set **SendToOwner** in the **GhostComponentAttribute** to specify if the component should be sent to client who owns the entity. The available values are:
>
> * **SendToOwner** - the component is only sent to the client who own the ghost
> * **SendToNonOwner** - the component is sent to all clients except the one who owns the ghost
> * **All** - the component is sent to all clients.
>
> For each component you want to serialize, you need to add an attribute to the values you want to send. Add a `[GhostField]` attribute to the fields you want to send in an `IComponentData`. Both component fields and properties are supported. The following conditions apply in general for a component to support serialization:
>
> * The component must be declared as public.
> * Only public members are considered. Adding a `[GhostField]` to a private member has no effect.
> * The **GhostField** can specify `Quantization` for floating point numbers. The floating point number will be multiplied by this number and converted to an integer in order to save bandwidth. Specifying a `Quantization` is mandatory for floating point numbers and not supported for integer numbers. To send a floating point number unquantized you have to explicitly specify `[GhostField(Quantization=0)]`.
> * The **GhostField** `Composite` flag controls how the delta compression computes the change fields bitmask for non primitive fields (struct). When set to `true` the delta compression will generate only 1 bit to indicate if the struct values are changed or not.
> * The **GhostField** `SendData` flag can be used to instruct code-generation to not include the field in the serialization data if is set to false. This is particularly useful for non primitive members (like structs), which will have all fields serialized by default.
> * The **GhostField** also has a `Smoothing` property which controls if the field will be interpolated or not on clients which are not predicting the ghost. Possible values are:
>   * **Clamp** - use the latest snapshot value
>   * **Interpolate** - interpolate the data between the two snapshot values and if no data is available for the next tick, clamp to the latest value.
>   * **InterpolateAndExtrapolate** - interpolate the GhostField value between snapshot values, and if no data is available for the next tick, the next value is linearly extrapolated using the previous two snapshot values. Extrapolation is limited (i.e. clamped) via `ClientTickRate.MaxExtrapolationTimeSimTicks`.
> * **GhostField** `MaxSmoothingDistance` allows you to disable interpolation when the values change more than the specified limit between two snapshots. This is useful for dealing with teleportation for example.
> * Finally the **GhostField** has a `SubType` property which can be set to an integer value to use special serialization rules supplied for that specific field.
>
> ### Entity spawning
>
> When the client side receives a new ghost, the ghost type is determined by a set of classification systems and then a spawn system spawns it. There is no specific spawn message, and when the client receives an unknown ghost ID, it counts as an implicit spawn.
>
> Because the client interpolates snapshot data, Unity cannot spawn entities immediately, unless it was preemptively spawned, such as with spawn prediction. This is because the data is not ready for the client to interpolate it. Otherwise, the object would appear and then not get any more updates until the interpolation is ready.
>
> Therefore normal spawns happen in a delayed manner. Spawning is split into three main types as follows:
>
> * **Delayed or interpolated spawning** (Asteroids Prefab)**.** The entity is spawned when the interpolation system is ready to apply updates. This is how remote entities are handled, because they are interpolated in a straightforward manner.
> * **Predicted spawning for the client predicted player object** (Player Prefab)**.** The object is predicted so the input handling applies immediately. Therefore, it doesn't need to be delay spawned. While the snapshot data for this object arrives, the update system applies the data directly to the object and then plays back the local inputs which have happened since that time, and corrects mistakes in the prediction.
> * **Predicted spawning for player spawned objects** (Bullet Prefab)**.** These are objects that the player input spawns, like in-game bullets or rockets that the player fires.
>
> The spawn code needs to run on the client, in the client prediction system. The spawn should use the predicted client version of the ghost prefab and add a **PredictedGhostSpawnRequestComponent** to it. Then, when the first snapshot update for the entity arrives it will apply to that predict spawned object (no new entity is created). After this, the snapshot updates are applied the same as in the predicted spawning for client predicted player object model.\
> To create the prefab for predicted spawning, you should use the utility method [GhostCollectionSystem.CreatePredictedSpawnPrefab](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostCollectionSystem.html).
>
> You need to implement some specific code to handle the predicted spawning for player spawned objects. You need to create a system updating in the **ClientSimulationSystemGroup** after **GhostSpawnClassificationSystem**. The system needs to go through the **GhostSpawnBuffer** buffer stored on a singleton with a **GhostSpawnQueueComponent**. For each entry in that list it should compare to the entries in the **PredictedGhostSpawn** buffer on the singleton with a **PredictedGhostSpawnList** component. If the two entries are the same the classification system should set the **PredictedSpawnEntity** property in the **GhostSpawnBuffer** and remove the entry from **GhostSpawnBuffer**.
>
> NetCode spawns entities on the client with a Prefab stored in the NetCode spawns entities on clients when there is a Prefab available for it. Pre spawned ghosts will work without any special consideration since they are referenced in a sub scene, but for manually spawned entities you must make sure that the prefabs exist on the client. You make sure that happens by having a component in a scene which references the prefab you want to spawn.
>
> From [NetCode Ghost snapshots documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/ghost-snapshots.html)
>
> Prefabs in parenthesis in spawning types added my Moetsi

From the 3 types of spawning described in the section above, asteroids are **delayed, interpolated, or spawning** (re-read the section above if you missed it). The server will spawn the asteroids, and when the interpolation system is ready to apply updates, the client will then spawn them as well.

To the client, asteroids just "appear" (because NetCode syncs ghosted entities) and their movement is updated through Snapshots.&#x20;

The client is not running any Physics code for the asteroid movement; the asteroids will move because of updated snapshots from the server. The server runs the systems that take in the PhysicsVelocity and updates the asteroids positions, these updates are then sent to the clients.

If it sounds like we are being repetitive, we are. It is important to understand the concept of interpolated ghosts to make sense of our implementations 💪.

{% hint style="info" %}
If the concepts 'interpolated spawning' and 'predicted spawning' are making your head spin, read and watch the explainer content we provided in the "Overview" page of this DOTS NetCode section.
{% endhint %}

#### Now, let's implement:

* Select the Asteroid prefab and add a GhostAuthoringComponent. Fill out the following fields:
  * Name =  "Asteroid"
  * Importance = "100"
  * Supported Ghost  Modes = All
  * Default Ghost Mode = Interpolated
  * Optimization Mode = Dynamic
* Click "Update component list" to see the components that will be ghosted
  * The list of components that appear have a marker S/IC/PC
    * This stands for:
      * "Server" (the component is only available on the server)
      * "Interpolated Client" (the component is only available on clients where the ghost is interpolated)
      * "Predicted Client" (the component is only available on clients where the ghost is predicted)
    * You might be curious why there is a "RenderMesh" component, yet rendering is not needed on the server? (we can update to remove this but we don't want to get so complicated so quickly)
  * When we add the GhostAuthoringComponent to our Player prefab and our Bullet prefab we will see how these values change
  * At the bottom of the component list you'll find the Rotation and Translation components. Expand these components:
    * Here you can see different fields configured for quantization and interpolation
  * &#x20;You can see how "customizable" what data sent where can get
    * This has been a large focus in recent releases

{% hint style="info" %}
If any of the fields above are confusing, check out the background information above or read it straight from the source in [NetCode's Ghost snapshots documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/ghost-snapshots.html)
{% endhint %}

![](/files/-MRqGEv3dGpqt-BtXI5S)

We need to update the systems that interacted with asteroids. Let's start with AsteroidSpawnSystem.

* Find AsteroidSpawnSystem and replace the current code with the code snippet below:&#x20;

```
using System.Diagnostics;
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Burst;
using Unity.Physics;
using Unity.NetCode;

//Asteroid spawning will occur on the server
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
public partial class AsteroidSpawnSystem : SystemBase
{
    //This will be our query for Asteroids
    private EntityQuery m_AsteroidQuery;

    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimECB;

    //This will be our query to find GameSettingsComponent data to know how many and where to spawn Asteroids
    private EntityQuery m_GameSettingsQuery;

    //This will save our Asteroid prefab to be used to spawn Asteroids
    private Entity m_Prefab;

    //This is the query for checking network connections with clients
    private EntityQuery m_ConnectionGroup;

    protected override void OnCreate()
    {
        //This is an EntityQuery for our Asteroids, they must have an AsteroidTag
        m_AsteroidQuery = GetEntityQuery(ComponentType.ReadWrite<AsteroidTag>());

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimECB = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //This is an EntityQuery for the GameSettingsComponent which will drive how many Asteroids we spawn
        m_GameSettingsQuery = GetEntityQuery(ComponentType.ReadWrite<GameSettingsComponent>());

        //This says "do not go to the OnUpdate method until an entity exists that meets this query"
        //We are using GameObjectConversion to create our GameSettingsComponent so we need to make sure 
        //The conversion process is complete before continuing
        RequireForUpdate(m_GameSettingsQuery);

        //This will be used to check how many connected clients there are
        //If there are no connected clients the server will not spawn asteroids to save CPU
        m_ConnectionGroup = GetEntityQuery(ComponentType.ReadWrite<NetworkStreamConnection>());
    }
    
    protected override void OnUpdate()
    {
        //Here we check the amount of connected clients
        if (m_ConnectionGroup.IsEmptyIgnoreFilter)
        {
            // No connected players, just destroy all asteroids to save CPU
            EntityManager.DestroyEntity(m_AsteroidQuery);
            return;
        }

        //Here we set the prefab we will use
        if (m_Prefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's AsteroidAuthoringComponent
            //and set m_Prefab to its Prefab value
            m_Prefab = GetSingleton<AsteroidAuthoringComponent>().Prefab;
            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        //Because of how ECS works we must declare local variables that will be used within the job
        //You cannot "GetSingleton<GameSettingsComponent>()" from within the job, must be declared outside
        var settings = GetSingleton<GameSettingsComponent>();

        //Here we create our commandBuffer where we will "record" our structural changes (creating an Asteroid)
        var commandBuffer = m_BeginSimECB.CreateCommandBuffer();

        //This provides the current amount of Asteroids in the EntityQuery
        var count = m_AsteroidQuery.CalculateEntityCountWithoutFiltering();

        //We must declare our prefab as a local variable (ECS funny business)
        var asteroidPrefab = m_Prefab;

        //We will use this to generate random positions
        var rand = new Unity.Mathematics.Random((uint)Stopwatch.GetTimestamp());

        Job
        .WithCode(() => {
            for (int i = count; i < settings.numAsteroids; ++i)
            {
                // this is how much within perimeter asteroids start
                var padding = 0.1f;

                // we are going to have the asteroids start on the perimeter of the level
                // choose the x, y, z coordinate of perimeter
                // so the x value must be from negative levelWidth/2 to positive levelWidth/2 (within padding)
                var xPosition = rand.NextFloat(-1f*((settings.levelWidth)/2-padding), (settings.levelWidth)/2-padding);
                // so the y value must be from negative levelHeight/2 to positive levelHeight/2 (within padding)
                var yPosition = rand.NextFloat(-1f*((settings.levelHeight)/2-padding), (settings.levelHeight)/2-padding);
                // so the z value must be from negative levelDepth/2 to positive levelDepth/2 (within padding)
                var zPosition = rand.NextFloat(-1f*((settings.levelDepth)/2-padding), (settings.levelDepth)/2-padding);
                
                //We now have xPosition, yPostiion, zPosition in the necessary range
                //With "chooseFace" we will decide which face of the cube the Asteroid will spawn on
                var chooseFace = rand.NextFloat(0,6);
                
                //Based on what face was chosen, we x, y or z to a perimeter value
                //(not important to learn ECS, just a way to make an interesting prespawned shape)
                if (chooseFace < 1) {xPosition = -1*((settings.levelWidth)/2-padding);}
                else if (chooseFace < 2) {xPosition = (settings.levelWidth)/2-padding;}
                else if (chooseFace < 3) {yPosition = -1*((settings.levelHeight)/2-padding);}
                else if (chooseFace < 4) {yPosition = (settings.levelHeight)/2-padding;}
                else if (chooseFace < 5) {zPosition = -1*((settings.levelDepth)/2-padding);}
                else if (chooseFace < 6) {zPosition = (settings.levelDepth)/2-padding;}

                //we then create a new translation component with the randomly generated x, y, and z values                
                var pos = new Translation{Value = new float3(xPosition, yPosition, zPosition)};

                //on our command buffer we record creating an entity from our Asteroid prefab
                var e = commandBuffer.Instantiate(asteroidPrefab);

                //we then set the Translation component of the Asteroid prefab equal to our new translation component
                commandBuffer.SetComponent(e, pos);

                //We will now set the PhysicsVelocity of our asteroids
                //here we generate a random Vector3 with x, y and z between -1 and 1
                var randomVel = new Vector3(rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f));
                //next we normalize it so it has a magnitude of 1
                randomVel.Normalize();
                //now we set the magnitude equal to the game settings
                randomVel = randomVel * settings.asteroidVelocity;
                //here we create a new VelocityComponent with the velocity data
                var vel = new PhysicsVelocity{Linear = new float3(randomVel.x, randomVel.y, randomVel.z)};
                //now we set the velocity component in our asteroid prefab
                commandBuffer.SetComponent(e, vel);

            }
        }).Schedule();

        //This will add our dependency to be played back on the BeginSimulationEntityCommandBuffer
        m_BeginSimECB.AddJobHandleForProducer(Dependency);
    }
}
```

* This system is now set to run on the server only
* We have included a check for connected clients and a delete-all of asteroids if there are none collected to save CPU
* Let's move the AsteroidSpawnSystem file into Server/Systems folder
* Now, let's hit play and see what happens

![Updating AsteroidSpawnSystem and hitting play](/files/-MRqHAUhFXrrNmP5G6QY)

* Holy moly that's a lot of errors, let's focus on this one:

![Error "Found a ghost in the ghost map which does not have an entity connected to it"](/files/-MRqHDWe9ZIkDFPMpBow)

* One of the errors is that the client is running the two systems (1) AsteroidsOutOfBoundsSystem and (2) AsteroidsDestructionSystem and deleting the server-spawned interpolated ghosts (not good!)
  * Only the server can decide what is destroyed and the ultimate "state" of the game
    * This is the "authoritative" part of NetCode's authoritative server client-predicted model
  * We need to update those two systems to only run on the server
* You will also notice there is now a bigger delay before the asteroids "appear"
  * That is because now the asteroid snapshots must be sent to the client before they are spawned
  * The wait for the data transfer creates the delay
* Let's start with updating AsteroidsOutOfBoundsSystem by updating the code in AsteroidsOutOfBoundsSystem.cs to:

```
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;
using Unity.NetCode;


//We cannot use [UpdateInGroup(typeof(ServerSimulationSystemGroup))] because we already have a group defined
//So we specify instead what world the system must run, ServerWorld
[UpdateInWorld(TargetWorld.Server)]
//We are adding this system within the FixedStepSimulationGroup
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(EndFixedStepSimulationEntityCommandBufferSystem))] 
public partial class AsteroidsOutOfBoundsSystem : SystemBase
{
    //We are going to use the EndFixedStepSimECB
    //This is because when we use Unity Physics our physics will run in the FixedStepSimulationSystem
    //We are dipping our toes into placing our systems in specific system groups
    //The FixedStepSimGroup has its own EntityCommandBufferSystem we will use to make the structural change
    //of adding the DestroyTag
    private EndFixedStepSimulationEntityCommandBufferSystem m_EndFixedStepSimECB;
    
    protected override void OnCreate()
    {
        //We grab the EndFixedStepSimECB for our OnUpdate
        m_EndFixedStepSimECB = World.GetOrCreateSystem<EndFixedStepSimulationEntityCommandBufferSystem>();
        
        //We want to make sure we don't update until we have our GameSettingsComponent
        //because we need the data from this component to know where the perimeter of our cube is
        RequireSingletonForUpdate<GameSettingsComponent>();
    }

    protected override void OnUpdate()
    {
        //We want to run this as parallel jobs so we need to add "AsParallelWriter" when creating
        //our command buffer
        var commandBuffer = m_EndFixedStepSimECB.CreateCommandBuffer().AsParallelWriter();

        //We must declare our local variables that we will use in our job
        var settings = GetSingleton<GameSettingsComponent>();

        //This time we query entities with components by using "WithAll" tag
        //This makes sure that we only grab entities with an AsteroidTag component so we don't affect other entities
        //that might have passed the perimeter of the cube  
        Entities
        .WithAll<AsteroidTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in Translation position) =>
        {
            //We check if the current Translation value is out of bounds
            if (Mathf.Abs(position.Value.x) > settings.levelWidth/2 ||
                Mathf.Abs(position.Value.y) > settings.levelHeight/2 ||
                Mathf.Abs(position.Value.z) > settings.levelDepth/2)
            {
                //If it is out of bounds wee add the DestroyTag component to the entity and return
                commandBuffer.AddComponent(entityInQueryIndex, entity, new DestroyTag());
                return;
            }

        }).ScheduleParallel();

        //We add the dependencies to the CommandBuffer that will be playing back these structural changes (adding a DestroyTag)
        m_EndFixedStepSimECB.AddJobHandleForProducer(Dependency);
    
    }
}
```

* Next, update AsteroidsDestructionSystem by updating the code in AsteroidsDestructionSystem.cs to:

  &#x20;

```
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;
using Unity.NetCode;

//We cannot use [UpdateInGroup(typeof(ServerSimulationSystemGroup))] because we already have a group defined
//So we specify instead what world the system must run, ServerWorld
[UpdateInWorld(TargetWorld.Server)]
//We are going to update LATE once all other systems are complete
//because we don't want to destroy the Entity before other systems have
//had a chance to interact with it if they need to
[UpdateInGroup(typeof(LateSimulationSystemGroup))]
public partial class AsteroidsDestructionSystem : SystemBase
{
    private EndSimulationEntityCommandBufferSystem m_EndSimEcb;    

    protected override void OnCreate()
    {
        //We grab the EndSimulationEntityCommandBufferSystem to record our structural changes
        m_EndSimEcb = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    
    protected override void OnUpdate()
    {
        //We add "AsParallelWriter" when we create our command buffer because we want
        //to run our jobs in parallel
        var commandBuffer = m_EndSimEcb.CreateCommandBuffer().AsParallelWriter();

        //We now any entities with a DestroyTag and an AsteroidTag
        //We could just query for a DestroyTag, but we might want to run different processes
        //if different entities are destroyed, so we made this one specifically for Asteroids
        Entities
        .WithAll<DestroyTag, AsteroidTag>()
        .ForEach((Entity entity, int entityInQueryIndex) =>
        {
            commandBuffer.DestroyEntity(entityInQueryIndex, entity);

        }).ScheduleParallel();

        //We then add the dependencies of these jobs to the EndSimulationEntityCOmmandBufferSystem
        //that will be playing back the structural changes recorded in this sytem
        m_EndSimEcb.AddJobHandleForProducer(Dependency);
    
    }
}
```

* Move both system files into the Server/Systems folder and hit play

![Updating the asteroid systems, moving them into Server/Systems folder, and hitting play](/files/-MRqIIs7oR2mQGRLFWvd)

* Looking much better!
* Let's do some housekeeping:
  * Move AsteroidAuthoringComponent into Server/Components
  * Move AsteroidTag into Mixed/Components
  * Then reattach AsteroidAuthoringComponent in ConvertedSubScene and drag the Asteroid Prefab onto it. as well as re-add AsteroidTag onto the Prefab
    * Moving the location of files sometimes causes issues!
  * Move DestroyTag into Mixed/Components
* Check out the DOTS Windows and see how the Asteroid systems are only running in ServerWorld
* No gif here, we believe in you 💪

{% hint style="success" %}
We now have server-spawned asteroids appearing on the client

* We added a GhostAuthoring component on our Asteroid prefab
* We updated our AsteroidSpawnSystem, AsteroidsOutOfBoundsSystem, and AsteroidsDestructionSystem
  {% endhint %}

Github branch link:&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Updating-Asteroids'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# DOTS NetCode and Player Prefabs

Code and workflows to turn the Player prefab into a NetCode ghost and spawn Thin Clients

## What you'll develop on this page

![Using PlayMode Tools to generate 2 Thin Clients and navigating through ICommandData flows](/files/-MRqx9-n_vnEvgD6atqZ)

We will update our Player prefab by "turning it into" a client-predicted ghost which spawns and moves by commands sent from the client.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Players>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## NetCode client-predicted model background

Our player will be **client-predicted**. This means we will be able to move and shoot with immediate feedback because the client will **predict** what will happen when it issues commands.

How is that possible? I thought the server was the authority, clients can't do what they want!

True (and way to go!) This is why the clients are only "predicting" what will happen based on the user's input (commands, like up, down, left, right arrow keys on a keyboard). The server makes the ultimate decision of what actually happened (by ingesting commands from all clients and deciding the truth).

{% hint style="info" %}
You are probably sick of our suggestions (pleas?) to watch Timothy Ford's talk if you haven't already watched it...

But if you have gotten this far and STILL don't know what the heck is going on with predicted-clients, do yourself a favor and checkout Timothy Ford's talk:&#x20;

[Watch Timothy Ford's talk from 24:15 to 33:05, seriously](https://www.youtube.com/watch?v=W3aieHjyNvw\&t=24m15s)
{% endhint %}

#### Spawning

Although eventually movement and shooting will be "instant" on the client (predicted), the first step, Spawning a player entity, happens as a result of the client sending an RPC to the server.&#x20;

Similar to how we updated ServerSendGameSystem in the last Section to send the newly connected client an RPC to load the game, we will do same here with Player; the client will send the server an RPC to spawn it a player. Once the server spawns the client's player entity, NetCode will send the entity to all clients, but the client that requested it will have a special version of the player entity that has a "PredictedGhostComponent" attached. This is a special NetCode component that we can use to know which ghosted entities are "owned" (predicted) by the clients. So of all the player entities in ClientWorld (as many as there are connected clients) only 1 entity will have the PredictedGhostComponent (the client's player entity).

> Prediction in a multiplayer games means that the client is running the same simulation as the server for the local player. The purpose of running the simulation on the client is so it can predictively apply inputs to the local player right away to reduce the input latency.
>
> Prediction should only run for entities which have the [PredictedGhostComponent](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.PredictedGhostComponent.html). Unity adds this component to all predicted ghosts on the client and to all ghosts on the server. On the client, the component also contains some data it needs for the prediction - such as which snapshot has been applied to the ghost.
>
> The prediction is based on a [GhostPredictionSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@0latest/index.html?subfolder=/api/Unity.NetCode.GhostPredictionSystemGroup.html) which always runs at a fixed timestep to get the same results on the client and server.
>
> \
> From [NetCode's Prediction documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/prediction.html)

Then we will use `Auto Command Target` to attach ICommandData to our player, and have NetCode automatically send those commands to the Server.

{% hint style="info" %}
We USED to say (pre v.50)

"We will then update the NCE's CommandTargetComponent's targetEntity to point at that entity in the a new PlayerGhostSpawnClassificationSystem. The server will also update its NCE's CommandTargetComponent's targetEntity field to point at the spawned entity on the server.

The CommandTargetComponent points to where the Commands sent from a client should be stored. We will be storing them in the player entities."

Now NetCode allows us to send multiple command streams just by where we attach the ICommand Data, much easier!
{% endhint %}

We will also need to update our player's camera. Currently the camera is part of the Player prefab. If we leave our Player prefab like this every time a remote client appears in ClientWorld the camera will change to that new remote client's camera (because Unity switches to the last activated camera automatically). Instead we will remove the camera from the Player prefab and instead add it to the player during PlayerGhostSpawnClassification. We will store a reference to the camera in PrefabCollection.

#### Movement

InputSpawnSystem and InputMovementSystem will no longer capture input **and** update state based on that input. Instead, client inputs will be stored as "PlayerCommands" in a new "InputSystem." The Commands are then sent to the server to playback. The server will use InputSpawnSystem and InputMovementSystem to play pack commands and update the game state. The systems will also be run by the client to "predict" what will happen. If there are any "disagreements" about what happened between the client and the server NetCode updates the state on the client to match the server.

> ## Command stream <a href="#command-stream" id="command-stream"></a>
>
> The client continuously sends a command stream to the server. This stream includes all inputs and acknowledgements of the last received snapshot. When no commands are sent a [NullCommandSendSystem](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NullCommandSendSystem.html) sends acknowledgements for received snapshots without any inputs. This is an automatic system to make sure the flow works automatically when the game does not need to send any inputs.
>
> To create a new input type, create a struct that implements the `ICommandData` interface. To implement that interface you need to provide a property for accessing the `Tick`.
>
> The serialization and registration code for the `ICommandData` will be generated automatically, but it is also possible to disable that and write the serialization manually.
>
> If you add your `ICommandData` component to a ghost which has `Has Owner` and `Support Auto Command Target` enabled in the autoring component the commands for that ghost will automatically be sent if the ghost is owned by you, is predicted, and [AutoCommandTarget](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.AutoCommandTarget.html).Enabled has not been set to false.
>
> If you are not using `Auto Command Target`, your game code must set the [CommandTargetComponent](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.CommandTargetComponent.html) on the connection entity to reference the entity that the `ICommandData` component has been attached to.
>
> You can have multiple command systems, and NetCode selects the correct one based on the `ICommandData` type of the entity that points to `CommandTargetComponent`.
>
> When you need to access inputs on the client and server, it is important to read the data from the `ICommandData` rather than reading it directly from the system. If you read the data from the system the inputs won’t match between the client and server so your game will not behave as expected.
>
> When you need to access the inputs from the buffer, you can use an extension method for `DynamicBuffer<ICommandData>` called `GetDataAtTick` which gets the matching tick for a specific frame. You can also use the `AddCommandData` utility method which adds more commands to the buffer.
>
> \
> From [NetCode's Command stream documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/command-stream.html)

#### Thin Clients

This is an experimental feature in NetCode's Multiplayer PlayMode Tools.&#x20;

Previously in the "Create a Socket Connection" section, you saw how we could add "Thin Clients," which produced more ClientWorlds and NCEs. This is part of Unity's effort to to build more tools to help developers build multiplayer games (nice!)

Currently this functionality is not well-documented and still being ironed out by Unity, so we at Moetsi have read in-between the lines from Unity sample projects and have broken down the explanation as follows:

A "Thin Client" will contain a Singleton "ThinClientComponent" in its ClientWorld. NetCode automatically adds this Singleton when Multiplayer PlayMode Tools has Num Thin Clients > 0.&#x20;

When creating input systems you must check for the Singleton ThinClientComponent, and if it exists you can create mock inputs to simulate client behavior.

As mentioned [here](https://forum.unity.com/threads/does-the-asteroids-sample-actually-use-auto-command-target.1288637/#post-8179523) by the DOTS NetCode team: "Thin clients just send the input stream to server, They don't have ghosts and they don't decompress snapshots. They can send RPC as normal client does (in case of asteroid, for the initial spawn and level loading)."

Because Thin clients do not get sent Ghosts, then we will not be able to use our "normal client" approach of sending ICommandData to the server (using the new, and awesome, Auto Command Target Approach). We will need to use the "old version" (setting the Command Target Component on the NCE).

So to keep your head on straight we will first implement spawning and commands for a "normal client". Then we will update our systems to account for thin clients.

## Updating Player spawn with NetCode

![Player spawn flow implemented in this section (if image is blurry right click and save or open in new tab to be able to zoom in)](/files/eruYmEw3yeO3M7DwjQZv)

### Updating the Player prefab

* First let's create PlayerEntityComponent in Mixed/Components. Paste this code snippet in the file

```
using Unity.Entities;
using Unity.NetCode;

[GenerateAuthoringComponent]
public struct PlayerEntityComponent : IComponentData
{
    public Entity PlayerEntity;
}
```

![Create PlayerEntityComponent](/files/-MQcdhfrqvpZv0HJ8pj-)

* Open the Player prefab and move the Camera GameObject from Hierarchy into Scripts and Prefabs. Once moved into the folder, delete the Camera GameObject from the Player prefab in Hierarchy

![Creating Camera prefab](/files/-MQcaC4GagVh3Vn2l5v2)

* Next add a GhostAuthoringComponent to the Player prefab
  * Name = Player
  * Importance = 90
  * Supported Ghost Modes = All
  * Default Ghost Mode = Owner Predicted
  * Optimization Mode = Dynamic
  * Check "Has Owner"
  * Check "Support Auto Command Target"
  * <img src="/files/khieAUWxGYu5P01WN9Vg" alt="" data-size="original">
* Finally add the PlayerEntityComponent to the prefab

![Updating Player prefab](/files/-MRqnheZBHDKYbpffXZF)

### Spawning a client-predicted player

* Create a new component called "CameraAuthoringComponent" and put it in a new folder Client/Components
* Paste the code snippet below into CameraAuthoringComponent.cs:

```
using Unity.Entities;
using UnityEngine;

[GenerateAuthoringComponent]
public struct CameraAuthoringComponent : IComponentData
{
    public Entity Prefab;
}

```

![Creating CameraAuthoringComponent in Client/Components](/files/-MQcbr-mk85II4-Y0jJn)

* Navigate to PrefabCollection in ConvertedSubScene and add CameraAuthoringComponent
* Drag the Camera prefab in Scripts and Prefabs onto the Prefab field in the CameraAuthoringComponent
* Save, return to SampleScene and reimport ConvertedSubScene

![Adding CameraAuthoringComponent to PrefabCollection](/files/-MRvMuNvuPLj3DIzxq0p)

* Now let's make PlayerSpawnRequestRpc in Mixed/Commands. Paste the code snippet below into PlayerSpawnRequestRpc.cs:

```
using Unity.NetCode;
using Unity.Entities;

public struct PlayerSpawnRequestRpc : IRpcCommand
{
}
```

![Create PlayerSpawnRequestRpc](/files/-MQcf-fLxjdTjHYkYBbe)

* In a coming section, we will update InputSpawnSystem and InputMovementSystem to InputResponseSpawnSystem and InputResponseMovementSystem
* For now let's delete InputSpawnSystem and InputMovementSystem so they do not interfere with our new work flows
* Create a new system in Client/Systems named InputSystem
* Paste the code snippet below into InputSystem.cs:

```
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;

//This is a special SystemGroup introduced in NetCode 0.5
//This group only exists on the client and is meant to be used when commands are being created
[UpdateInGroup(typeof(GhostInputSystemGroup))]
public partial class InputSystem : SystemBase
{
    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //We need this sytem group so we can grab its "ServerTick" for prediction when we respond to Commands
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;


    protected override void OnCreate()
    {

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We set our ClientSimulationSystemGroup who will provide its ServerTick needed for the Commands
        m_ClientSimulationSystemGroup = World.GetOrCreateSystem<ClientSimulationSystemGroup>();

        //The client must have loaded the game to spawn a player so we wait for the 
        //NetworkStreamInGame component added during the load game flow
        RequireSingletonForUpdate<NetworkStreamInGame>();
    }

    protected override void OnUpdate()
    {
    
        //We have removed the other inputs for now and will add them in the movement section
        byte shoot;
        shoot = 0;

        if (Input.GetKey("space"))
        {
            shoot = 1;
        };

        //Must declare local variables before using them in the .ForEach()
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();

        TryGetSingletonEntity<PlayerCommand>(out var targetEntity);
        Job.WithCode(() => {
            if (targetEntity == Entity.Null && shoot != 0)
            {
                var req = commandBuffer.CreateEntity();
                commandBuffer.AddComponent<PlayerSpawnRequestRpc>(req);
                commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent());

            }
        }).Schedule();

        //We need to add the jobs dependency to the command buffer
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

![Deleting InputSpawnSystem and InputMovementSystem and creating InputSystem in Client/Systems](/files/-MQcgH7xNzxn7x2sSwVC)

* Now let's create the system that will respond to the PlayerSpawnRequestRpc
* Create a new system named PlayerSpawnSystem in the Server/Systems folder
* Paste the code snippet below into PlayerSpawnSystem.cs:

```
using System.Diagnostics;
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.NetCode;
using UnityEngine;

//This tag is only used by the systems in this file so we define it here
public struct PlayerSpawnInProgressTag : IComponentData
{
}

//Only the server will be running this system to spawn the player
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
public partial class PlayerSpawnSystem : SystemBase
{

    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;
    private Entity m_Prefab;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We check to ensure GameSettingsComponent exists to know if the SubScene has been streamed in
        //We need the SubScene for actions in our OnUpdate()
        RequireSingletonForUpdate<GameSettingsComponent>(); 
    }

    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_Prefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's PlayerAuthoringComponent
            //and set m_Prefab to its Prefab value
            m_Prefab = GetSingleton<PlayerAuthoringComponent>().Prefab;
            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        //Because of how ECS works we must declare local variables that will be used within the job
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        var playerPrefab = m_Prefab;
        var rand = new Unity.Mathematics.Random((uint) Stopwatch.GetTimestamp());
        var gameSettings = GetSingleton<GameSettingsComponent>();
        

        //GetComponentDataFromEntity allows us to grab data from an entity that we don't have access to
        //until we are within a job
        //We know we will need to get the PlayerSpawningStateComponent from an NCE but we don't know which one yet
        //So we create a variable that will get PlayerSpawningStateComponent from an entity
        var playerStateFromEntity = GetComponentDataFromEntity<PlayerSpawningStateComponent>();

        //Similar to playerStateFromEntity, these variables WILL get data from an entity (in the job below)
        //but do not have it currently
        var networkIdFromEntity = GetComponentDataFromEntity<NetworkIdComponent>();

        //We are looking for an Entity with a PlayerSpawnRequestRpc
        //That means the client associated with that NCE wants a player to be spawned for them
        Entities
        .ForEach((Entity entity, in PlayerSpawnRequestRpc request,
            in ReceiveRpcCommandRequestComponent requestSource) =>
        {
            //We immediately destroy the request so we act on it once
            commandBuffer.DestroyEntity(entity);

            // This makes sure that we don't act on another RPC from the same NCE before we finish spawning the first
            if (!playerStateFromEntity.HasComponent(requestSource.SourceConnection) ||
                playerStateFromEntity[requestSource.SourceConnection].IsSpawning != 0)
            {
                return;
            }

            //We create our player prefab
            var player = commandBuffer.Instantiate(playerPrefab);

            //We will spawn our player in the center-ish of our game
            var width = gameSettings.levelWidth * .2f;
            var height = gameSettings.levelHeight * .2f;
            var depth = gameSettings.levelDepth * .2f;
            

            var pos = new Translation
            {
                Value = new float3(rand.NextFloat(-width, width),
                    rand.NextFloat(-height, height), rand.NextFloat(-depth, depth))
            };

            //We will not spawn a random rotation for simplicity but include
            //setting rotation for you to be able to update in your own projects if you like
            var rot = new Rotation {Value = Quaternion.identity};

            //Here we set the componets that already exist on the Player prefab
            commandBuffer.SetComponent(player, pos);
            commandBuffer.SetComponent(player, rot);
            //This sets the GhostOwnerComponent value to the NCE NetworkId (how we know what client the player belongs to)
            commandBuffer.SetComponent(player, new GhostOwnerComponent {NetworkId = networkIdFromEntity[requestSource.SourceConnection].Value});
            //This sets the PlayerEntity value in PlayerEntityComponent to the NCE
            commandBuffer.SetComponent(player, new PlayerEntityComponent {PlayerEntity = requestSource.SourceConnection});

            //Here we add a component that was not included in the Player prefab, PlayerSpawnInProgressTag
            //This is a temporary tag used to make sure the entity was able to be created and will be removed
            //in PlayerCompleteSpawnSystem below    
            commandBuffer.AddComponent(player, new PlayerSpawnInProgressTag());

            //We update the PlayerSpawningStateComponent tag on the NCE to "has a spawning/spawned player" (1)
            playerStateFromEntity[requestSource.SourceConnection] = new PlayerSpawningStateComponent {IsSpawning = 1};
        }).Schedule();


        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}

//We want to complete the spawn before ghosts are sent on the server
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
[UpdateBefore(typeof(GhostSendSystem))]
public partial class PlayerCompleteSpawnSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();

        //GetComponentDataFromEntity allows us to grab data from an entity that we don't have access to
        //until we are within a job
        //We don't know exactly which NCE we currently want to grab data from, but we do know we will want to
        //so we use GetComponentDataFromEntity to prepare ECS that we will be grabbing this data from an entity
        var playerStateFromEntity = GetComponentDataFromEntity<PlayerSpawningStateComponent>();
        var connectionFromEntity = GetComponentDataFromEntity<NetworkStreamConnection>();

        // This is used to help with clean up when players disconnect, we keep track
        var linkedEntityGroupFromEntity = GetBufferFromEntity<LinkedEntityGroup>();

        //Now the server checks for Players that are in the middle of spawning (haven't been sent yet)
        Entities
        .WithAll<PlayerSpawnInProgressTag>()
        .ForEach((Entity entity, in PlayerEntityComponent player) =>
            {
                //This is another check from Unity samples
                //This ensures there was no disconnect
                if (!playerStateFromEntity.HasComponent(player.PlayerEntity) ||
                    !connectionFromEntity[player.PlayerEntity].Value.IsCreated)
                {
                    //Player was disconnected during spawn, or other error so delete
                    commandBuffer.DestroyEntity(entity);
                    return;
                }

                //If there was no error with spawning the player we can remove the PlayerSpawnInProgressTag
                commandBuffer.RemoveComponent<PlayerSpawnInProgressTag>(entity);

                // We add the the player to the linked entity group (so we know what to delete when the player disconnects)
                var linkedEntityGroup = linkedEntityGroupFromEntity[player.PlayerEntity];
                linkedEntityGroup.Add(new LinkedEntityGroup {Value = entity});

            }).Schedule();
            
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
} 
```

* This file actually contains 2 systems:
  * PlayerSpawnSystem
  * PlayerCompleteSpawnSystem
* PlayerSpawnSystem will instantiate the prefab and set the components on the Player prefab and add a PlayerSpawnInProgressTag
  * It does not fully commit to updating  because first it will ensure that the entity made it over to the client without issues
* PlayerCompleteSpawnSystem will check for any entities with a PlayerSpawnInProgressTag (which means the entity was created) and if they exist they will remove the tag and add it to the linked entity group (used for house keeping when a player disconnects)

![](/files/-MQcgvb2mt9CK6C1qpA3)

* Finally let's create PlayerGhostSpawnClassificationSystem in Client/Systems. Paste the code snippet below into the file:

```
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.NetCode;
using UnityEngine;
using Unity.Transforms;
using Unity.Mathematics;

//We are updating only in the client world because only the client must specify exactly which player entity it "owns"
[UpdateInWorld(TargetWorld.Client)]
//We will be updating after NetCode's GhostSpawnClassificationSystem because we want
//to ensure that the PredictedGhostComponent (which it adds) is available on the player entity to identify it
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
[UpdateAfter(typeof(GhostSpawnClassificationSystem))]
public partial class PlayerGhostSpawnClassificationSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //We will store the Camera prefab here which we will attach when we identify our player entity
    private Entity m_CameraPrefab;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetExistingSystem<BeginSimulationEntityCommandBufferSystem>();

        //We need to make sure we have NCE before we start the update loop (otherwise it's unnecessary)
        RequireSingletonForUpdate<NetworkIdComponent>();
        RequireSingletonForUpdate<CameraAuthoringComponent>();
    }

    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_CameraPrefab == Entity.Null)
        {
            //We grab our camera and set our variable
            m_CameraPrefab = GetSingleton<CameraAuthoringComponent>().Prefab;
            return;
        }
        
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();
        
        //We must declare our local variables before using them
        var camera = m_CameraPrefab;
        //The "playerEntity" is the NCE
        var networkIdComponent = GetSingleton<NetworkIdComponent>();
        //The false is to signify that the data will NOT be read-only
        var commandTargetFromEntity = GetComponentDataFromEntity<CommandTargetComponent>(false);

        //We will look for Player prefabs that we have not added a "PlayerClassifiedTag" to (which means we have checked the player if it is "ours")
        Entities
        .WithAll<PlayerTag>()
        .WithNone<PlayerClassifiedTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in GhostOwnerComponent ghostOwnerComponent) =>
        {
            // If this is true this means this Player is mine (because the GhostOwnerComponent value is equal to the NetworkId)
            // Remember the GhostOwnerComponent value is set by the server and is ghosted to the client
            if (ghostOwnerComponent.NetworkId == networkIdComponent.Value)
            {
                //This creates our camera
                var cameraEntity = commandBuffer.Instantiate(entityInQueryIndex, camera);
                //This is how you "attach" a prefab entity to another
                commandBuffer.AddComponent(entityInQueryIndex, cameraEntity, new Parent { Value = entity });
                commandBuffer.AddComponent(entityInQueryIndex, cameraEntity, new LocalToParent() );
            }
            // This means we have classified this Player prefab
            commandBuffer.AddComponent(entityInQueryIndex, entity, new PlayerClassifiedTag() );

        }).ScheduleParallel();

        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}

```

![Creating PlayerGhostSpawnClassificationSystem in Client/Systems](/files/-MQch0iIdlPmL9i2LPgp)

* Hit play then hit space bar to spawn our player

![Hitting play then spawning our player with spacebar using our new server/client flow](/files/-MQchbmuAOF9ms-w4zX-)

* Great. Now we are able to spawn our player entity and have it set up in NetCode

{% hint style="warning" %}
**WARNING!**

Sometimes you can get an error here where the camera stops working after updating PlayerGhostSpawnClassificationSystem (when we added the camera on the client). Instead of switching from the main camera to the newly-added player camera (that has been added by PlayerGhostSpawnClassificationSystem), the active camera stays as the Main Camera.

From our testing this has a 50% of happening. These are the steps we recommend you take to fix this issue:&#x20;

1. Reimport all assets (this sometimes fixes it, if not go to step 2)
2. Quit out of Unity entirely, then reopen the Project (this should fix it most of the time, if not go to step 3)
3. Restart your computer (yup that's right, somehow restarting the computer has been known to fix this camera issue 😔)

If you are still having issues with the new camera flow, ask us a question on [Discord](https://discord.com/invite/88j758eUvs).
{% endhint %}

{% hint style="success" %}
&#x20;We can now spawn a client-predicted Player prefab

* We updated our Player prefab by
  * Removing the Camera GameObject
  * Adding a GhostAuthoringComponent
  * Adding a PlayerEntityComponent
* Created a CameraAuthoringComponent and adding it to the PrefabCollection
* Created PlayerSpawnRequestRpc
* Deleted InputSpawnSystem and InputMovementSystem
* Created InputSystem
* Created PlayerSpawnSystem
* Created PlayerGhostSpawnClassificationSystem
  {% endhint %}

## Updating player movement

![Creating PlayerCommands and updating game state flow implemented in this section](/files/riTEUcIAdoMHBbxY91zV)

* Let's start by creating the ICommandData component that will store our input Commands in the Mixed/Components folder
* Name it PlayerCommand
* Paste the code snippet below into PlayerCommand.cs:

```
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Burst;
using Unity.Entities;
using Unity. Transforms;
using Unity.Mathematics;


[GhostComponent(PrefabType = GhostPrefabType.AllPredicted)]
public struct PlayerCommand : ICommandData
{
    public uint Tick {get; set;}
    public byte right;
    public byte left;
    public byte thrust;
    public byte reverseThrust;
    public byte selfDestruct;
    public byte shoot;
    public float mouseX;
    public float mouseY;
}

```

![Creating PlayerCommand in Mixed/Components](/files/-MQd9g0WSe1V8PuzoakV)

* Next let's create an authoring component in the Mixed/Components folder that will add a buffer of PlayerCommands to whatever prefab we add it to
* Name it PlayerCommandBufferAuthoringComponent

```
using Unity.Entities;
using UnityEngine;

public class PlayerCommandBufferAuthoringComponent : MonoBehaviour, IConvertGameObjectToEntity
{
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddBuffer<PlayerCommand>(entity);
    }
}
```

![Creating PlayerCommandBufferAuthoringComponent](/files/-MQdA9HV9YRQ_cK8W7yz)

* We need to add our PlayerCommandBufferAuthoringComponent to our Player prefab (navigate to Player prefab and click "Add Component" in the Inspector)

![Adding PlayerCommandBufferAuthoringComponent to our Player prefab](/files/-MQdAWwycfCcLx-co7H8)

* Let's do a quick review of NetCode's prediction handling to make sense of ".ShouldPredict()" in&#x20;

> ## Prediction <a href="#prediction" id="prediction"></a>
>
> Prediction in a multiplayer games means that the client is running the same simulation as the server for the local player. The purpose of running the simulation on the client is so it can predictively apply inputs to the local player right away to reduce the input latency.
>
> Prediction should only run for entities which have the [PredictedGhostComponent](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.PredictedGhostComponent.html). Unity adds this component to all predicted ghosts on the client and to all ghosts on the server. On the client, the component also contains some data it needs for the prediction - such as which snapshot has been applied to the ghost.
>
> The prediction is based on a [GhostPredictionSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@0latest/index.html?subfolder=/api/Unity.NetCode.GhostPredictionSystemGroup.html) which always runs at a fixed timestep to get the same results on the client and server.
>
> ### Client <a href="#client" id="client"></a>
>
> The basic flow on the client is:
>
> * NetCode applies the latest snapshot it received from the server to all predicted entities.
> * While applying the snapshots, NetCode also finds the oldest snapshot it applied to any entity.
> * Once NetCode applies the snapshots, the [GhostPredictionSystemGroup](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostPredictionSystemGroup.html) runs from the oldest tick applied to any entity, to the tick the prediction is targeting.
> * When the prediction runs, the `GhostPredictionSystemGroup` sets the correct time for the current prediction tick in the ECS TimeData struct. It also sets [GhostPredictionSystemGroup.PredictingTick](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostPredictionSystemGroup.html#Unity_NetCode_GhostPredictionSystemGroup_PredictingTick) to the tick being predicted.
>
> Because the prediction loop runs from the oldest tick applied to any entity, and some entities might already have newer data, you must check whether each entity needs to be simulated or not. To perform these checks, call the static method [GhostPredictionSystemGroup.ShouldPredict](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostPredictionSystemGroup.html#Unity_NetCode_GhostPredictionSystemGroup_ShouldPredict_System_UInt32_Unity_NetCode_PredictedGhostComponent_) before updating an entity. If it returns `false` the update should not run for that entity.
>
> If an entity did not receive any new data from the network since the last prediction ran, and it ended with simulating a full tick (which is not always true when you use a dynamic timestep), the prediction continues from where it finished last time, rather than applying the network data.
>
> ### Server <a href="#server" id="server"></a>
>
> On the server the prediction loop always runs exactly once, and does not update the TimeData struct because it is already correct. It still sets `GhostPredictionSystemGroup.PredictingTick` to make sure the exact same code can be run on both the client and server.<br>
>
> From [NetCode's Prediction documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/prediction.html)

![Creating VelocityComponent and MovementSystem](/files/-MQdBbaIJNvj4cFDtB_M)

* Drag the InputSystem file into the Client/Systems folder
* Next let's update InputSystem by pasting the code snippet below into InputSystem.cs:

```
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;

//This is a special SystemGroup introduced in NetCode 0.5
//This group only exists on the client and is meant to be used when commands are being created
[UpdateInGroup(typeof(GhostInputSystemGroup))]
public partial class InputSystem : SystemBase
{
    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //We need this sytem group so we can grab its "ServerTick" for prediction when we respond to Commands
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;

        //We are going to use this to rate limit bullets per second
        //We could have included this in the game settings, no "ECS reason" not to
        private float m_PerSecond = 10f;
        private float m_NextTime = 0;

    protected override void OnCreate()
    {

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We set our ClientSimulationSystemGroup who will provide its ServerTick needed for the Commands
        m_ClientSimulationSystemGroup = World.GetOrCreateSystem<ClientSimulationSystemGroup>();


        //The client must have loaded the game to spawn a player so we wait for the 
        //NetworkStreamInGame component added during the load game flow
        RequireSingletonForUpdate<NetworkStreamInGame>();
    }

    protected override void OnUpdate()
    {
    
        //We now have all our inputs
        byte right, left, thrust, reverseThrust, selfDestruct, shoot;
        right = left = thrust = reverseThrust = selfDestruct = shoot = 0;

        //for looking around with mouse
        float mouseX = 0;
        float mouseY = 0;

        if (Input.GetKey("d"))
        {
            right = 1;
        }
        if (Input.GetKey("a"))
        {
            left = 1;
        }
        if (Input.GetKey("w"))
        {
            thrust = 1;
        }
        if (Input.GetKey("s"))
        {
            reverseThrust = 1;
        }
        if (Input.GetKey("p"))
        {
            selfDestruct = 1;
        }
        if (Input.GetKey("space"))
        {
            shoot = 1;
        }
        if (Input.GetMouseButton(1))
        {
            mouseX = Input.GetAxis("Mouse X");
            mouseY = Input.GetAxis("Mouse Y");

        }
        
        //we are going to implement rate limiting for shooting
        var canShoot = false;
        if (UnityEngine.Time.time >= m_NextTime)
        {
            canShoot = true;
            m_NextTime += (1/m_PerSecond);
        }

        //We are sending the simulationsystemgroup tick so the server can playback our commands appropriately
        var inputTargetTick = m_ClientSimulationSystemGroup.ServerTick;    
        //Must declare local variables before using them in the .ForEach()
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        // This is how we will grab the buffer of PlayerCommands from the player prefab
        var inputFromEntity = GetBufferFromEntity<PlayerCommand>();

        TryGetSingletonEntity<PlayerCommand>(out var targetEntity);
        Job.WithCode(() => {
        if (targetEntity == Entity.Null)
        {
            if (shoot != 0)
            {
                var req = commandBuffer.CreateEntity();
                commandBuffer.AddComponent<PlayerSpawnRequestRpc>(req);
                commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent());
            }
        }
        else
        {
            if (shoot == 1 && canShoot)
                shoot = 1;
            else
                shoot = 0;
            var input = inputFromEntity[targetEntity];
            input.AddCommandData(new PlayerCommand{Tick = inputTargetTick, left = left, right = right, thrust = thrust, reverseThrust = reverseThrust,
                selfDestruct = selfDestruct, shoot = shoot,
                mouseX = mouseX,
                mouseY = mouseY});
        }
        }).Schedule();

        //We need to add the jobs dependency to the command buffer
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

* This updated InputSystem is pretty intense, so take another look at the Command documentation to get a better sense of what's going on

> ## Command stream <a href="#command-stream" id="command-stream"></a>
>
> The client continuously sends a command stream to the server. This stream includes all inputs and acknowledgements of the last received snapshot. When no commands are sent a [NullCommandSendSystem](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NullCommandSendSystem.html) sends acknowledgements for received snapshots without any inputs. This is an automatic system to make sure the flow works automatically when the game does not need to send any inputs.
>
> To create a new input type, create a struct that implements the `ICommandData` interface. To implement that interface you need to provide a property for accessing the `Tick`.
>
> The serialization and registration code for the `ICommandData` will be generated automatically, but it is also possible to disable that and write the serialization manually.
>
> If you add your `ICommandData` component to a ghost which has `Has Owner` and `Support Auto Command Target` enabled in the autoring component the commands for that ghost will automatically be sent if the ghost is owned by you, is predicted, and [AutoCommandTarget](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.AutoCommandTarget.html).Enabled has not been set to false.
>
> If you are not using `Auto Command Target`, your game code must set the [CommandTargetComponent](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.CommandTargetComponent.html) on the connection entity to reference the entity that the `ICommandData` component has been attached to.
>
> You can have multiple command systems, and NetCode selects the correct one based on the `ICommandData` type of the entity that points to `CommandTargetComponent`.
>
> When you need to access inputs on the client and server, it is important to read the data from the `ICommandData` rather than reading it directly from the system. If you read the data from the system the inputs won’t match between the client and server so your game will not behave as expected.
>
> When you need to access the inputs from the buffer, you can use an extension method for `DynamicBuffer<ICommandData>` called `GetDataAtTick` which gets the matching tick for a specific frame. You can also use the `AddCommandData` utility method which adds more commands to the buffer.
>
> From [NetCode's Command stream documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/command-stream.html)

* You can see why we need to add tick data in ICommandData
  * This is how NetCode knows "when" the Command came

![](/files/-MQdCQNYmbIi1vyYf-rD)

* We need to create InputResponseMovementSystem
  * Both the server and the client use this system so put the file in Mixed/Systems folder
* Paste the code snippet below into InputResponseMovementSystem.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.NetCode;
using Unity.Networking.Transport.Utilities;
using Unity.Collections;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Jobs;
using UnityEngine;

//InputResponseMovementSystem runs on both the Client and Server
//It is predicted on the client but "decided" on the server
[UpdateInWorld(TargetWorld.ClientAndServer)]
// [UpdateInGroup(typeof(PredictedPhysicsSystemGroup))]
// want to change the Velocity BEFORE the physics is run (which happens after BuildPhysicsWorld)
// so it is not like the input had no affect on the player for a frame), so we run before BuildPhysicsWorld
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(BuildPhysicsWorld))]
public partial class InputResponseMovementSystem : SystemBase
{
    //This is a special NetCode group that provides a "prediction tick" and a fixed "DeltaTime"
    private GhostPredictionSystemGroup m_PredictionGroup;
     

    protected override void OnCreate()
    {
        // m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We will grab this system so we can use its "prediction tick" and "DeltaTime"
        m_PredictionGroup = World.GetOrCreateSystem<GhostPredictionSystemGroup>();

        // Creating this Singleton is what allows the client to predict physics
        // Once the singleton is present, all the physics systems are moved into a new group inside the GhostPredictionSystemGroup that run in sync with the ghost simulation as expected.
        // The PhysicsVelocity is replicated to all clients and it is the only thing it necessary to sync the physic state.
        Entity physicsSingleton = EntityManager.CreateEntity();
        EntityManager.AddComponentData(physicsSingleton, new PredictedPhysicsConfig {});
        
    }

    protected override void OnUpdate()
    {
        //No need for a CommandBuffer because we are not making any structural changes to any entities
        //We are setting values on components that already exist
        // var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();

        //These are special NetCode values needed to work the prediction system
        var currentTick = m_PredictionGroup.PredictingTick;
        var deltaTime = m_PredictionGroup.Time.DeltaTime;

        //We must declare our local variables before the .ForEach()
        var playerForce = GetSingleton<GameSettingsComponent>().playerForce;

        //We will grab the buffer of player commands from the player entity
        var inputFromEntity = GetBufferFromEntity<PlayerCommand>(true);
        //We are looking for player entities that have PlayerCommands in their buffer
        Entities
        .WithReadOnly(inputFromEntity)
        .WithAll<PlayerTag, PlayerCommand>()
        .ForEach((Entity entity, int entityInQueryIndex, ref Rotation rotation, ref PhysicsVelocity velocity,
                in GhostOwnerComponent ghostOwner, in PredictedGhostComponent prediction) =>
        {
            //Here we check if we SHOULD do the prediction based on the tick, if we shouldn't, we return
            if (!GhostPredictionSystemGroup.ShouldPredict(currentTick, prediction))
                return;
            
            //We grab the buffer of commands from the player entity
            var input = inputFromEntity[entity];

            //We then grab the Command from the current tick (which is the PredictingTick)
            //if we cannot get it at the current tick we make sure shoot is 0
            //This is where we will store the current tick data
            PlayerCommand inputData;
            if (!input.GetDataAtTick(currentTick, out inputData))
                inputData.shoot = 0;

            if (inputData.right == 1)
            {   //thrust to the right of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(1,0,0)).xyz * playerForce * deltaTime;                
            }
            if (inputData.left == 1)
            {   //thrust to the left of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(-1,0,0)).xyz * playerForce * deltaTime;
            }
            if (inputData.thrust == 1)
            {   //thrust forward of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(0,0,1)).xyz * playerForce * deltaTime;
            }
            if (inputData.reverseThrust == 1)
            {   //thrust backwards of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(0,0,-1)).xyz * playerForce * deltaTime;
            }

            
            if (inputData.mouseX != 0 || inputData.mouseY != 0)
            {   //move the mouse
                //here we have "hardwired" the look speed, we could have included this in the GameSettingsComponent to make it configurable
                float lookSpeedH = 2f;
                float lookSpeedV = 2f;
                Quaternion currentQuaternion = rotation.Value; 
                float yaw = currentQuaternion.eulerAngles.y;
                float pitch = currentQuaternion.eulerAngles.x;

                //MOVING WITH MOUSE
                yaw += lookSpeedH * inputData.mouseX;
                pitch -= lookSpeedV * inputData.mouseY;
                Quaternion newQuaternion = Quaternion.identity;
                newQuaternion.eulerAngles = new Vector3(pitch,yaw, 0);
                rotation.Value = newQuaternion;
            }
           
        }).ScheduleParallel();

        //No need to .AddJobHandleForProducer() because we did not need a CommandBuffer to make structural changes
    }
    
}
```

![](/files/-MQdDWQc7febYH708O6d)

* The client "predicts" the movement but the server ultimately decides game state by sending back ghost Snapshots of correct game state

![Adding VelocityComponent to Player prefab and re-building NetCode generated code](/files/-MQdE587w7s_mE0WNTZ5)

* Navigate to GameSettings in ConvertedSubScene increase the Player Force to 20 to make the player controls feel a bit more "zippy"
* Reimport ConvertedSubScene and hit "play"

![Updating GameSettings and navigating](/files/-MQdGLLyelWEb8NbJeRG)

* We are able to spawn and move around through ICommandData
* Now let's do some clean up
  * Move PlayerTag into Mixed/Components
  * Move PlayerAuthoringComponent to Server/Components
    * You will likely need to update the prefab with these scripts because it will lose track of them
* No gif here, we believe in you 💪

{% hint style="success" %}
&#x20;We can now spawn a client-predicted Player prefab and move it through commands

* We added a GhostAuthoring component on our Player prefab
* We created PlayerSpawnRequest
* We created PlayerCommand
* We merged InputSpawnSystem and InputMovementSystem into InputSystem
* We created InputResponseMovementSystem system
  {% endhint %}

## Updating Systems to Handle Thin Clients

As mentioned earlier:

As mentioned [here](https://forum.unity.com/threads/does-the-asteroids-sample-actually-use-auto-command-target.1288637/#post-8179523) by the DOTS NetCode team: "Thin clients just send the input stream to server, They don't have ghosts and they don't decompress snapshots. They can send RPC as normal client does (in case of asteroid, for the initial spawn and level loading)."

Because Thin clients do not get sent Ghosts, then we will not be able to use our "normal client" approach of sending ICommandData to the server (using the new, and awesome, Auto Command Target Approach). We will need to use the "old version" (setting the Command Target Component on the NCE).

* Let's update InputSystem.cs to generate mock data if we are a thin client

```
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;

//This is a special SystemGroup introduced in NetCode 0.5
//This group only exists on the client and is meant to be used when commands are being created
[UpdateInGroup(typeof(GhostInputSystemGroup))]
public partial class InputSystem : SystemBase
{
    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //We need this sytem group so we can grab its "ServerTick" for prediction when we respond to Commands
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;

    //We are going to use this to rate limit bullets per second
    //We could have included this in the game settings, no "ECS reason" not to
    private float m_PerSecond = 10f;
    private float m_NextTime = 0;
    //We use this for thin client command generation
    private int m_FrameCount;

    protected override void OnCreate()
    {

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We set our ClientSimulationSystemGroup who will provide its ServerTick needed for the Commands
        m_ClientSimulationSystemGroup = World.GetOrCreateSystem<ClientSimulationSystemGroup>();


        //The client must have loaded the game to spawn a player so we wait for the 
        //NetworkStreamInGame component added during the load game flow
        RequireSingletonForUpdate<NetworkStreamInGame>();
    }

    protected override void OnUpdate()
    {
        bool isThinClient = HasSingleton<ThinClientComponent>();
        if (HasSingleton<CommandTargetComponent>() && GetSingleton<CommandTargetComponent>().targetEntity == Entity.Null)
        {
            if (isThinClient)
            {
                // No ghosts are spawned, so create a placeholder struct to store the commands in
                var ent = EntityManager.CreateEntity();
                EntityManager.AddBuffer<PlayerCommand>(ent);
                SetSingleton(new CommandTargetComponent{targetEntity = ent});
            }
        }
    
        //We now have all our inputs
        byte right, left, thrust, reverseThrust, selfDestruct, shoot;
        right = left = thrust = reverseThrust = selfDestruct = shoot = 0;

        //for looking around with mouse
        float mouseX = 0;
        float mouseY = 0;

        //We are adding this difference so we can use "Num Thin Client" in "Multiplayer Mode Tools"
        //These are the instructions if we are NOT a thin client
        if (!isThinClient)
        {
            if (Input.GetKey("d"))
            {
                right = 1;
            }
            if (Input.GetKey("a"))
            {
                left = 1;
            }
            if (Input.GetKey("w"))
            {
                thrust = 1;
            }
            if (Input.GetKey("s"))
            {
                reverseThrust = 1;
            }
            if (Input.GetKey("p"))
            {
                selfDestruct = 1;
            }
            if (Input.GetKey("space"))
            {
                shoot = 1;
            }
            if (Input.GetMouseButton(1))
            {
                mouseX = Input.GetAxis("Mouse X");
                mouseY = Input.GetAxis("Mouse Y");

            }
        }
        else
        {
            // Spawn and generate some random inputs
            var state = (int) Time.ElapsedTime % 3;
            if (state == 0)
            {
                left = 1;
            }
            else {
                thrust = 1;
            }
            ++m_FrameCount;
            if (m_FrameCount % 100 == 0)
            {
                shoot = 1;
                m_FrameCount = 0;
            }
        }
        
        //we are going to implement rate limiting for shooting
        var canShoot = false;
        if (UnityEngine.Time.time >= m_NextTime)
        {
            canShoot = true;
            m_NextTime += (1/m_PerSecond);
        }

        //We are sending the simulationsystemgroup tick so the server can playback our commands appropriately
        var inputTargetTick = m_ClientSimulationSystemGroup.ServerTick;    
        //Must declare local variables before using them in the .ForEach()
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        // This is how we will grab the buffer of PlayerCommands from the player prefab
        var inputFromEntity = GetBufferFromEntity<PlayerCommand>();

        TryGetSingletonEntity<PlayerCommand>(out var targetEntity);
        Job.WithCode(() => {
        if (isThinClient && shoot != 0)
        {
            // Special handling for thin clients since we can't tell if the ship is spawned or not
            // This means every time we shoot we also send an RPC, but the Server protects against creating more Players
            var req = commandBuffer.CreateEntity();
            commandBuffer.AddComponent<PlayerSpawnRequestRpc>(req);
            commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent());
        }
        if (targetEntity == Entity.Null)
        {
            if (shoot != 0)
            {
                var req = commandBuffer.CreateEntity();
                commandBuffer.AddComponent<PlayerSpawnRequestRpc>(req);
                commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent());
            }
        }
        else
        {
            if (shoot == 1 && canShoot)
                shoot = 1;
            else
                shoot = 0;

            var input = inputFromEntity[targetEntity];
            input.AddCommandData(new PlayerCommand{Tick = inputTargetTick, left = left, right = right, thrust = thrust, reverseThrust = reverseThrust,
                selfDestruct = selfDestruct, shoot = shoot,
                mouseX = mouseX,
                mouseY = mouseY});
        }
        }).Schedule();

        //We need to add the jobs dependency to the command buffer
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

* Next let's update the PlayerSpawnSystem.cs

```
using System.Diagnostics;
using Unity.Entities;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.NetCode;
using UnityEngine;

//This tag is only used by the systems in this file so we define it here
public struct PlayerSpawnInProgressTag : IComponentData
{
}

//Only the server will be running this system to spawn the player
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
public partial class PlayerSpawnSystem : SystemBase
{

    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;
    private Entity m_Prefab;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();


        //We check to ensure GameSettingsComponent exists to know if the SubScene has been streamed in
        //We need the SubScene for actions in our OnUpdate()
        RequireSingletonForUpdate<GameSettingsComponent>(); 
    }

    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_Prefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's PlayerAuthoringComponent
            //and set m_Prefab to its Prefab value
            m_Prefab = GetSingleton<PlayerAuthoringComponent>().Prefab;
            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }

        //Because of how ECS works we must declare local variables that will be used within the job
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        var playerPrefab = m_Prefab;
        var rand = new Unity.Mathematics.Random((uint) Stopwatch.GetTimestamp());
        var gameSettings = GetSingleton<GameSettingsComponent>();

        //GetComponentDataFromEntity allows us to grab data from an entity that we don't have access to
        //until we are within a job
        //We know we will need to get the PlayerSpawningStateComponent from an NCE but we don't know which one yet
        //So we create a variable that will get PlayerSpawningStateComponent from an entity
        var playerStateFromEntity = GetComponentDataFromEntity<PlayerSpawningStateComponent>();

        //Similar to playerStateFromEntity, these variables WILL get data from an entity (in the job below)
        //but do not have it currently
        var commandTargetFromEntity = GetComponentDataFromEntity<CommandTargetComponent>();
        var networkIdFromEntity = GetComponentDataFromEntity<NetworkIdComponent>();

        //We are looking for NCEs with a PlayerSpawnRequestRpc
        //That means the client associated with that NCE wants a player to be spawned for them
        Entities
        .ForEach((Entity entity, in PlayerSpawnRequestRpc request,
            in ReceiveRpcCommandRequestComponent requestSource) =>
        {
            //We immediately destroy the request so we act on it once
            commandBuffer.DestroyEntity(entity);

            //These are checks to see if the NCE has disconnected or if there are any other issues
            //These checks are pulled from Unity samples and we have left them in even though they seem
            //Is there a PlayerSpawningState on the NCE
            //Is there a CommandTargetComponent on the NCE
            //Is the CommandTargetComponent targetEntity != Entity.Null
            //Is the PlayerSpawningState == 0
            //If all those are true we continue with spawning, otherwise we don't

            if (!playerStateFromEntity.HasComponent(requestSource.SourceConnection) ||
                !commandTargetFromEntity.HasComponent(requestSource.SourceConnection) ||
                commandTargetFromEntity[requestSource.SourceConnection].targetEntity != Entity.Null ||
                playerStateFromEntity[requestSource.SourceConnection].IsSpawning != 0)
                return;

            //We create our player prefab
            var player = commandBuffer.Instantiate(playerPrefab);

            //We will spawn our player in the center-ish of our game
            var width = gameSettings.levelWidth * .2f;
            var height = gameSettings.levelHeight * .2f;
            var depth = gameSettings.levelDepth * .2f;
            

            var pos = new Translation
            {
                Value = new float3(rand.NextFloat(-width, width),
                    rand.NextFloat(-height, height), rand.NextFloat(-depth, depth))
            };

            //We will not spawn a random rotation for simplicity but include
            //setting rotation for you to be able to update in your own projects if you like
            var rot = new Rotation {Value = Quaternion.identity};

            //Here we set the componets that already exist on the Player prefab
            commandBuffer.SetComponent(player, pos);
            commandBuffer.SetComponent(player, rot);
            //This sets the GhostOwnerComponent value to the NCE NetworkId
            commandBuffer.SetComponent(player, new GhostOwnerComponent {NetworkId = networkIdFromEntity[requestSource.SourceConnection].Value});
            //This sets the PlayerEntity value in PlayerEntityComponent to the NCE
            commandBuffer.SetComponent(player, new PlayerEntityComponent {PlayerEntity = requestSource.SourceConnection});

            //Here we add a component that was not included in the Player prefab, PlayerSpawnInProgressTag
            //This is a temporary tag used to make sure the entity was able to be created and will be removed
            //in PlayerCompleteSpawnSystem below    
            commandBuffer.AddComponent(player, new PlayerSpawnInProgressTag());

            //We update the PlayerSpawningStateComponent tag on the NCE to "currently spawning" (1)
            playerStateFromEntity[requestSource.SourceConnection] = new PlayerSpawningStateComponent {IsSpawning = 1};
        }).Schedule();


        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}

//We want to complete the spawn before ghosts are sent on the server
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
[UpdateBefore(typeof(GhostSendSystem))]
public partial class PlayerCompleteSpawnSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();

        //GetComponentDataFromEntity allows us to grab data from an entity that we don't have access to
        //until we are within a job
        //We don't know exactly which NCE we currently want to grab data from, but we do know we will want to
        //so we use GetComponentDataFromEntity to prepare ECS that we will be grabbing this data from an entity
        var playerStateFromEntity = GetComponentDataFromEntity<PlayerSpawningStateComponent>();
        var commandTargetFromEntity = GetComponentDataFromEntity<CommandTargetComponent>();
        var connectionFromEntity = GetComponentDataFromEntity<NetworkStreamConnection>();

        Entities.WithAll<PlayerSpawnInProgressTag>().
            ForEach((Entity entity, in PlayerEntityComponent player) =>
            {
                // This is another check from Unity samples
                // This ensures there was no disconnect
                if (!playerStateFromEntity.HasComponent(player.PlayerEntity) ||
                    !connectionFromEntity[player.PlayerEntity].Value.IsCreated)
                {
                    //Player was disconnected during spawn, or other error so delete
                    commandBuffer.DestroyEntity(entity);
                    return;
                }

                //If there was no error with spawning the player we can remove the PlayerSpawnInProgressTag
                commandBuffer.RemoveComponent<PlayerSpawnInProgressTag>(entity);

                //We now update the NCE to point at our player entity
                commandTargetFromEntity[player.PlayerEntity] = new CommandTargetComponent {targetEntity = entity};
                //We can now say that our player is no longer spawning so we set IsSpawning = 0 on the NCE
                playerStateFromEntity[player.PlayerEntity] = new PlayerSpawningStateComponent {IsSpawning = 0};
            }).Schedule();
            
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

* Note that the PlayerSpawnSystem checks the NCE to see if the CommandTargetComponent targetEntity has been set to see if there is already an active player for a Network Connection
  * In this way the server makes sure it doesn't spawn more than 1 player per Network Connection
* Now we can support Thin Clients, update PlayMode tools and add Thin Clients, hit play, and checkout the Players go!

{% hint style="success" %}
We can now spawn and move thin clients by generated mock data

* We updated InputSystem
* We updated PlayerSpawnSystem
  {% endhint %}

Github branch link:&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Updating-Players'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Use DOTS NetCode for Collisions and Destroying Bullet Prefabs

Code and workflows to spawn ghosted bullets and update to server-side destruction

## What you'll develop on this page

![Spawning ghosted bullets to destroy asteroids and players while interacting with Thin Clients](/files/-MQhIe0rALGHAlGuo0qV)

We will "turn" our bullet prefabs "into" ghosts and update the entity destruction flow so it is server-authoritative.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Bullets-and-Destruction>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## NetCode client-predicted model background

> ### Entity spawning
>
> When the client side receives a new ghost, the ghost type is determined by a set of classification systems and then a spawn system spawns it. There is no specific spawn message, and when the client receives an unknown ghost ID, it counts as an implicit spawn.
>
> Because the client interpolates snapshot data, Unity cannot spawn entities immediately, unless it was preemptively spawned, such as with spawn prediction. This is because the data is not ready for the client to interpolate it. Otherwise, the object would appear and then not get any more updates until the interpolation is ready.
>
> Therefore normal spawns happen in a delayed manner. Spawning is split into three main types as follows:
>
> * **Delayed or interpolated spawning.** The entity is spawned when the interpolation system is ready to apply updates. This is how remote entities are handled, because they are interpolated in a straightforward manner.
> * **Predicted spawning for the client predicted player object.** The object is predicted so the input handling applies immediately. Therefore, it doesn't need to be delay spawned. While the snapshot data for this object arrives, the update system applies the data directly to the object and then plays back the local inputs which have happened since that time, and corrects mistakes in the prediction.
> * **Predicted spawning for player spawned objects.** These are objects that the player input spawns, like in-game bullets or rockets that the player fires.
>
> #### Implement Predicted Spawning for player spawned objects <a href="#implement-predicted-spawning-for-player-spawned-objects" id="implement-predicted-spawning-for-player-spawned-objects"></a>
>
> The spawn code needs to run on the client, in the client prediction system. The spawn should use the predicted client version of the ghost prefab and add a **PredictedGhostSpawnRequestComponent** to it. Then, when the first snapshot update for the entity arrives it will apply to that predict spawned object (no new entity is created). After this, the snapshot updates are applied the same as in the predicted spawning for client predicted player object model.\
> To create the prefab for predicted spawning, you should use the utility method [GhostCollectionSystem.CreatePredictedSpawnPrefab](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostCollectionSystem.html).
>
> You need to implement some specific code to handle the predicted spawning for player spawned objects. You need to create a system updating in the **ClientSimulationSystemGroup** after [GhostSpawnClassificationSystem](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostSpawnClassificationSystem.html). The system needs to go through the **GhostSpawnBuffer** buffer stored on a singleton with a **GhostSpawnQueueComponent**. For each entry in that list it should compare to the entries in the **PredictedGhostSpawn** buffer on the singleton with a **PredictedGhostSpawnList** component. If the two entries are the same the classification system should set the **PredictedSpawnEntity** property in the **GhostSpawnBuffer** and remove the entry from **GhostSpawnBuffer**.
>
> NetCode spawns entities on clients when there is a Prefab available for it. Pre spawned ghosts will work without any special consideration since they are referenced in a sub scene, but for manually spawned entities you must make sure that the prefabs exist on the client. You make sure that happens by having a component in a scene which references the prefab you want to spawn.
>
> \
> From [NetCode's Ghost snapshots documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/ghost-snapshots.html)

We are now on the third type of spawning listed above, "Predicted spawning for player spawned objects". Asteroids were "Delayed or interpolated spawning".

A fair amount of the workflow in this section is similar to spawning the Player prefab from the previous section because it is also predicted. We will have a BulletGhostSpawnClassificationSystem that is similar to PlayerGhostSpawnClassificationSystem. However, there will be a bit of a difference in workflow within BulletGhostSpawnClassificationSystem when it comes to identifying the client's bullets.

## Updating our bullet spawn

### Updating the Bullet Prefab

* Navigate to the Bullet prefab
* Add GhostAuthoringComponent
  * Name = Bullet
  * Importance = 200
  * Supported Ghost Mode = All
  * Default Ghost Mode = Owner Predicted
  * Optimization Mode = Dynamic
  * Check "Has Owner"

![Updating the Bullet prefab](/files/-MRr-xliRHWeOfPV7hJG)

### Updating to predicted bullet spawning

* We are going to change our implementation of rate limiting from rate limiting on the client side, to rate limiting on the server side
* This is more line with the authoritative model, the server should be in control of these limits
* Paste the code snippet below into InputSystem.cs:

```
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;

//This is a special SystemGroup introduced in NetCode 0.5
//This group only exists on the client and is meant to be used when commands are being created
[UpdateInGroup(typeof(GhostInputSystemGroup))]
public partial class InputSystem : SystemBase
{
    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //We need this sytem group so we can grab its "ServerTick" for prediction when we respond to Commands
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;

    //We use this for thin client command generation
    private int m_FrameCount;

    protected override void OnCreate()
    {

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We set our ClientSimulationSystemGroup who will provide its ServerTick needed for the Commands
        m_ClientSimulationSystemGroup = World.GetOrCreateSystem<ClientSimulationSystemGroup>();


        //The client must have loaded the game to spawn a player so we wait for the 
        //NetworkStreamInGame component added during the load game flow
        RequireSingletonForUpdate<NetworkStreamInGame>();
    }

    protected override void OnUpdate()
    {
        bool isThinClient = HasSingleton<ThinClientComponent>();
        if (HasSingleton<CommandTargetComponent>() && GetSingleton<CommandTargetComponent>().targetEntity == Entity.Null)
        {
            if (isThinClient)
            {
                // No ghosts are spawned, so create a placeholder struct to store the commands in
                var ent = EntityManager.CreateEntity();
                EntityManager.AddBuffer<PlayerCommand>(ent);
                SetSingleton(new CommandTargetComponent{targetEntity = ent});
            }
        }
    
        //We now have all our inputs
        byte right, left, thrust, reverseThrust, selfDestruct, shoot;
        right = left = thrust = reverseThrust = selfDestruct = shoot = 0;

        //for looking around with mouse
        float mouseX = 0;
        float mouseY = 0;

        //We are adding this difference so we can use "Num Thin Client" in "Multiplayer Mode Tools"
        //These are the instructions if we are NOT a thin client
        if (!isThinClient)
        {
            if (Input.GetKey("d"))
            {
                right = 1;
            }
            if (Input.GetKey("a"))
            {
                left = 1;
            }
            if (Input.GetKey("w"))
            {
                thrust = 1;
            }
            if (Input.GetKey("s"))
            {
                reverseThrust = 1;
            }
            if (Input.GetKey("p"))
            {
                selfDestruct = 1;
            }
            if (Input.GetKey("space"))
            {
                shoot = 1;
            }
            if (Input.GetMouseButton(1))
            {
                mouseX = Input.GetAxis("Mouse X");
                mouseY = Input.GetAxis("Mouse Y");

            }
        }
        else
        {
            // Spawn and generate some random inputs
            var state = (int) Time.ElapsedTime % 3;
            if (state == 0)
            {
                left = 1;
            }
            else {
                thrust = 1;
            }
            ++m_FrameCount;
            if (m_FrameCount % 100 == 0)
            {
                shoot = 1;
                m_FrameCount = 0;
            }
        }

        //We are sending the simulationsystemgroup tick so the server can playback our commands appropriately
        var inputTargetTick = m_ClientSimulationSystemGroup.ServerTick;    
        //Must declare local variables before using them in the .ForEach()
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        // This is how we will grab the buffer of PlayerCommands from the player prefab
        var inputFromEntity = GetBufferFromEntity<PlayerCommand>();

        TryGetSingletonEntity<PlayerCommand>(out var targetEntity);
        Job.WithCode(() => {
        if (isThinClient && shoot != 0)
        {
            // Special handling for thin clients since we can't tell if the ship is spawned or not
            // This means every time we shoot we also send an RPC, but the Server protects against creating more Players
            var req = commandBuffer.CreateEntity();
            commandBuffer.AddComponent<PlayerSpawnRequestRpc>(req);
            commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent());
        }
        if (targetEntity == Entity.Null)
        {
            if (shoot != 0)
            {
                var req = commandBuffer.CreateEntity();
                commandBuffer.AddComponent<PlayerSpawnRequestRpc>(req);
                commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent());
            }
        }
        else
        {
            var input = inputFromEntity[targetEntity];
            input.AddCommandData(new PlayerCommand{Tick = inputTargetTick, left = left, right = right, thrust = thrust, reverseThrust = reverseThrust,
                selfDestruct = selfDestruct, shoot = shoot,
                mouseX = mouseX,
                mouseY = mouseY});
        }
        }).Schedule();

        //We need to add the jobs dependency to the command buffer
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

![Updating InputSystem so that shooting rate-limiting happens on the server](/files/-MQgdGBsOz3nFsR7gwcd)

* We need a way to store firing data on the player entity so the server can know if the client's firing system has "cooled down"
* So we are going to repurpose our BulletSpawnOffsetComponent and rename it to PlayerStateAndOffsetComponent&#x20;
  * We could make a new component for rate limiting but this would mean we would need to put 9 different components into our .ForEach() for our InputResponseSpawnSystem 😱
    * [This is doable](https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/ecs_entities_foreach.html), but a little overkill for this gitbook
* Paste the code snippet below into PlayerStateAndOffsetComponent.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;

public struct PlayerStateAndOffsetComponent : IComponentData
{
    public float3 Value;
    [GhostField]
    public int State;
    public uint WeaponCooldown;

}
```

![Updating BulletOffsetComponent to PlayerStateAndOffsetComponent (better to do file renaming in Unity or else you will get a .meta warning)](/files/-MQgdiNTHNlE1V9KaoAf)

* We also included a "State" field in the component that can be updated when the user is thrusting or firing
  * Although we won't be doing anything with "State" in this project, you are totally free to add something like change a client's color when it is thrusting or firing, if you want! To do this update the State value and create a client-only system that updates Player prefab meshes based on the State value
    * Just a thought!
* Since we updated BulletOffsetComponent we will also need to update our SetBulletSpawnOffset which references it
  * We will not rename the SetBulletSpawnOffset system and can keep the system as the same name, because even though we are adding the PlayerStateAndOffsetComponent to the entity the primary purpose of this component is to set the bullet offset
    * So the name still works (kind of)
* Paste the code snippet below into SetBulletSpawnOffset.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

public class SetBulletSpawnOffset : UnityEngine.MonoBehaviour, IConvertGameObjectToEntity
{
    public GameObject bulletSpawn;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        var bulletOffset = default(PlayerStateAndOffsetComponent);

        var offsetVector = bulletSpawn.transform.position;
        bulletOffset.Value = new float3(offsetVector.x, offsetVector.y, offsetVector.z);        

        dstManager.AddComponentData(entity, bulletOffset);
    }
}

```

![Updating SetBulletSpawnOffset to use PlayerStateAndOffsetComponent](/files/-MQgeAsJa-RhrQ1CX2Al)

* Now let's re-add our SetBulletSpawnOffset system onto our Player prefab and drag the Bullet Spawn GameObject into the bulletSpawn field
  * This really isn't necessary, but sometimes updating components that are on prefabs causes errors so better to be safe than sorry

![Removing and re-adding SetBulletOffset on the Player prefab (to be safe)](/files/-MQgeaqSxO_bSzHPfzRA)

* Now we need to create the system that will be responding to Commands that have to do with spawning
* Similar to InputResponseMovementSystem, which responds to inputs for movement, we need to create InputResponseSpawnSystem (which responds to inputs for spawning)
* Create InputResponseSpawnSystem in the Mixed/Systems folder
* Paste the code snippet below into InputResponseSpawnSystem.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.NetCode;
using Unity.Networking.Transport.Utilities;
using Unity.Collections;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Jobs;
using UnityEngine;

//InputResponseSpawntSystem runs on both the Client and Server
//It is predicted on the client but "decided" on the server
[UpdateInWorld(TargetWorld.ClientAndServer)]
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateAfter(typeof(ExportPhysicsWorld))]
public partial class InputResponseSpawnSystem : SystemBase
{
    //We will use the BeginSimulationEntityCommandBufferSystem for our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //This is a special NetCode group that provides a "prediction tick" and a fixed "DeltaTime"
    private GhostPredictionSystemGroup m_PredictionGroup;

    //This will save our Bullet prefab to be used to spawn bullets 
    private Entity m_BulletPrefab;

    //We are going to use this for "weapon cooldown"
    private const int k_CoolDownTicksCount = 5;


    protected override void OnCreate()
    {
        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //This will grab the BeginSimulationEntityCommandBuffer system to be used in OnUpdate
        m_PredictionGroup = World.GetOrCreateSystem<GhostPredictionSystemGroup>();
        
        //We check to ensure GameSettingsComponent exists to know if the SubScene has been streamed in
        //We need the SubScene for actions in our OnUpdate()
        RequireSingletonForUpdate<GameSettingsComponent>(); 
        // Make sure we have the bullet prefab to be able to create the predicted spawning
        RequireSingletonForUpdate<BulletAuthoringComponent>();        
    }

    protected override void OnUpdate()
    {

        //Here we set the prefab we will use
        if (m_BulletPrefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's BulletAuthoringComponent
            //and set m_BulletPrefab to its Prefab value
            var foundPrefab = GetSingleton<BulletAuthoringComponent>().Prefab;
            m_BulletPrefab = GhostCollectionSystem.CreatePredictedSpawnPrefab(EntityManager, foundPrefab);
            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }
        
        //We need a CommandBuffer because we will be making structural changes (creating bullet entities)
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();

        //Must declare our local variables before the jobs in the .ForEach()
        var bulletVelocity = GetSingleton<GameSettingsComponent>().bulletVelocity;
        var bulletPrefab = m_BulletPrefab;
        //These are special NetCode values needed to work the prediction system
        var deltaTime = m_PredictionGroup.Time.DeltaTime;
        var currentTick = m_PredictionGroup.PredictingTick;

        //We will grab the buffer of player command from the palyer entity
        var inputFromEntity = GetBufferFromEntity<PlayerCommand>(true);

        //We are looking for player entities that have PlayerCommands in their buffer
        Entities
        .WithReadOnly(inputFromEntity)
        .WithAll<PlayerTag, PlayerCommand>()
        .ForEach((Entity entity, int entityInQueryIndex, ref PlayerStateAndOffsetComponent bulletOffset, in Rotation rotation, in Translation position, in PhysicsVelocity velocityComponent,
                in GhostOwnerComponent ghostOwner, in PredictedGhostComponent prediction) =>
        {
            //Here we check if we SHOULD do the prediction based on the tick, if we shouldn't, we return
            if (!GhostPredictionSystemGroup.ShouldPredict(currentTick, prediction))
                return;

            //We grab the buffer of commands from the player entity
            var input = inputFromEntity[entity];

            //We then grab the Command from the current tick (which is the PredictingTick)
            //if we cannot get it at the current tick we make sure shoot is 0
            //This is where we will store the current tick data
            PlayerCommand inputData;
            if (!input.GetDataAtTick(currentTick, out inputData))
                inputData.shoot = 0;

            //Here we add the destroy tag to the player if the self-destruct button was pressed
            if (inputData.selfDestruct == 1)
            {  
                commandBuffer.AddComponent<DestroyTag>(entityInQueryIndex, entity);
            }

            var canShoot = bulletOffset.WeaponCooldown == 0 || SequenceHelpers.IsNewer(currentTick, bulletOffset.WeaponCooldown);
            if (inputData.shoot != 0 && canShoot)
            {
                // We create the bullet here
                var bullet = commandBuffer.Instantiate(nativeThreadIndex, bulletPrefab);
                //We declare it as a predicted spawning for player spawned objects by adding a special component
                commandBuffer.AddComponent(entityInQueryIndex, bullet, new PredictedGhostSpawnRequestComponent());


                //we set the bullets position as the player's position + the bullet spawn offset
                //math.mul(rotation.Value,bulletOffset.Value) finds the position of the bullet offset in the given rotation
                //think of it as finding the LocalToParent of the bullet offset (because the offset needs to be rotated in the players direction)
                var newPosition = new Translation {Value = position.Value + math.mul(rotation.Value, bulletOffset.Value).xyz};

                // bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) takes linear direction of where facing and multiplies by velocity
                // adding to the players physics Velocity makes sure that it takes into account the already existing player velocity (so if shoot backwards while moving forwards it stays in place)
                var vel = new PhysicsVelocity {Linear = (bulletVelocity * math.mul(rotation.Value, new float3(0,0,1)).xyz) + velocityComponent.Linear};

                commandBuffer.SetComponent(entityInQueryIndex, bullet, newPosition);
                commandBuffer.SetComponent(entityInQueryIndex, bullet, vel);
                commandBuffer.SetComponent(entityInQueryIndex, bullet,
                    new GhostOwnerComponent {NetworkId = ghostOwner.NetworkId});


                bulletOffset.WeaponCooldown = currentTick + k_CoolDownTicksCount;
            }

        }).ScheduleParallel();

        //We must add our dependency to the CommandBuffer because we made structural changes
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

* We identify the bullet as a Predicted spawning prefab by adding "PredictedGhostSpawnRequestComponent"&#x20;
* This allows us to identify it in the BulletGhostSpawnClassificationSystem
* The bulletOffset.WeaponCooldown may seem confusing
  * It makes sure that the Player can only fire every 5 server ticks
    * If you want to increase or decrease the rate of fire update `k_CoolDownTicksCount`

> #### Implement Predicted Spawning for player spawned objects <a href="#implement-predicted-spawning-for-player-spawned-objects" id="implement-predicted-spawning-for-player-spawned-objects"></a>
>
> The spawn code needs to run on the client, in the client prediction system. The spawn should use the predicted client version of the ghost prefab and add a **PredictedGhostSpawnRequestComponent** to it. Then, when the first snapshot update for the entity arrives it will apply to that predict spawned object (no new entity is created). After this, the snapshot updates are applied the same as in the predicted spawning for client predicted player object model.\
> To create the prefab for predicted spawning, you should use the utility method [GhostCollectionSystem.CreatePredictedSpawnPrefab](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostCollectionSystem.html).
>
> You need to implement some specific code to handle the predicted spawning for player spawned objects. You need to create a system updating in the **ClientSimulationSystemGroup** after [GhostSpawnClassificationSystem](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostSpawnClassificationSystem.html). The system needs to go through the **GhostSpawnBuffer** buffer stored on a singleton with a **GhostSpawnQueueComponent**. For each entry in that list it should compare to the entries in the **PredictedGhostSpawn** buffer on the singleton with a **PredictedGhostSpawnList** component. If the two entries are the same the classification system should set the **PredictedSpawnEntity** property in the **GhostSpawnBuffer** and remove the entry from **GhostSpawnBuffer**.
>
> NetCode spawns entities on clients when there is a Prefab available for it. Pre spawned ghosts will work without any special consideration since they are referenced in a sub scene, but for manually spawned entities you must make sure that the prefabs exist on the client. You make sure that happens by having a component in a scene which references the prefab you want to spawn.
>
> \
> From [NetCode's Ghost snapshots documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.6/manual/ghost-snapshots.html)

![](/files/-MQghlj6SZsJ-QmwfpqX)

* Now we must classify these predicted bullets with BulletGhostSpawnClassificationSystem
* Create BulletGhostSpawnClassificationSystem in the Client/Systems folder
* Paste the code snippet below into BulletGhostSpawnClassificationSystem.cs:

```
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Networking.Transport.Utilities;

//This system will only run on the client and within GhostSimulationSystemGroup
//and after GhostSpawnClassification system as is specified in the NetCode documentation
[UpdateInWorld(TargetWorld.Client)]
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
[UpdateAfter(typeof(GhostSpawnClassificationSystem))]
public partial class BulletGhostSpawnClassificationSystem : SystemBase
{
    protected override void OnCreate()
    {
        //Both of these components are needed in the OnUpdate so we will wait until they exist to update
        RequireSingletonForUpdate<GhostSpawnQueueComponent>();
        RequireSingletonForUpdate<PredictedGhostSpawnList>();
    }
    protected override void OnUpdate()
    {
        //This is the NetCode recommended method to identify predicted spawning for player spawned objects
        //More information can be found at: https://docs.unity3d.com/Packages/com.unity.netcode@0.5/manual/ghost-snapshots.html
        //under "Entity spawning"
        var spawnListEntity = GetSingletonEntity<PredictedGhostSpawnList>();
        var spawnListFromEntity = GetBufferFromEntity<PredictedGhostSpawn>();
        Dependency = Entities
            .WithAll<GhostSpawnQueueComponent>()
            .WithoutBurst()
            .ForEach((DynamicBuffer<GhostSpawnBuffer> ghosts, DynamicBuffer<SnapshotDataBuffer> data) =>
        {
            var spawnList = spawnListFromEntity[spawnListEntity];
            for (int i = 0; i < ghosts.Length; ++i)
            {
                var ghost = ghosts[i];
                if (ghost.SpawnType == GhostSpawnBuffer.Type.Predicted)
                {
                    for (int j = 0; j < spawnList.Length; ++j)
                    {
                        if (ghost.GhostType == spawnList[j].ghostType && !SequenceHelpers.IsNewer(spawnList[j].spawnTick, ghost.ServerSpawnTick + 5) && SequenceHelpers.IsNewer(spawnList[j].spawnTick + 5, ghost.ServerSpawnTick))
                        {
                            ghost.PredictedSpawnEntity = spawnList[j].entity;
                            spawnList[j] = spawnList[spawnList.Length-1];
                            spawnList.RemoveAt(spawnList.Length - 1);
                            break;
                        }
                    }
                    ghosts[i] = ghost;
                }
            }
        }).Schedule(Dependency);
    }
}

```

* This code implements the steps described in the official Unity NetCode documentation
  * It's a bit weird to follow, but no need to sweat it; this is just what needs to be done with predicted spawning
    * If you have other predicted-spawning objects you want to include in your project (i.e. not bullets) just make sure to follow the same steps as in this system

![](/files/-MQgiDh8V30CBz2xk8oF)

* Navigate to Multiplayer > PlayMode Tools and make sure "Num Thin Clients" is at 0 and save
* Hit play, then hit spacebar to spawn bullets

![Predicted-spawning bullets and receiving errors](/files/-MRr2NpqMl-hvlMH7zir)

* Although we are able to spawn bullets at first, after a while errors appear, what gives?
  * This is because our client is destroying bullets in BulletAgeSystem
    * We know that only the server can make those kind of decisions, so we will fix that in the next section
* Now for some housekeeping:
  * Move into Mixed/Components
    * BulletTag
    * BulletAgeComponent
    * BulletAuthoringComponent
    * PlayerStateAndOffsetComponent
  * Create a new folder in Scripts and Prefabs called "Authoring" (same level as Client/Mixed/Server/Multiplayer Setup)
    * Here we will start moving items that aren't necessary at runtime, just during authoring
    * Move SetBulletSpawnOffset into the "Authoring" folder
    * Move SetGameSettingsSystem into the "Authoring" folder
* No gif here, we believe in you 💪

{% hint style="success" %}
&#x20;We can now predicted-spawn bullets

* We updated our Bullet Prefab
* We updated InputSystem and removed rate-limiting
* We updated BulletOffsetComponent to PlayerStateAndOffsetComponent
* Changed the added component in SetBulletSpawnOffset
* Created InputResponseSpawnSystem
* Created BulletGhostSpawnClassificationSystem
  {% endhint %}

## Updating destruction workflows

### Bullet destruction

* We will need to update our BulletAgeSystem to run only on the server
  * The **server** must be the authority on when entities are destroyed, not the client
  * Move BulletAgeSystem into the Server/Systems folder
* Update BulletAgeSystem.cs by pasting in the code snippet below:

```
using Unity.Entities;
using Unity.NetCode;

//Only our server will decide when bullets die of old age
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
public partial class BulletAgeSystem : SystemBase
{
    //We will be using the BeginSimulationEntityCommandBuffer to record our structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;
    
    //This is a special NetCode group that provides a "prediction tick" and a fixed "DeltaTime"
    private GhostPredictionSystemGroup m_PredictionGroup;

    protected override void OnCreate()
    {
        //Grab the CommandBuffer for structural changes
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We will grab this system so we can use its "DeltaTime"
        m_PredictionGroup = World.GetOrCreateSystem<GhostPredictionSystemGroup>();
    }

    protected override void OnUpdate()
    {
        //We create our CommandBuffer and add .AsParallelWriter() because we will be scheduling parallel jobs
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();

        //We must declare local variables before using them in the job below
        var deltaTime = m_PredictionGroup.Time.DeltaTime;

        //Our query writes to the BulletAgeComponent
        //The reason we don't need to add .WithAll<BulletTag>() here is because referencing the BulletAgeComponent
        //requires the Entities to have a BulletAgeComponent and only Bullets have those
        Entities.ForEach((Entity entity, int nativeThreadIndex, ref BulletAgeComponent age) =>
        {
            age.age += deltaTime;
            if (age.age > age.maxAge)
                commandBuffer.DestroyEntity(nativeThreadIndex, entity);

        }).ScheduleParallel();
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

![Moving BulletAgeSystem to the Server/Systems folder and then updating the code](/files/-MQh4fZN9Huihekrras8)

### Player destruction

* Currently we have PlayerDestructionSystem running on both the client and the server
  * Let's update this system so that it only runs on the server
  * We'll also set the NCE CommandTargetComponent's targetEntity back to being equal to null (how it was before we spawned a player). Think of this as a "clean up"
  * It needs to be set back to null because if not, the server will not respond to any more player spawn requests from that NCE
    * This is because the server checks if the NCE is null before it spawns a player in PlayerSpawnSystem (that is one of those 4 checks)
  * First move the PlayerDestructionSystem file to the Server/Systems folder
* Paste the code snippet below into PlayerDestructionSystem.cs:

```
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;
using Unity.NetCode;

//We are going to update LATE once all other systems are complete
//because we don't want to destroy the Entity before other systems have
//had a chance to interact with it if they need to
[UpdateInWorld(TargetWorld.Server)]
[UpdateInGroup(typeof(LateSimulationSystemGroup))]
public partial class PlayerDestructionSystem : SystemBase
{
    private EndSimulationEntityCommandBufferSystem m_EndSimEcb;    

    protected override void OnCreate()
    {
        //We grab the EndSimulationEntityCommandBufferSystem to record our structural changes
        m_EndSimEcb = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    
    protected override void OnUpdate()
    {
        //We add "AsParallelWriter" when we create our command buffer because we want
        //to run our jobs in parallel
        var commandBuffer = m_EndSimEcb.CreateCommandBuffer().AsParallelWriter();

        //We are going to need to update the NCE CommandTargetComponent so we set the argument to false (not read-only)
        var commandTargetFromEntity = GetComponentDataFromEntity<CommandTargetComponent>(false);

        //We now any entities with a DestroyTag and an PlayerTag
        //We could just query for a DestroyTag, but we might want to run different processes
        //if different entities are destroyed, so we made this one specifically for Players
        //We query specifically for players because we need to clear the NCE when they are destroyed
        //In order to write over a variable that we pass through to a job we must include "WithNativeDisableParallelForRestricion"
        //It means "yes we know what we are doing, allow us to write over this variable"
        Entities
        .WithNativeDisableParallelForRestriction(commandTargetFromEntity)
        .WithAll<DestroyTag, PlayerTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in PlayerEntityComponent playerEntity) =>
        {
            // Reset the CommandTargetComponent on the Network Connection Entity to the player
            //We are able to find the NCE the player belongs to through the PlayerEntity component
            var state = commandTargetFromEntity[playerEntity.PlayerEntity]; 
            state.targetEntity = Entity.Null;
            commandTargetFromEntity[playerEntity.PlayerEntity] = state;

            //Then destroy the entity
            commandBuffer.DestroyEntity(entityInQueryIndex, entity);

        }).ScheduleParallel();

        //We then add the dependencies of these jobs to the EndSimulationEntityCOmmandBufferSystem
        //that will be playing back the structural changes recorded in this sytem
        m_EndSimEcb.AddJobHandleForProducer(Dependency);
    
    }
}

```

![Updating PlayerDestructionSystem and moving it to the Server/Systems folder](/files/-MQh5P1hBbOd1nbDtznE)

* Hit play, shoot around, self-destruct, and re-spawn to check out the updates

![Hitting play and checking out destruction workflow updates](/files/-MQh9JQt8jJHTyWP7Yj2)

* You might notice that sometimes asteroids and players sometimes turn red, but do not get destroyed. What's up with that?? Doesn't changing the render mesh to red mean the bullet and the object collided?!
  * Remember that **both** the client and server are running ChangeMaterialAndDestroySystem
  * So at times a client might "predict" that a bullet collided with an object and change the render mesh of the collided object
  * But the *server* calculated that those two entities did *not* actually collide and so the server does not add the "DestroyTag" to the object, and therefore it does not get destroyed
  * The render mesh of the object is **not ghosted**
    * That means the value of the render mesh **is not** synchronized between the server and clients
  * So because the client *predicted* something would happen that did not actually happen, we are left with red objects that do not actually get destroyed
  * We could build a workflow that changes the render mesh back to the appropriate color if the server does not confirm the hit, but that's out of scope of this gitbook
    * If you want to do this yourself and you have a cool solution you're willing to share, please let us know in the [Moetsi Discord](https://discord.com/invite/88j758eUvs)
* Now let's add 2 Thin Clients and make sure everything still functions with Thin Clients
  * Go to Multiplayer menu > PlayMode Tools > Type 2 in the Num Thin Clients field

![Adding 2 Thin Clients then hitting play](/files/-MRr4ByuzP358t8kRAzV)

* Boy, do those guys zip around!
* You will notice that based on the randomly-generated inputs in InputSystem the Thin Clients accelerate into their own bullets which causes them to get destroyed
  * This is actually useful for testing because they continue spawning as you continue to try getting shot and try shooting them. This helps ensure you that all workflows are functioning
* Final housekeeping in this section:
  * Move ChangeMaterialAndDestroySystem to Mixed/Systems
  * Move StatefulCollisionEventBufferAuthoring to Authoring
  * Move StatefulTriggerEventBufferAuthoring to Authoring
  * Move StatefulTriggerEventBufferSystem to Mixed/Systems
  * Move StatefulCollisionEventBufferSystem to Mixed/Systems
  * Move to Mixed/Components
    * IStatefulSimulationEvent
    * StatefulCollisionEvent
    * StatefulSimulationEventBuffers
    * StatefulTriggerEvent
* No gif here, we believe in you 💪

{% hint style="success" %}
&#x20;We now are able to destroy bullets and players in NetCode

* We updated BulletAgeSystem to run on server
* We updated PlayerDestructionSystem to run on the server
  {% endhint %}

Github branch link:&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Updating-Bullets-and-Destruction'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Dynamically Changing Ghosts Between Interpolated and Predicted

Code and workflows to update ghosts between interpolated and predicted

## What you'll develop on this page

![Interpolated Asteroids being changed to predicted Asteroids when near Player](/files/f55AvLzaJRjpmjBerPdI)

We will update Asteroids to be "predicted" when within a specified distance from the player. Although difficult to tell from the gif the Asteroids close to the player are moving smoother than those far away.&#x20;

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Changing-Between-Interpolated-and-Predicted>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Updating Asteroids Based on Proximity to Player

Currently all the Asteroids are interpolated. That means that the server updates their movement, and then snapshots are sent to the clients to update their position.

You might have noticed that the Asteroids movement is not as "smooth" as it was in our Physics section. The reason is that the client does not get as many update   s because of the amount of Asteroids. The server has to send a snapshot for every single Asteroid.

By interpolating all the asteroids, we reduce the computation requirements needed on the client, because they run less physics. So it is a trade-off, do we reduce computation and increase bandwidth?

The answer, like most things in engineering, it depends! For a large scale game, it probably is unnecessary to predicted the physics of everything on the map, especially when the player cannot see/interact with the predicted objects. So in this section we will implement a system that changes which Asteroids are interpolated vs. predicted based on a player's proximity.

* First let's make a ClientSettingsAuthoringComponent.cs in Authoring/ to create an authoring component where we will store the radius where Asteroids switch form interpolated to predicted

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct ClientSettings : IComponentData
{
    public float predictionRadius;
    public float predictionRadiusMargin;

}
```

* Let's add this component in ConvertedSubScene on the GameSettings GameObject
* Let's set predictionRadius to 5 and predictionRadiusMargin to 1

![Adding Client Settings component on the GameSettings object in the SubScene](/files/bqHvgz7bzT5N6sM7o8Fb)

* Now let's create AsteroidSwitchPredictionSystem.cs in Client/Systems

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.NetCode;
using Unity.Collections;
using Unity.Burst;

[UpdateInGroup(typeof(ClientSimulationSystemGroup))]
public partial class AsteroidSwitchPredictionSystem : SystemBase
{
    private NativeList<Entity> m_ToPredicted;
    private NativeList<Entity> m_ToInterpolated;
    private GhostSpawnSystem m_GhostSpawnSystem;
    protected override void OnCreate()
    {
        RequireSingletonForUpdate<ClientSettings>();
        m_ToPredicted = new NativeList<Entity>(16, Allocator.Persistent);
        m_ToInterpolated = new NativeList<Entity>(16, Allocator.Persistent);
        m_GhostSpawnSystem = World.GetExistingSystem<GhostSpawnSystem>();
    }
    protected override void OnDestroy()
    {
        m_ToPredicted.Dispose();
        m_ToInterpolated.Dispose();
    }
    protected override void OnUpdate()
    {
        var spawnSystem = m_GhostSpawnSystem;
        var toPredicted = m_ToPredicted;
        var toInterpolated = m_ToInterpolated;
        for (int i = 0; i < toPredicted.Length; ++i)
        {
            if (EntityManager.HasComponent<GhostComponent>(toPredicted[i]))
                spawnSystem.ConvertGhostToPredicted(toPredicted[i], 1.0f);
        }
        for (int i = 0; i < toInterpolated.Length; ++i)
        {
            if (EntityManager.HasComponent<GhostComponent>(toInterpolated[i]))
                spawnSystem.ConvertGhostToInterpolated(toInterpolated[i], 1.0f);
        }
        toPredicted.Clear();
        toInterpolated.Clear();

        var settings = GetSingleton<ClientSettings>();
        if (settings.predictionRadius <= 0)
            return;

        if (!TryGetSingletonEntity<PlayerCommand>(out var playerEnt) || !EntityManager.HasComponent<Translation>(playerEnt))
            return;
        var playerPos = EntityManager.GetComponentData<Translation>(playerEnt).Value;

        var radiusSq = settings.predictionRadius*settings.predictionRadius;
        Entities
            .WithNone<PredictedGhostComponent>()
            .WithAll<AsteroidTag>()
            .ForEach((Entity ent, in Translation position) =>
        {
            if (math.distancesq(playerPos, position.Value) < radiusSq)
            {
                // convert to predicted
                toPredicted.Add(ent);
            }
        }).Schedule();
        radiusSq = settings.predictionRadius + settings.predictionRadiusMargin;
        radiusSq = radiusSq*radiusSq;
        Entities
            .WithAll<PredictedGhostComponent>()
            .WithAll<AsteroidTag>()
            .ForEach((Entity ent, in Translation position) =>
        {
            if (math.distancesq(playerPos, position.Value) > radiusSq)
            {
                // convert to interpolated
                toInterpolated.Add(ent);
            }
        }).Schedule();
    }
}

```

* Now let's hit play and check out the difference for Asteroids that are close to the player
* The Asteroids that are closer to the player are moving in a smoother fashion! (because they are predicted)
* What is great is that this runs on the client side, so the client can make a decision of how much prediction to do
  * You can imagine you can have settings where lower powered devices run less prediction

{% hint style="success" %}
We now predict Asteroids that are within our prediction radius

* We created the ClientSettingsAuthoringComponent and added it to the GameSettings GameObject in ConvertedSubScene
* We created AsteroidSwitchPredictionSystem to use these settings to change nearby Asteroids to predicted from interpolated (and switch back)
  {% endhint %}

Github branch link:&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Changing-Between-Interpolated-and-Predicted'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Intro to UI Toolkit

Code and workflows on how to use UI Builder and UI Toolkit from scratch

## What you'll develop in the UI Builder and UI Toolkit section

![Navigating between scenes using UI and appropriately handling creating/destroying client/server worlds](/files/-MR6nT8oNuxIKApT1sOW)

Create a multi-view UI in a Navigation Scene, which is able to trigger a game scene, as well as game UI that is able to navigate back to our Navigation Scene.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Navigating-Between-Scenes>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

### Functionalities included

* Creating a UI document and Panel settings
* Creating a ScreenManager to handle switching between views
* Nesting uxmls and custom Visual Elements in UI Builder
* Creating custom VisualElements
  * Setting callbacks on OnGeometryChange()
  * Updating DisplayStyle to switch views
* Creating a USS document shared by multiple UXML files
  * Creating a class list
  * Extracting inline styles to a class list
  * Adding classes to elements from StyleSheets
  * Creating custom styling for :hover and :active states
* Styling a view to be responsive to changes in width to be prepared for both mobile and desktop views
  * Creating headers and footers for game information
  * Using display flex in UI builder to create responsive designs
  * Using standard Unity elements to create an interface
    * VisualElement
    * Label
    * TextField
    * Button
  * Adding a png or SVG background
* Creating a ListView
  * Setting a source of data for the ListView
  * Creating custom click events and loading data when clicking on an item in ListView
* Loading data in between scenes to configure host/client build
  * Using launch GameObjects to configure a scene to build as a host or a client
* Creating a ClientServerBootstrap to stop automatic world creation
  * Triggering client and server worlds manually
  * Supporting Thin Clients when creating client worlds
* Deleting all entities and worlds
  * Using UniversalQuery in EntityManager
  * Clean up to return to initial state

## Unity UI Builder + UI Toolkit

UI Builder is a new UI-creation tool by Unity. It allows you to easily create UI's that integrate with your Unity project. UI Toolkit  (formerly UIElement) is a collection of features, functionality, resources and tools for developing user interfaces (UI). A good way to think about the relationship between the two is UI Toolkit is the elements needed to create UI and UI Builder is the tool you use to put them together.

## [UI Toolkit overview](https://docs.unity3d.com/2020.3/Documentation/Manual/UIElements.html)

## UI Toolkit

UI Toolkit is a collection of features, functionality, resources, and tools for developing user interfaces (UI). You can use **UI**\
&#x20;Toolkit to develop custom UI and extensions for the Unity Editor, runtime debugging tools, and runtime UI for games and applications.

UI Toolkit is inspired by standard web technologies. If you have experience developing web pages or applications, much of your knowledge might be transferable, and many of the core concepts might be familiar.

| **NOTE:**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Although Unity recommends using UI Toolkit for some new UI development projects, it’s still missing features found in Unity UI (uGUI) and IMGUI. These older systems are more appropriate for certain use cases, and are required to support deprecated projects. For information about when it’s appropriate to choose an older system instead of the UI Toolkit, see the [Comparison of UI systems in Unity](https://docs.unity3d.com/2020.3/Documentation/Manual/UI-system-compare.html). |

### UI Toolkit Overview

This section provides a short description of the major UI Toolkit features, functionality, resources, and tools, including:

* [**UI system**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIElements.html#ui-system): Contains the core features and functionality required to create user interfaces.
* [**UI Assets**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIElements.html#ui-assets): Asset types inspired by standard web formats. Use them to structure and style UI.
* [**Tools and resources**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIElements.html#ui-tools-and-resources): Create and debug your interfaces, and learn how to use UI toolkit.

#### UI system

The core of UI Toolkit is a retained-mode UI system based on recognized web technologies. It supports stylesheets, and dynamic and contextual event handling.

The UI system includes the following features:

* [**Visual tree**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-VisualTree.html)**:** Defines every user interface you build with the UI Toolkit. A visual tree is an object graph, made of lightweight nodes, that holds all the elements in a window or panel.
* [**Controls**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-Controls.html)**:** A library of standard UI controls such as buttons, popups, list views, and color pickers. You can use them as-is, customize them, or create your own controls.
* [**Data binding system**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-Binding.html)**:** Links properties to the controls that modify their values.
* [**Layout Engine**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-LayoutEngine.html)**:** A layout system based on the CSS Flexbox model. It positions elements based on layout and styling properties.
* [**Event System**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-Events.html)\
  **:** Communicates user interactions to elements; for example, input, touch and pointer interactions, drag and drop operations, and other event types. The system includes a dispatcher, a handler, a synthesizer, and a library of event types.
* **UI Renderer:** A **rendering**\
  &#x20;system built directly on top of Unity’s graphics device layer.
* **UI Toolkit Runtime Support (via the** [**UI Toolkit package**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-UITK-package.html)**):** Contains the components required to create runtime UI. The UI Toolkit package is currently in preview.

#### UI Assets

The UI Toolkit provides the following Asset types that you can use to build user interfaces in a way that’s similar to how you develop web applications:

* [**UXML documents**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-UXML.html)**:** Unity eXtensible Markup Language (UXML) is an HTML and XML inspired markup language that you use to define the structure of user interfaces and reusable UI templates. Although you can build interfaces directly in C# files, Unity recommends using UXML documents in most cases.
* [**Unity Style Sheets (USS)**](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-USS.html)**:** Style sheets allow you to apply visual styles and behaviors to user interfaces. They’re similar to Cascading Style Sheets (CSS) used on the web, and support a subset of standard CSS properties. Although you can apply styles directly in C# files, Unity recommends using USS files.

#### UI Tools and resources

The UI toolkit also includes the following tools and resources to help you create UI:

* **UI Debugger:** The UI debugger is a diagnostic tool that resembles a web browser’s debugging view. Use it to explore a hierarchy of elements and get information about its underlying UXML structure and USS styles. You can find it in the Editor under **Window > UI Toolkit > Debugger**.
* [**UI Builder (package)**](https://docs.unity3d.com/2020.3/Documentation/Manual/com.unity.ui.builder.html)**:** The UI Builder lets you visually create and edit UI Toolkit assets such as UXML and USS files. The UI Builder package is currently in preview. You can install it from the Package Manager window in the Unity Editor under **Window > Package Manager**.
* **UI Samples:** The UI Toolkit includes a library of code samples for UI controls that you can view in the Editor under **Window > UI Toolkit > Samples**.

## [UI Builder overview](https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/index.html)

The **UI Builder** lets you visually create and edit UI assets such as UI Documents (UXML / `.uxml` assets), and StyleSheets (USS / `.uss` files), that you use with Unity's **UI Toolkit** (formerly UIElements). After you set up the UI Builder package, you can open the UI Builder window from the menu (**Window > UI Toolkit > UI Builder**), or from the Project window (double-click a `.uxml` asset).

> Note: In Unity 2019.x, you open the UI Builder from the **Window > UI > UI Builder** menu.
>
> Note: UI Builder is the visual authoring tool for UI Toolkit. It does not include runtime support. To enable runtime support in Unity 2020.1 and later, install the UI Toolkit package. For details, see this post on the Unity Forum: <https://forum.unity.com/threads/ui-toolkit-1-0-preview-available.927822/>.

### The main UI Builder window <a href="#the-main-ui-builder-window" id="the-main-ui-builder-window"></a>

![UI Builder Main Window](https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/images/UIBuilderAnnotatedMainWindow.png)

1. **StyleSheets**: Manage StyleSheets and USS Selectors to share styles across UI Documents (UXML) and elements.
2. **Hierarchy**: Select, reorder, reparent, cut, copy, paste, delete elements in the UI hierarchy of your document.
3. **Library**: Create new elements or instance other UI Documents (UXML).
4. **Viewport**: See what your UI Document (UXML) looks like and edit elements visually directly on the Canvas.
5. **Code Previews**: See what the UI Builder is creating as text for both the UI Document (UXML) and the StyleSheets (USS).
6. **Inspector**: Use it to change the attributes and style properties of the selected element or USS selector.

Watch Unity's talk on UI Builder here: <https://www.youtube.com/watch?v=t4tfgI1XvGs> **Gives a good overview.**

## The approach we take in this gitbook

### Single UI Document per Scene

There are a couple of ways to work with UI Toolkit. UI Toolkit requires a GameObject in a Scene with a UI Document Component that will hold a Source Asset (Visual Tree Asset).

GameObject\
&#x20;   UI Document Component\
&#x20;       Source Asset (Visual Tree Asset)

![](/files/-MQjLMgQlU83X-PaORQu)

Multiple UI views and states can be handled by multiple GameObjects. Each view can have its own GameObject and the use of scripts can enable/disable different GameObjects as needed. This will in turn enable/disable different UIs. With this 1:1 GameObject to Source Asset approach you create a good UI flow. This is a fine approach.

Instead, in this gitbook, we will be using a single GameObject per scene, yet have a "ScreenManager" as the Source Asset which will handle enabling/disabling different child Visual Elements. "ScreenManager" is not an official Unity term, but the term we at Moetsi use to indicate a "screen that manages other screens."

In both cases there are multiple visual elements for different views. However it will be easier to navigate among views and edit in our UI Builder when taking the approach we just outlined above.

It is also possible to use the "ScreenManager" approach + multiple GameObjects (and there are probably use cases where this is preferred, so no hate on that approach). In this section we'll demonstrate how to change between different GameObject/SourceAsset combinations because we will be switching between Scenes using our UI.

### Single USS sheet

Heads up: In this section we'll be working with one single USS sheet that we apply to all our UXML files. This is not a great approach for large and complicated UI projects. If you are familiar with web development, this is like having one single CSS sheet for an entire web app, which is not great for large complex systems with many teams.

So that being said, here's another reminder that the purpose of this gitbook is to introduce how to work with new technologies and put them together. Separating out a USS sheet per UXML document would be overkill for such a small project and would probably add more confusion than clarity in explaining how to work with UI Builder and UI Toolkit.

If you understand how to work with a single USS sheet it should not be too tough to include more USS sheets on your own. We will mention how to do it in this tutorial (although we will not implement it). &#x20;

## UI Toolkit's Flexbox model

UI Toolkit's layout engine is based on the CSS Flexbox Model. Knowing how to create Flexbox layouts is critical to be able to use UI Builder and UI Toolkit.

If you have not used Flexbox before we highly recommend you complete this rigorous online course to get up to speed: <https://flexboxfroggy.com/>

## UI Toolkit + DOTS

We will integrate our UI with DOTS in the next section of this gitbook called "Multiplayer (NetCode+, UI Toolkit+), **not in this section**. Instead, this section will focus on integrating UI with scripts (no DOTS) for folks who are not interested in DOTS at all but still want to learn how to use UI Builder and UI Toolkit.

## Unity resources

Unity documentation for UI Builder 1.0.0-preview\.13: <https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/index.html> **Refer to this for more information.**

Unity documentation for UI Toolkit: <https://docs.unity3d.com/2020.3/Documentation/Manual/UIElements.html> **Refer to this for more information.**

**To best prepare for the following UI Builder and UI Toolkit code-alongs, we recommend you complete the following check-list:**

* [ ] Watch Unity's UI Builder talk
* [ ] Complete Flexbox Froggy

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Create a ScreenManager

Code and workflows for navigate between different views using UI Builder and UI Toolkit

## What you'll develop on this page

![Navigating between different views in our TitleScreenUI](/files/-MQta4IlykW8ywTGQhkj)

Navigate between different views using UI Builder by creating Navigation Scene with a ScreenManager.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Creating-a-ScreenManager>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Background on the UI Approach

### Components of our UI

Often in games there is a "title" screen with options or settings the user sets up before going into the game. In this section we will implement the ability to switch between different UI views like that in this page.

We will create a new Scene called "NavigationScene." We choose the name "NavigationScene" because the users of Moetsi XR experiences usually need UI to "navigate" to the environment they want to join.

NavigationScene will have a GameObject that contains a "UI Document" Component which has a "Source Asset" field. We will put in a ScreenManager ("TitleSceneManager") that will control the display of different views in the Scene. You should think of the TitleSceneManager as the "switchboard" that decides which views are shown.

We will have 4 "views" within our TitleScreenManager:

1. Title Screen (the first view you see)
2. Host Game Screen (to host a local game)
3. Join Game Screen (to join a local game)
4. Manual Connect Screen (to manually connect to a game)

![Asteroid NavigationScene screen flow](/files/-MQyOalNZixdjhh6i0V6)

"ScreenManager" is not an official Unity term. It is a term we use to indicate a design pattern whereby a "managing" View handles the switching between different views.

### How we'll build our UI

You will notice a pattern for how we build UI. We start with an **UXML**. Usually we put a custom **Visual Element (cVE)** as the first child of the UXML.

> The most basic building block in **UI**\
> &#x20;Toolkit is a visual element. The visual elements are ordered into a hierarchy tree with parent-child relationships. The diagram below displays a simplified example of the hierarchy tree, and the rendered result in UI Toolkit.
>
> ![Simplified hierarchy of the visual tree](https://docs.unity3d.com/2020.3/Documentation/uploads/Main/VisualTreeExample.png)Simplified hierarchy of the visual tree
>
> ### Visual elements
>
> The [VisualElement](https://docs.unity3d.com/2020.3/Documentation/ScriptReference/UIElements.VisualElement.html) class is the base for all nodes in the visual tree. The `VisualElement` base class contains common properties for all controls, such as styles, layout data, and event handlers. Visual elements can have children and descendant visual elements. For example, in the diagram above, the first `Box` visual element has three child visual elements: `Label`, `Checkbox`, and `Slider`.
>
> You can customize the appearance of visual elements through stylesheets. You can also use event callbacks to modify the behavior of a visual element.
>
> `VisualElement` derives into subclasses that define additional behavior and functionality, such as controls. UI Toolkit includes a variety of built-in controls with specialized behavior. For example, the following items are available as built-in controls:
>
> * Buttons
> * Toggles
> * Text input fields
>
> You can also combine visual elements together and modify their behavior to create custom controls. For a list of built-in controls, see the [Control reference](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-Controls-Reference.html) page.
>
> From [UI Toolkit's Visual Tree Documentation](https://docs.unity3d.com/2020.3/Documentation/Manual/UIE-VisualTree.html)

Within that custom Visual Element (cVE) we put all the elements that come to mind when we think of UI: text, buttons, labels, input fields, images (these are all also "Visual Elements" in UI Toolkit terminology) and give each of the elements a "Name" so that we can reference them within our custom Visual Element (cVE).

Standard Visual Elements are Visual Elements provided by Unity with standard functionality (label, textfield, buttons, input fields, images). cVEs will be created by us and they do things like add callbacks to children Visual Elements or set data to Visual Elements.

We will follow the flow of UXML > cVE > Standard Visual elements throughout this gitbook.

![Our TitleScreenManager uxml/VE approach](/files/-MQrxx8WRSn4iK5EK0PR)

* Admittedly, this might be a bit confusing without any context so let's just get to it!

## Setting up our Components

* First let's begin by adding the necessary packages to our manifest
* Open the manifest.json file in your Project folder and add these three lines to the bottom:

```
"com.unity.ui": "1.0.0-preview.18",
"com.unity.ui.builder": "1.0.0-preview.18",
"com.unity.vectorgraphics": "2.0.0-preview.19",
```

![Adding UI packages to manifest.json (updating TextMeshPro not seen in this gif but do it)](/files/-MQoMRHZX6TWw2ADcB3A)

{% hint style="info" %}
You might get warnings and errors after adding these packages.

This is because the packages are still a bit wonky. Fear not, these warnings and errors will not cause any problems; please ignore them. Each time you open the project the errors *will* appear, but they won't cause any trouble.

This does **NOT** mean from now on you can ignore all errors in the gitbook. Make sure you do not have any errors before you add these packages and note the type of errors that appear after you install the packages. These are UI Toolkit/UI Builder errors.
{% endhint %}

* Create a new folder in "Assets" called "UI"
* Within our "Scenes" folder, right-click, select "Create", then select Scene and name the newly created scene "NavigationScene"
* Double click on "NavigationScene" to open it

![Creating UI folder and NavigationScene](/files/-MQoMW-PC6M2beCFdqfy)

* Right click in the Hierarchy and create an empty GameObject called TitleScreenUI
* Add an "Input System Event System (UI Toolkit)" component

> ## Class EventSystem <a href="#unityengine_uielements_eventsystem" id="unityengine_uielements_eventsystem"></a>
>
> Use this class to handle input, and send events to a UI Toolkit runtime panel.
>
> From [UI Toolkit's Event System documentation](https://docs.unity3d.com/Packages/com.unity.ui@1.0/api/UnityEngine.UIElements.EventSystem.html)

* Also add a "UI Document" component

> ## Class UIDocument <a href="#unityengine_uielements_uidocument" id="unityengine_uielements_uidocument"></a>
>
> Defines a Component that connects VisualElements to GameObjects. This makes it possible to render UI defined in UXML documents in the Game view.
>
> From [UI Toolkit's UIDocument documentation](https://docs.unity3d.com/Packages/com.unity.ui@1.0/api/UnityEngine.UIElements.UIDocument.html)

* Notice the field in the UI Document component called "Source Asset"
  * As mentioned in the Overview page of this section of the gitbook, this is going to be our "parent" for all our visual elements/uxmls

![Creating our TitleScreenUI GameObject](/files/-MQoMdsnnu2NhfFgMJi1)

* Open the UI folder, right-click inside, hover over "Create", navigate down to UI Toolkit, and select "Panel Setting Asset" name it "PanelSettings"

![Creating a Panel Settings Asset (and crashing Unity) ](/files/-MQoR6fkTRq6Yusyy5pJ)

> ## Class PanelSettings <a href="#unityengine_uielements_panelsettings" id="unityengine_uielements_panelsettings"></a>
>
> Defines a Panel Settings asset that instantiates a panel at runtime. The panel makes it possible for Unity to display UXML-file based UI in the Game view.
>
> From [UI Toolkit's PanelSettings documentation](https://docs.unity3d.com/Packages/com.unity.ui@1.0/api/UnityEngine.UIElements.PanelSettings.html)

* We created a "Panel Settings" with default Unity values
  * No need to update most of these as the default values work fine
  * Digging into the weeds of each field in Panel Settings is a bit overkill for this gitbook, but if you're curious we recommend that you check out the documentation linked above
* Select PanelSettings and in the inspector change Scale Model to "Constant Pixel Size"
* Drag "PanelSettings" into the "Panel Settings" field in the UI Document component found in Inspector when TitleScreenUI is selected in Hierarchy

![Dragging our PanelSettings to the UI Document Component](/files/-MQoRGld7NGwc_TV5nAV)

* Right-click in our UI folder, hover over Create, navigate down to UI Toolkit, and select "UI Document" and name the file TitleScreenManager
  * This will be our parent uxml of our NavigationScene UI
* Drag it to the Source Asset field of the UI Document component found in Inspector when TitleScreenUI is selected in Hierarchy

![Creating TitleScreenManager and dragging to SourceAsset field](/files/-MQoSYRKZN0I_u3scwvo)

* Now create your next 4 UI Documents (right-click in UI folder, Create, hover over UI Toolkit, and select UI Document, naming each individual UI Document as follows):&#x20;
  * TitleScreen
  * HostGameScreen
  * JoinGameScreen
  * ManualConnectScreen

![Creating uxmls for our next 4 views](/files/-MQoUVEfnrDZt1pLLTOZ)

* If you expand each of the uxmls (UI Documents) you will notice that they each have something called inlineStyle
  * We will have a shared USS that will allow us to share stylings across views
  * But a view might have some stylings that are specific to that one particular view and to no other views; these stylings are called "inline" styles
    * (AKA styles that are part of the document but do not come from a separate USS sheet)

* For now, we're not going to paste in any code snippets into these uxmls we just made right now

* Now let's create our 4 custom Visual Elements (cVEs)
  * Again, they will have the exact same names as the uxml files. Please try not to get confused!&#x20;

* To make each of the 4 cVEs, just right-click in the UI folder > Create > C# Script and name each file according to the four bullet points below, pasting the code snippet underneath into each cs files:&#x20;

* TitleScreenManager

```
using UnityEngine;
using UnityEngine.UIElements;

public class TitleScreenManager : VisualElement
{
    
    public new class UxmlFactory : UxmlFactory<TitleScreenManager, UxmlTraits> { }

    public TitleScreenManager()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }
}
```

* HostGameScreen

```
using UnityEngine;
using UnityEngine.UIElements;

public class HostGameScreen : VisualElement
{
    
    public new class UxmlFactory : UxmlFactory<HostGameScreen, UxmlTraits> { }

    public HostGameScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }
}
```

* JoinGameScreen

```
using UnityEngine;
using UnityEngine.UIElements;

public class JoinGameScreen : VisualElement
{
    
    public new class UxmlFactory : UxmlFactory<JoinGameScreen, UxmlTraits> { }

    public JoinGameScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }
}
```

* ManualConnectScreen

```
using UnityEngine;
using UnityEngine.UIElements;

public class ManualConnectScreen : VisualElement
{
    
    public new class UxmlFactory : UxmlFactory<ManualConnectScreen, UxmlTraits> { }

    public ManualConnectScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }
}
```

* These are the custom Visual Elements (cVEs) we will nest in our UXMLs
* In the code for each, you will notice that we work within 3 boilerplate "sections" (for lack of a better word) to build our UI. They are as follows:
  * UxmlFactory
    * (this will allow us to read from the UXML we are nested in so we can add functionality)
    * We will not alter this
  * this.RegisterCallback(OnGeometryChange)
    * We will not alter this
  * OnGeometryChange(GeometryChangedEvent evt)
    * This runs each time the layout is updated
    * Here we will assign any visual elements we want to interact with and register any callbacks
    * Think of it like Create() or OnCreate()

{% hint style="info" %}
We have named our custom Visual Elements (cVEs) after the uxmls they will be nested in.

FYI: you are able to change the name of your cVEs and nest them anywhere; the name is not a dependency for working with uxml.

However, if you do change the name you must change it in the 3 places you see the name used in the files above:

* Where the public class is defined (at the top)
* Where you define the uxml factory
* The constructor that contains registering the callback OnGeometryChange

Again, if you change the name of a cVE, the uxmls **will** **not** **automatically** pick up this change. Instead, you will need to manually update the names in the uxml files themselves.

We at Moetsi like to match names as a rule of thumb (although it may *seem* confusing) to provide clarity on exactly what each cVE should do. We don't find it too confusing because the uxmls and cVEs are different file types so the little iconography next to the file names symbolize the difference between the two.

\
These are a lot of opinionated decisions we made for the sake of the gitbook. Anyone with frontend webapp experience knows how many different ways you can skin a cat when it comes to a front end. We are trying to show you all the techniques you can use with latest packages. Actual structure and separation of concerns in projects can vary a lot based on architecture approach.
{% endhint %}

> ## Class VisualElement.UxmlFactory <a href="#unityengine_uielements_visualelement_uxmlfactory" id="unityengine_uielements_visualelement_uxmlfactory"></a>
>
> Instantiates a [VisualElement](https://docs.unity3d.com/Packages/com.unity.ui@1.0/api/UnityEngine.UIElements.VisualElement.html) using the data read from a UXML file.
>
> From [UI Toolkit's UxmlFactory documentation](https://docs.unity3d.com/Packages/com.unity.ui@1.0/api/UnityEngine.UIElements.VisualElement.UxmlFactory.html)

> ## GeometryChangedEvent
>
> Event sent after layout calculations, when the position or the dimension of an element changes. This event cannot be cancelled, it does not trickle down, and it does not bubble up.
>
> From [UIElement's GeometryChangedEvent documentation](https://docs.unity3d.com/2020.3/Documentation/ScriptReference/UIElements.GeometryChangedEvent.html)

![Creating our 4 custom Visual Elements (cVEs)](/files/-MQrqGouyhffeVvTzJUT)

* Now that we have created our uxmls and cVEs let's open up UI Builder
  * Navigate to "Window', select "UI Toolkit", then choose "UI Builder"

![Opening up UI Builder](/files/-MQrqF1txQScdy1DvXUN)

* We are now ready to to structure our components
  * You might not have "TitleScreenManager" already available in the Hierarchy when you open UI Builder, don't worry we open it up as our next step below

{% hint style="success" %}
&#x20;We created our UI components in this section and we are prepared  to structure them in the next section

* We created NavigationScene
* We added a TitleScreenUI GameObject
* We added Event System (UI Toolkit) and UI Document
* We created uxmls
  * TitleScreenManager
  * TitleScreen
  * HostGameScreen
  * JoinGameScreen
  * ManualConnectScreen
* We created custom Visual Elements (cVEs)
  * TitleScreenManager
  * HostGameScreen
  * JoinGameScreen
  * ManualConnectScreen
* We navigated to UI Builder
  {% endhint %}

## Working with UI Builder

We are going to use the UI Builder to put our different components together and to also add additional standard Visual Elements. In the next section we will also do styling in the UI Builder.

You will notice that there is no way to create new Visual Elements through the UI Builder. This is why we started this section by creating those items first. It is generally a good idea to plan out your views and structure before working with UI Builder to make construction a bit easier.

> ### The main UI Builder window <a href="#the-main-ui-builder-window" id="the-main-ui-builder-window"></a>
>
> <img src="https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/images/UIBuilderAnnotatedMainWindow.png" alt="UI Builder Main Window" data-size="original">
>
> 1. **StyleSheets**: Manage StyleSheets and USS Selectors to share styles across UI Documents (UXML) and elements.
> 2. **Hierarchy**: Select, reorder, reparent, cut, copy, paste, delete elements in the UI hierarchy of your document.
> 3. **Library**: Create new elements or instance other UI Documents (UXML).
> 4. **Viewport**: See what your UI Document (UXML) looks like and edit elements visually directly on the Canvas.
> 5. **Code Previews**: See what the UI Builder is creating as text for both the UI Document (UXML) and the StyleSheets (USS).
> 6. **Inspector**: Use it to change the attributes and style properties of the selected element or USS selector.
>
> ### &#x20;<a href="#setting-up-the-ui-builder" id="setting-up-the-ui-builder"></a>
>
> From [UI Builder's Overview documentation](https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/index.html)

{% hint style="info" %}
It's a good idea to watch [Unity's UI Builder video](https://www.youtube.com/watch?v=t4tfgI1XvGs) to get an overview of the different parts of UI Builder. This gitbook will touch on most aspects of UI Builder, but to get a more exhaustive understanding we recommend watching Unity's run-through of the entire system.

[Watch Unity's talk on UI Builder here.](https://www.youtube.com/watch?v=t4tfgI1XvGs)
{% endhint %}

* While UI Builder is open, navigate to the "Library" section and take a look at what exists on the "Standard" tab
  * Standard Visual Elements are broken out into two sections:
    * Containers
    * Controls
  * We will use these Standard Visual Element to build our UI in UI Builder
* Now click on the Project tab
  * This tab is broken out into
    * UI Documents (UXML)
    * Custom Controls (C#) (Our custom Visual Elements, cVEs)
* Expand the UI folder in the Assets folder and notice the uxmls we created
* Scroll to TitleScreenManager, hover over, and click on the icon that appears on the right
* We have now loaded our TitleScreenManager uxml into our Hierarchy
  * Be careful not to double-click any items in project as that actually loads into the Hierarchy (unless you intend to load the item you double clicked into your current Hierarchy)
* Also notice that you cannot "open" our cVEs like you can our uxmls
  * There is no icon that appears when you hover over a cVE
  * In order to update the contents of our cVEs, we must go into the actual file

![Navigating UI Builder to find our uxmls and custom Visual Elements](/files/-MQrueDn6vz9Qt3Sl2ui)

* Drag TitleScreenManager.cs from Custom Controls into the Hierarchy
* Click on TitleScreenManager.uxml in the Hierarchy and hit save
  * Generally, always click on the uxml and save after making any change
    * UI Builder is still a bit wonky, so it's is better to be safe than sorry and a necessary step to saving is selecting the uxml in the Hierarchy before hitting save

![Dragging our custom Visual Element into our uxml](/files/-MQrvRVyUlU4eB__HBHr)

{% hint style="danger" %}
**UI BUILDER CURRENTLY HAS A "DOUBLE SAVE BUG"**

Currently if you "save" after a change in UI Builder, it will save fine the first time.

But if you hit save AGAIN, immediately without making changes (and sometimes when making a change regarding children/hierarchy), it will save, BUT UI BUILDER WILL VISUALLY LOSE DATA.

If you then hit save a *third* time, it will save the lost data state (aka erasing everything).

So...

**Only "save" once per change**\
**And if you hit "save" and UI Builder loses data DO NOT HIT SAVE AGAIN (or it will save the lost data state)**\
**Instead change to another UXML in the Project hierarchy and then switch back (UI Builder will correctly show the data again)**

\
We have reached out to Unity to fix this issue. You can see it described here: <https://forum.unity.com/threads/saving-twice-in-a-row-in-ui-builder-causes-errors-and-lost-data.1296954/>

Below in the GIF we demonstrate this bug (data being lost in UI Builder) and then show that by switching to another UXML and back you can return your data state.
{% endhint %}

![Demonstration of the "double save bug" and how switching to another UXML can bring the data back. Notice the console log errors from when the second save clears the UI Builder data. But then switching to another UXML brings it back.](/files/A57M73pMdE2CtNN5BfV2)

* We now have our custom Visual Element (cVE) nested in our uxml
  * Woo! Our ScreenManager is all set up!
  * FYI: Having a "ScreenManager all set up" is when there is a parent UXML + a child cVE, which will control all other children uxmls/Visual Elements
* Click on our TitleScreenManager cVE in Hierarchy and notice how the large blue highlighted area shrinks down to a line when we click from the TitleScreenManager uxml to the cVE
  * You may need to expand the window to see this
  * This is because our cVE is currently styled to consume only as much "room" on the screen as its contents take up
    * And because our cVE is empty, the styled area shrinks

![Seeing how the areas covered by the uxml and cVE differ](/files/-MQryQSNEux6yfM-CyxQ)

{% hint style="info" %}
It is important to note that the size of the TitleScreenManager uxml in UI Builder does not have a connection to the size the game will be rendered at.

The uxml will take as much space as exists (similar to view height and view width in Web Development).

By resizing this uxml canvas, we can test how the UI will react to different screen sizes/resolutions.
{% endhint %}

> Within the **Viewport** pane, you can find the **Canvas**, a floating re-sizable edit-time container that contains a live version of the UI Document (UXML) being edited. If you can't see it, try clicking on the **Fit Canvas** button in the **Viewport** toolbar to bring it into view. Any settings related to the **Canvas**, like its size, are not saved as part of the UI Document (UXML) but will be remembered (using an internal separate settings file) for the next time you open the same UI Document (UXML).
>
> You can directly resize the **Canvas** inside the **Viewport** by dragging its edges or corners. For exact sizing, you can click on the header of the **Canvas** to access its settings via the **Inspector** pane, where you will see fields for **Canvas** height and width. In the same section, you can also lock the **Canvas** size to the size of the Unity Game Window using the **Match Game View** checkbox to better match a runtime UI environment:\
> ![CanvasSizeSettings](https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/images/CanvasSizeSettings.png)
>
> With the **Canvas** selected, in the **Inspector**, you can change the **Canvas** background to make editing the UI in context easier. You can set it to be a solid color, a specific texture (ie. a mockup from a UI Designer), or a live view from a **Camera** in the currently open Unity Scene:\
> ![CanvasBackgroundSettings](https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/images/CanvasBackgroundSettings.png)
>
> From [UI Builder's Setting up the edit-time Canvas documentation](https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/uib-getting-started-canvas.html)

* Maximize the UI Builder to make it easier to navigate
* Click on TitleScreenManager uxml and resize it to see that it the blue highlighted area always takes up the size of the canvas
  * Type in values in Canvas Size in the Inspector to see the canvas resize

    Drag the sides of the canvas and notice how the "Canvas Size" values in the Inspector change
* Hit "Fit Canvas" in the top and it will automatically set to be contained within the Viewport

![Resizing the canvas and seeing the effects in the Inspector and vice versa](/files/-MQs1V1PbQQz63mPUe36)

* We aren't going to dive much deeper into the functionality of the UI Builder because it's so thoroughly explained in [Unity's UI Builder talk](https://www.youtube.com/watch?v=t4tfgI1XvGs)
* We will move on and focus on how to make certain functionalities work
* Click on our TitleScreenManager cVE and in the Inspector, type TitleScreenManager in the "Name" field

![Naming the TitleScreenManager cVE](/files/-MQs5UBMQRKXKj8Q-2i7)

* In "StyleSheets" (top left) hit the plus sign and select "Create new USS"
* Create a new USS named "TitleScreenUI" in the "UI" folder (you might need to expand the Finder window in order to navigate to the UI folder)
* If you don't save often, sometimes UI Builder can get confused

![Creating TitleScreenUI.uss in Assets/UI](/files/-MQsBTnBaSgX9MxW1ElQ)

* Now select the TitleScreenManager cVE and in the Inspector navigate to "Flex"
  * Update "Grow" to 1
* Notice how the highlighted area now encompasses the entire canvas
  * We have "styled" our TitleScreenManager cVE to "grow" to the entire canvas
* Now under "StyleSheet" in the Inspector type ".screen" into the Style Class List input field and then click "Extract Inlined Styles to New Class"
  * Nice! We just made a new "class" of style called ".screen"
  * We can now apply this style to other Visual Elements and it will update the Visual Element's styling
  * We will use this class for all our "screens"

![Creating class ".screen"](/files/-MQsCyI9hjKG4rVPZY27)

* Now expand TitleScreenUI.uss in StyleSheets

![Seeing our new class in TitleScreenUI.uss](/files/-MQsDEqowhf19NDp0U9M)

* Here is where we see that the UI Builder has automatically put our extracted class in our USS
  * Thanks, UI Builder!
* Now let's drag our 4 other uxmls **into** our TitleScreenManager cVE
  * They should be nested within the cVE

![Nesting our 4 uxmls in our TitleScreenManager cVE](/files/-MQsGH_BwmgONnppbH4H)

{% hint style="success" %}
We now have our initial setup of our TitleScreenManager

* We nested our TitleScreenManager cVE within the uxml
* We created TitleScreenUI USS
* We created a .screen class
* We nested TitleScreen, HostGameScreen, JoinGameScreen, ManualConnectScreen uxmls in our TitleScreenManager cVE
  {% endhint %}

## Adding Visual Elements and switching views

We are going to add Visual Elements to our nested uxmls (TitleScreen, HostGameScreen, JoinGameScreen, ManualConnectScreen). While TitleScreenManager.uxml is the focus in our Hierarchy, it is not possible to add Visual Elements to the nested uxmls. We will quickly show you what we mean.

* In UI Builder, navigate to the Library, select the Standard tab, and drag a VisualElement into our nested TitleScreen uxml
* Save TitleScreenManager.uxml
* Now go to Library, then to the Project tab, and open the TitleScreen uxml
* No VisualElement!
* That is because we did not insert the VisualElement into the actual TitleScreen uxml file
  * It is part of the TitleScreenManager uxml (because it was the focus in our Hierarchy)
* Delete the VisualElement and save

![Adding VisualElement does not appear in TitleScreen uxml](/files/-MQsKSCIgRjEVE6kWpor)

* TitleScreenManager.uxml is the file that's actually keeping track of this new VisualElement
  * This approach of not actually nesting the VisualElement in the child uxml *can* work, but in this gitbook we want to have self-contained files for separation of concerns
* So let's take a brief look at the different ways we **can** edit these nested uxmls
* Right-click on TitleScreen in TitleScreenManager's Hierarchy and notice the options on the bottom:
  * Open in UI Builder
  * Open Instance in Isolation
  * Open Instance in Context
  * Show In Project
* We will be editing our uxmls through the "Open in UI Builder" option
  * This allows you to focus on the exact screen you are working on
  * This will open the uxml in the same way that navigating to Library > Project > UI Documents > uxml selection would
  * You can use the other options to edit while in the context of the greater Hierarchy but we have found it easier to focus on one view at a time when doing anything other than just minor touchups
* Click on "Open in UI Builder"

![Checking out different ways to edit a uxml](/files/-MQsLCVfVll1SS-OoP7y)

* The uxml will take up as much space as its parent
  * The parent of TitleScreenManager uxml is the actual display so it will take up as much space as the display
  * The parent of TitleScreen is our cVE (TitleScreenManager) which we have made take up the entire display as well (through the .screen class)
* We are going to add a VisualElement that takes up the entire screen to this uxml so that we can make full use of the display when adding our content
  * This might seem a little confusing at first but we want to take up the whole area with a parent element
    * We just made a full screen container, why do we need another one?!
  * Think of it as turning on the lights to a room
  * We want the light everywhere so we can place our objects relative to each other knowing the boundaries of the room
  * If we were to place our items in the room in the dark, we will not be able to control where they are relative to boundaries
  * (Eh, that wasn't our best analogy. If you have a better one please reach out in the [Moetsi Discord](https://discord.com/invite/88j758eUvs)!)
* Drag a standard VisualElement to the Hierarchy
* Change the VisualElement's name to "screen"
  * Instead of changing the name when the item is selected in Inspector, you can also do this from the Hierarchy by renaming the element "#screen"
    * The "#" is a convention from CSS (web development)
* In StyleSheets (top left of UI Builder), click the "+" and "Add Existing USS" and select "TitleScreenUI.uss"
* When the #screen VisualElement is selected in Hierarchy, go to Inspector and under StyleSheet type in ".screen" in the Style Class List field. Click "Add Style Class to List"
  * This will add ".screen" from TitleScreenUI.uss to "#screen"

![Adding "screen", TitleScreenUI USS, and adding .screen class](/files/-MQsOMMftYowBfxMkfZ3)

* We can see that screen now has the same styling as our .screen class and grows to take up space
* We want to be able to differentiate screens so let's change the background
* Select "#screen" and in the Inspector scroll down to "Background" and change the color to a blueish color (or whatever color you like)
* Next, let's add 3 buttons as children of screen
* Find the Button in the Standard tab under Controls, and drag three of them under #screen in Hierarchy as children
  * Name = host-local-button
    * Text: Host game&#x20;
  * Name = join-local-button
    * Text: Join game
  * Name = manual-connect-button
    * Text: Manually connect

![Setting background color and adding 4 buttons](/files/-MQt2X8MlNJRG3EqP_xH)

* Now we will change the backgrounds and add a button to each of the other views as well
  * We will navigate to one of the 3 other views from TitleScreen using TitleScreen's 3 buttons
  * The other views will have a button back to TitleScreen
* For the other 3 screens we will not add a standard VisualElement as we did for TitleScreen, we will add our cVEs
  * We did not need to add a cVE for TitleScreen to add callbacks to the buttons because we will use the cVE TitleScreenManager
    * If this is still a bit confusing, please bare with us and once we start adding callbacks it should make more sense
    * Hit save!
* Open up HostGameScreen by navigating to the Project tab in Library and clicking the open icon when hovering over it:
  * Hit + under StyleSheets and click "Add Existing USS," select TitleScreenUI USS
  * Drag HostGameScreen from "Custom Controls" in Library into the Hierarchy
  * Add the .screen class to the HostGameScreen cVE (found in the StyleSheet section, type ".screen" and click Add Style Class to List)
  * Name the cVE "HostGameScreen" (by typing in HostGameScreen into the "Name" field in Inspector)

![Adding USS, class, and name to HostGameScreen](/files/-MQt3DeD_AUWfxnIkr_x)

* Next
  * Make the background greenish&#x20;
  * Add a Label (drag one from Controls under Standard tab in Library)
    * Don't name the Label
    * Text = "Host Screen"
  * Add a button
    * Name = "back-button"
    * text = "Back"

![Adding background, label, and button to HostGameScreen](/files/-MQt4oQ8-rnZz9jH1XpN)

* We have our basic (albeit not very pretty) HostGameScreen
  * You might be wondering why didn't we name the label?
    * Or not, but we are going to tell you anyway!
  * In order to reference VisualElements in our custom Visual Elements (cVEs) they must have names to identify them (like an id)
  * If we don't plan on interacting with an element, then there is no real functional need to name them
    * Other than to make the Hierarchy a bit more clear to know what is going on
      * Which we will eventually do in the next section, "Styling a View," but it's too much to do right now
* Next up open JoinGameScreen
  * Add TitleScreenUI USS
  * Drag JoinGameScreen from "Custom Controls" in Library into the Hierarchy
  * Add the .screen class to the JoinGameScreen cVE
  * Name the cVE "JoinGameScreen"

![Setting up JoinGameScreen](/files/-MQt60XXQey0jy_e7gB5)

* Next
  * Make the background orange-ish&#x20;
  * Add a Label
    * Text = "Join Screen"
  * Add a button
    * Name = "back-button"
    * text = "Back"

![Adding a background, label, and button to JoinGameScreen](/files/-MQt9Z7cpmqdEa7cNf3i)

* One more screen to go, ManualConnectScreen
* Open ManualConnectScreen
  * Add TitleScreenUI USS
  * Drag ManualConnectScreen from "Custom Controls" in Library into the Hierarchy
  * Add the .screen class to the ManualConnectScreen cVE
  * Name the cVE "ManualConnectScreen"

![Setting up ManualConnectScreen](/files/-MQtCzFetGYurIvp79W_)

* Next
  * Make the background purplish&#x20;
  * Add a Label
    * Text = "Manual Connect Screen"
  * Add a button
    * Name = "back-button"
    * text = "Back"

![Setting up background and buttons to ManualConnectScreen](/files/-MQtD_CdVh7IVOYH2CWb)

* Now let's navigate to TitleScreenManager

![Navigating to TitleScreenManager and seeing a mess](/files/-MQtF3msDoYHofGotVau)

* What is going on why is it when we click on each of our nested uxmls the highlighted area is so small?
  * We have not added the .screen class to all the uxmls themselves
    * Just the Visual Elements within them
    * Let's fix that
* Add the .screen class to each of the nested uxmls

![Adding the .screen class to all the uxmls](/files/-MQtFibFepLluI_aNI2X)

* Okay so they are not tiny any more, they grow to take the space but now there are 4 equally spaced screens
  * We want to only display one screen at a time
* Click on HostGameScreen in the Hierarchy and in the Inspector navigate to the Display section and change the Display field from "flex" to "none" (you need to hover over the little icons to know which one is "flex" and which one is "none")
* Repeat for JoinGameScreen and ManualConnectScreen

![Changing screen Display values to "none"](/files/-MQtHqAycwH-bChRfffQ)

* We have our single TitleScreen visible with 3 buttons that will take us to the other views
* One more thing to checkout in UI Builder:
  * At the bottom you can pull up "UXML Preview" and "USS Preview" and see that what we created in TitleScreenManager.uxml is being actually saved as code

![Examining UXML Preview and USS Preview](/files/-MQtKEMa2HT3NeMkaYiV)

* This illustrates that UI Builder is almost like an "interpreter" of the uxml and USS we are creating and all the changes we make are saved and reflected in these files
* In the next step we will add callbacks to the buttons we created to change the display values of different screens

{% hint style="success" %}
&#x20; Now now have our NavigationScene UI set up

* We added TitleScreenUI USS to each uxml
* We added a .screen class, background, name, buttons and labels to each view
* We added the .screen class to each uxml
* We updated the Display property in the inspector to "none" of
  * HostGameScreen
  * JoinGameScreen
  * ManualConnectScreen
    {% endhint %}

## Adding callbacks to our UI

Now that we have our screens and buttons set up, we want to add logic to be able to toggle between different views.

The TitleScreenManager cVE will handle adding callbacks to the TitleScreen uxml buttons and the back buttons that return to the uxml. This way we have a single cVE that we know is in charge of displaying views (separation of concerns).

![Our TitleScreenManager uxml/VE approach](/files/-MQrxx8WRSn4iK5EK0PR)

* Close out of UI Builder and open up TitleScreenManager.cs (the cVE)
* We are going to need to declare 4 variables for our 4 views at the top
  * TitleScreen
  * HostGameScreen
  * JoinGameScreen
  * ManualConnectScreen
* Then we are going to have to "query" our nested children by name, and assign them to those variables
* Once we have our views, we will then query within *those* views to find our buttons and assign callbacks
* Finally, we will create functions to hide and display views when different buttons are pressed
* Paste the code snippet below into TitleScreenManager.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class TitleScreenManager : VisualElement
{
    VisualElement m_TitleScreen;
    VisualElement m_HostScreen;
    VisualElement m_JoinScreen;
    VisualElement m_ManualConnectScreen;
    
    public new class UxmlFactory : UxmlFactory<TitleScreenManager, UxmlTraits> { }

    public TitleScreenManager()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        m_TitleScreen = this.Q("TitleScreen");
        m_HostScreen = this.Q("HostGameScreen");
        m_JoinScreen = this.Q("JoinGameScreen");
        m_ManualConnectScreen = this.Q("ManualConnectScreen");

        m_TitleScreen?.Q("host-local-button")?.RegisterCallback<ClickEvent>(ev => EnableHostScreen());
        m_TitleScreen?.Q("join-local-button")?.RegisterCallback<ClickEvent>(ev => EnableJoinScreen());
        m_TitleScreen?.Q("manual-connect-button")?.RegisterCallback<ClickEvent>(ev => EnableManualScreen());

        m_HostScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());
        m_JoinScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());
        m_ManualConnectScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    public void EnableHostScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.Flex;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.None;

    }

    public void EnableJoinScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.Flex;
        m_ManualConnectScreen.style.display = DisplayStyle.None;
    }

    public void EnableManualScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.Flex;
    }

    public void EnableTitleScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.Flex;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.None;
    }

}

```

* Now hit play and check out the navigation
  * There may be console warnings `Internal: deleting an allocation that is older than its permitted lifetime of 4 frames (age = 5)`if the frame rate is very fast

![Our TitleScreenManager working and allowing us to switch between views](/files/-MQt_2mUtkNp6CAyq7nK)

{% hint style="success" %}
&#x20;We now have our TitleScreenManager functioning

* We updated our TitleScreenManager custom Visual Element to provide callbacks and functions when  but it is harmestsclicked
  {% endhint %}

**Github branch link:**&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Creating-a-ScreenManager'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Style a View

Code and workflows to style the Project title screen with UI Builder

## What you'll develop on this page

![Functional TitleScreenUI in NavigationScene](/files/-MR2gdnu1QB97R9flDAy)

How to style the title screen from this Project using UI Builder and have it be mobile-friendly.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Styling-a-View>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Styling Title Screen

This section covers a bit of css (web development). If you are familiar with front-end web development then this probably isn't going to be very helpful. If you are *not* familiar with css, then there are far better resources online to teach good css styling approaches than this gitbook. We highly recommend that you check out [Traversy Media's CSS Crash Course For Absolute Beginners](https://www.youtube.com/watch?v=yfoY53QXEnI) if you are just getting started. We include a bit of styling in this gitbook for completeness, but there are far superior resources.

We will style the TitleScreen in this section by creating and adding selectors to our TitleScreenUI USS, and at the end of this page we will provide code for the final TitleScreenManager, TitleScreen, HostGameScreen, JoinGameScreen, ManualConnectScreen uxmls/cVEs as well as the final TitleScreenUI USS.

We won't show instructions for all the views but after seeing how styling works on 1 screen it should not be difficult to put the pieces together If you have questions on any of the other views reach out on Moetsi Discord.

* Open up the UI Builder and open up the TitleScreenManager uxml
* Currently we have .screen as a selector applied to most parent views
* We want our backgrounds to be white, so we are going to update that selector to be white as well
* Select the .screen selector from StyleSheets and set the background equal to #FFFFFF (white)

![Setting .screen background to white and still seeing colored backgrounds](/files/-MQvl0RfsZ6cLx8ac9fG)

* What gives, why isn't the backgrounds white?!
  * We initially set the .screen selector, but then the specific inline backgrounds we gave each of our views *overrides* the initial selector assignment
* Let's remove all the inline backgrounds for all the views
  * TitleScreen
  * HostGameScreen
  * JoinGameScreen
  * ManualConnectScreen
* To do this, for each of the views select the uxml from Project in Library, then select the cVE in Hierarchy, then navigate to Background in Inspector and right-click on the "Color" property and click "unset"
* Make sure to save each screen, then return to TitleScreenManager

![Unsetting background color values for all views](/files/-MQvl_ZLVqPLpQtAb_aS)

* Click "preview" and navigate the screens to make sure all the colors have been updated

![Hitting preview and ensuring our colors have been fixed](/files/-MQvmFufRWGp87LqkzYv)

* Now we are ready to style TitleScreen, open up the uxml
* Delete the 3 buttons
  * We will reimplement them later but currently they are in the way

![Deleting the 3 buttons from TitleScreen](/files/-MQvmdvtQKzhXk6-QG6q)

* The way we are going to separate this view is by "header" and "main content"
* We want the "header" to always be at the top of the view and always be a certain height
* "main content" will be right below the header and be a max width (550 px) and shrink if the resolution is more narrow
  * This way we can accommodate narrow views but also not make our buttons and content super wide if we are on desktop

{% hint style="info" %}
We will be extracting most of our styles to TitleScreenUI USS. This is because a lot of our styles are shared amount screens. Take a look at the flow diagram below to better understand this.

Going through and styling our first view will feel a bit tedious, but once we have our style sheet and components set up it is much simpler to set up other screens. We literally copy and paste Hierarchy components into other views.
{% endhint %}

![Flow of UI to get a feel of how we will be styling components](/files/-MQyOSq-h3vPVuY329xn)

* Let's get started
* Drag two VisualElements (VEs) into "screen" in the Hierarchy
* Name the top one "header" and the bottom one "main-content"

![Adding header and main-content](/files/-MQwNHlraHHHpXTAVCCa)

### Styling the header

* First we will style header
  * Position
    * Position = Absolute
    * Top = 0
  * Flex
    * Grow = 1
    * Direction = row
  * Align
    * Align Items = center
    * Justify Content = space between
  * Size
    * Width = 100%
    * Height = 108px

![Adding styling to our header](/files/-MQy2XrFVHbLAd0ForJR)

* Now let's extract these styles and make it a new selector called ".header"
  * To do this type in ".header" under Style Class List under StyleSheet in Inspector and then click "Extract Inlined Styles to New Class"

![Extracting styles into ".header"](/files/-MQy4SiTaiVFGJw-x_pM)

* You might have noticed that we did "space between" for the Justify Content
  * That will allows us to keep both our logo and Quit button "hugged" to the sides of the screen regardless of its size
* Now within header let's add 2 standard elements
  * button
  * VisualElement
* Place the VisualElement above the button in the Hierarchy
  * The order doesn't *really* matter, but this order is the format we like to use at Moetsi

![Adding VisualElement and button to header](/files/-MQyApox6tU3YmhxpRcu)

* Name the VisualElement "#moetsi-logo"
  * You could name this Visual Element whatever you want, of course. We do not refer to it anywhere by name so the name doesn't matter (we are just describing what we put there)
* Now let's style the moetsi-logo in the Inspector
  * Flex
    * Grow = 1
  * Size
    * Width
      * Max = 200px
    * Height = 68px
  * Margin
    * Left = 10px
  * Scale Mode (in Background) = scale-to-fit

![Styling moetsi-logo](/files/-MQyIlCz94IM0T3HLdYW)

* Now import an image you would like to use, in our case we imported our logo
* Drag the image into the Assets/UI folder

{% file src="/files/-MR2afpgcL3lg\_6sL9mE" %}
Moetsi Logo
{% endfile %}

* In UI Builder go to Background and select the image to set it as the Background

{% hint style="info" %}
We can also add an SVG

We chose not to include the SVG in the main gitbook flow because the SVG package currently causes some visual artifacts and we think that png is for now still the way to  go.

<img src="/files/-MR5qH3ov4oJJk6TVYtH" alt="" data-size="original">vector

vs.

![](/files/-MR5qZ2acVQ5uAryFHWT)png

But just in case you'd like to know, these the steps to use a vector file as a Background image:

* Import your SVG into Assets/UI
* Select the SVG and checkout the top of the Inspector
* In the bottom dropdown "Generated Asset Type" select "UI Toolkit Vector Image"

![](/files/-MR5rTbxXJxr1RbfNFN4)

* Click "apply"
* Now in UI Builder you can change the background dropdown from "Texture" to "Vector"![](/files/-MR5roEXIpY21ENWzmRr)
* Select your SVG and you're all set!
  {% endhint %}

![Adding the image to moetsi-logo](/files/-MQyJcKnfqK_rT1Fwpfp)

* Great, now let's extract the selector (by typing .logo into the "Style Class List" field under StyleSheet in the Inspector)

![Extracting the inline styles to a new selector ".logo"](/files/-MQyJaoU_N1xwrqbYaGw)

* Now let's move onto our quit button
* Select the Button under #header within TitleScreen.uxml in Hierarchy
* Name the button "#quit-button"
* In the Inspector update the styling
  * Button
    * Text = Quit
  * Size
    * Width = 120px
    * Height = 68px

![Naming quit-button, changing text, and styling](/files/-MQySZSFcHc2GjNSpGU1)

* Styling continued
  * Margin
    * All = 0px (to set to 0)
    * Right = 10px
  * Padding
    * All = 0px
  * Text
    * Size = 24
    * Color = #00000
  * Background
    * Color = Alpha = 0 (make it see through)
  * Border
    * Color = #000000
    * Width = 3px
    * Radius = 10px

![Styling quit-button](/files/-MQySmurWK9s8NDMfG9p)

![Finishing styling quit-button](/files/-MQySs8EIfZZPDJPthaz)

* Not necessary but we will be creating a folder UI/Fonts for Moetsi typography and assigning our button the font

{% file src="/files/-MR2bEkrno3DGRz7efRm" %}
Moetsi text font
{% endfile %}

![Adding fonts and assigning the quit-button text to the added font](/files/-MQyVW6Z0OYuhzaNmWGH)

* Now let's select quit-button and extract our inline selector

  &#x20;and name it ".quit-button"

  * To do this: select quit-button in Hierarchy, then type ".quit-button" into the Style Class List field under StyleSheet then click "Extract Inlined Styles to New Class"

![Extracting our .quit-button inline selector](/files/-MQyWPiE1pXq6Fx1h0zV)

* Next, in StyleSheets, click into the input field that reads "Add new selector..."
* Type in ".quit-button:hover" and press enter
* Next, type in ".quit-button:active" and press enter

![Adding :hover and :active states](/files/-MQyWetgYZHDk1xYdAOT)

* These selectors will change the styling of our button when the user "hovers" or clicks ("active") on the button
* Select .quit-button:hover and configure in the Inspector
  * Border
    * Color = #000000
    * Width = 5px
    * Radius = 9px

![Updating quit-button hover stylings](/files/-MQyY3znbmGk-60CAlHZ)

* Select .quit-button:active and configure in the Inspector
  * Text
    * Color = #FFFFFF
  * Background
    * Color = #000000 (put the alpha to 100%)
* Save and go to the editor, hit play, and interact with the button

![Updating quit-button active stylings](/files/-MQy_a01nCB-0tHF4iAI)

* Great we have our header fully styled

### Styling main content

* Open UI Builder back up
* Let's first start by adding the items we will be styling in main-content
* Go to Library > Standard tab and drag these into main-content as children:
  * Label (name = "title")
  * VisualElement (name = "join-local-title")
  * VisualElement (name = "joining-local-game-view")
  * Button (name = "manual-connect")
  * Label (name = "or")
  * Button (name = "host-local-game")

![Adding our children elements to main-content](/files/-MQycRFRiNqDdV8kbyRl)

* Currently main-content is overlapping the header because the header is positioned absolute, so let's style main-content:
  * Position
    * Position = Absolute
    * Top = 108
  * Align
    * Align Items = center
  * Size
    * Width = 100% (find the % by clicking the button to the right of the text field)
      * Width Max = 550px
  * Margin & Padding
    * Padding
      * Left = 20
      * Right = 20

![main-content styled](/files/-MQyf4cLVBilAMIXGSlC)

* Extract the inline styles to make a new selector .main-content
* Next, let's style #title (within #main-content)
  * Label
    * Text = 3D XR Asteroids
  * Margin & Padding
    * Top = 100px
  * Font
    * Font = (change if you like)
    * Size = 64

{% file src="/files/-MR2bWX048\_1QKn2Lj9c" %}
Moetsi title font
{% endfile %}

![Styled title](/files/-MQyhkiq7XRZGLqL2B-J)

* Extract the inline styles and make selector ".title"
* Next, let's style #join-local-title
  * Size
    * Width = 100% (find the % by clicking the button to the right of the text field)
  * Margin & Padding
    * Top = 100px

![join-local-title styled](/files/-MQyhodXDrtpHnHNWX8r)

* Extract the inline styles to make a new selector .section-title-container
* Drag a label into join-local-title and name it "#join-a-local-game"
  * Label
    * Text = Join A Local Game&#x20;
  * Margin & Padding
    * Padding = 0px (all)
  * Font
    * Size = 36

![join-a-local-game styled](/files/-MQyjInb2ZxZ3V5f3aWx)

* Extract the inline styles to make a new selector .section-title
* If you run into issues with the canvas (like in the image above) expand the size of the canvas in the TitleScreen uxml
* Next, let's style the joining-local-game-view
  * Change the name to "local-games-list-container" in the Name field in Inspector
  * Align
    * Align Items = center
    * Justify Content = center
  * Size
    * Width = 100%
    * Height = 200px
  * Border
    * Color = #000000
    * Width = 5px
    * Radius = 10px

![local-games-list-container styled](/files/-MQyo32FzJL1COEFEYwC)

* Drag a standard ListView element into local-games-list-container (we will make this functional in the next section "Creating a List")&#x20;
  * ListView
    * Name = local-games-list
    * Item Height = 100
    * Size
      * Width = 100%
      * Height = 100%

![local-games-list styled](/files/-MQypXTZArnzEgMRd6b3)

* Now let's style manual-connect
  * First, add a Button
    * Text = Connect to a Local Game
  * Now back to manual-connect:
  * Flex
    * Wrap = wrap
  * Size
    * Width = 100%
    * Height = 68px
  * Margin & Padding
    * Margin
      * All = 0px (to clear out)
      * Top = 21px
    * Padding = 0px (all)
  * Font
    * Size =24
    * Color = #96BFD0
    * Wrap = normal
  * Background
    * Color = 0 Alpha (see through)
  * Border
    * Color = #96BFD0
    * Width = 5px
    * Radius = 10px

![manual-connect styled](/files/-MQysmBd99cPd0_Hw3qD)

* Extract the inline styling and make a new selector named ".blue-button"
* Now go back to StyleSheets and add 2 new selectors by typing in the below into the "Add new selector..." field (press enter between each selector):
  * .blue-button:hover
  * .blue-button:active
* Click on .blue-button:hover to style it:
  * Border
    * Width = 7px (all)
    * Radius = 9px (all)

![blue-button:hover styled](/files/-MQytfPPzSlaONnfNEe5)

* Now style .blue-button:active like so:
  * Text
    * Color = #FFFFFF
  * Background
    * Color = #96BFD0

![blue-button:active styled](/files/-MQyttumKtw5E_n5lEF5)

* Next, let's style "or" (within #manual-connect in Hierarchy)
  * Label
    * Text = Or
  * Margin & Padding
    * Margin
      * Top = 20px
    * Padding = 0px (all)
  * Text
    * Size = 36

![](/files/-MQywDioHXh5ipdDGPRw)

* Next, let's style #host-local-game:
  * Button
    * Text = Host A Local Game
  * Flex
    * Wrap = wrap
  * Size
    * Width = 100%
    * Height = 120px
  * Margin & Padding
    * Margin
      * All = 0 px (to reset)
      * Top = 36px&#x20;
    * Padding  = 0px (all)
  * Text
    * Size = 36
    * Color = #A0C272
  * Background
    * Color = 0 alpha (see through)
  * Border
    * Color = #A0C272
    * Width = 5px
    * Radius = 10px

![host-local-game styled](/files/-MQyx3EDhKkMPWb23NcY)

* Extract the inline styles into a new selector .green-button

* Go to StyleSheets and add 2 new selectors (by typing in the name below into "Add new selector..." and hitting enter)
  * .green-button:hover
  * .green-button:active

* .green-button:hover
  * Border
    * Width = 7px
    * Radius = 9px

![.green-button:hover styled](/files/-MQyyZNrlo42oqZRSdZs)

* .green-button:active
  * Font
    * Color = #FFFFFF
  * Background
    * Color = #A0C272

![.green-button-active styled](/files/-MQyzGg8kR5bdAX-3X1P)

* Now update the #screen Visual Element (not the selector)
  * Align
    * Align Items = center
    * No pic here, we believe in you 💪
* Play around with the size of the canvas to see how the different elements resize for different screen sizes
* Navigate to the editor, hit play, and hover and click on the buttons

![Hitting Play in Editor and reviewing TitleScreen](/files/-MQz1T5uKD9l0sJCycow)

{% hint style="success" %}
We now have our styled TitleScreen uxml

* We added Visual Elements
* We styled the Visual Elements and made selectors from the inline styles
* For the buttons we made additional selectors for hover and active states
  {% endhint %}

## **All the code you need for the uxml and USS in this project**

In terms of learning new things, there isn't much to gain by styling even *more* screens together here in this section. Instead, we provide you with the code snippets to paste into your uxml and cVE files and the TitleScreenUI USS below.

You will notice that the first child in the updated uxmls for TitleScreen, HostGameScreen, JoinGameScreen, and ManualConnectScreen is a "ScrollView" Visual Element. This Visual Element exists because Unity does not currently support "overflow" (where the view automatically becomes scrollable if the contents of the UI are too big for a display). The implication of not supporting overflow is that small screen sizes "cut off" UI. So to account for all screen sizes and avoid this, we wrap all of our views in a ScrollView so that if your resolution is too small to fit all UI content, we will be able to scroll to navigate it all.&#x20;

Why add ScrollView now, so late in the section? That's because having ScrollView in the Hierarchy makes it a little harder to navigate the Hierarchy in UI Builder, so that's why we do the ScrollView wrap step last.

* Paste the code snippet below into your TitleScreenUI.uss file:

```
.screen {
    flex-grow: 1;
    font-size: 20px;
    align-items: stretch;
    background-color: rgb(255, 255, 255);
}

.quit-button {
    margin-right: 10px;
    margin-left: 0;
    width: 120px;
    height: 68px;
    font-size: 24px;
    color: rgb(0, 0, 0);
    background-color: rgba(0, 0, 0, 0);
    border-left-color: rgb(0, 0, 0);
    border-right-color: rgb(0, 0, 0);
    border-top-color: rgb(0, 0, 0);
    border-bottom-color: rgb(0, 0, 0);
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
}

.quit-button:hover {
    background-color: rgba(0, 0, 0, 0);
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}

.quit-button:active {
    background-color: rgb(0, 0, 0);
    color: rgb(255, 255, 255);
}

.main-menu-button {
    width: 219px;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
}

.header {
    height: 108px;
    position: absolute;
    flex-direction: row;
    justify-content: space-between;
    flex-grow: 1;
    top: 0;
    width: 100%;
    align-items: center;
}

.main-content {
    position: absolute;
    top: 108px;
    left: auto;
    right: auto;
    bottom: auto;
    max-width: 550px;
    width: 100%;
    height: auto;
    align-items: center;
    padding-left: 20px;
    padding-right: 20px;
    -unity-font: url('/Assets/UI/Fonts/HV.ttf');
    color: rgb(0, 0, 0);
}

.title {
    font-size: 64px;
    margin-top: 100px;
    -unity-font: url('/Assets/UI/Fonts/Cervo.otf');
    color: rgb(0, 0, 0);
}

.section-title-container {
    width: 100%;
    margin-top: 100px;
}

.section-title {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 36px;
    color: rgb(0, 0, 0);
}

.blue-button {
    width: 100%;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 0;
    margin-right: 0;
    margin-top: 21px;
    margin-bottom: 0;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    height: 68px;
    border-left-color: rgb(150, 191, 208);
    border-right-color: rgb(150, 191, 208);
    border-top-color: rgb(150, 191, 208);
    border-bottom-color: rgb(150, 191, 208);
    background-color: rgba(0, 0, 0, 0);
    font-size: 24px;
    color: rgb(150, 191, 208);
    flex-wrap: wrap;
    white-space: normal;
}

.blue-button:hover {
    border-left-width: 7px;
    border-right-width: 7px;
    border-top-width: 7px;
    border-bottom-width: 7px;
    background-color: rgba(0, 0, 0, 0);
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
}

.blue-button:active {
    background-color: rgb(150, 191, 208);
    color: rgb(255, 255, 255);
}

.green-button {
    left: auto;
    right: auto;
    margin-left: 0;
    margin-right: 0;
    margin-top: 36px;
    margin-bottom: 0;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    width: 100%;
    height: 120px;
    color: rgb(160, 194, 114);
    font-size: 36px;
    background-color: rgba(0, 0, 0, 0);
    border-left-color: rgb(160, 194, 114);
    border-right-color: rgb(160, 194, 114);
    border-top-color: rgb(160, 194, 114);
    border-bottom-color: rgb(160, 194, 114);
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    flex-wrap: wrap;
    white-space: normal;
}

.green-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 7px;
    border-right-width: 7px;
    border-top-width: 7px;
    border-bottom-width: 7px;
    background-color: rgba(0, 0, 0, 0);
}

.green-button:active {
    background-color: rgb(160, 194, 114);
    color: rgb(255, 255, 255);
}

.data-section {
    width: 100%;
    background-color: rgb(255, 255, 255);
}

.data-section-input {
    flex-direction: column-reverse;
    height: 49px;
    margin-left: 0;
    margin-right: 0;
    margin-top: 36px;
    margin-bottom: 0;
    font-size: 36px;
    color: rgb(160, 194, 114);
    border-bottom-width: 4px;
    border-bottom-color: rgb(160, 194, 114);
    border-top-color: rgb(160, 194, 114);
    border-left-color: rgb(160, 194, 114);
    border-right-color: rgb(160, 194, 114);
    width: auto;
    background-color: rgba(0, 0, 0, 0);
}

.data-section-label {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    color: rgb(160, 194, 114);
}

.unity-base-field {
}

.screen-scroll-container {
    flex-grow: 1;
    background-color: rgb(255, 255, 255);
}

.quit-game-button {
    flex-direction: column-reverse;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    background-color: rgba(0, 0, 0, 0);
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    width: 219px;
    height: 68px;
    border-left-color: rgb(255, 255, 255);
    border-right-color: rgb(255, 255, 255);
    border-top-color: rgb(255, 255, 255);
    border-bottom-color: rgb(255, 255, 255);
    font-size: 24px;
    color: rgb(255, 255, 255);
    white-space: normal;
}

.quit-game-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}

.quit-game-button:active {
    background-color: rgb(255, 255, 255);
    color: rgb(0, 0, 0);
}

.logo {
    flex-grow: 1;
    max-width: 200px;
    height: 68px;
    margin-left: 10px;
    background-image: url('/Assets/UI/Moetsi-logo-hq.png');
    -unity-background-scale-mode: scale-to-fit;
}

.local-games-list-container {
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 200px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    border-left-color: rgb(0, 0, 0);
    border-right-color: rgb(0, 0, 0);
    border-top-color: rgb(0, 0, 0);
    border-bottom-color: rgb(0, 0, 0);
}

.local-games-list {
    width: 100%;
    height: 100%;
}

.or {
    color: rgb(0, 0, 0);
    margin-top: 20px;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 36px;
}

.HostGameScreen {
    align-items: center;
}

.JoinGameScreen {
    align-items: center;
}

.ManualConnectScreen {
    align-items: center;
}
```

* Paste the code snippet below into your TitleScreenManager.uxml file:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <ui:Template name="TitleScreen" src="TitleScreen.uxml" />
    <ui:Template name="HostGameScreen" src="HostGameScreen.uxml" />
    <ui:Template name="JoinGameScreen" src="JoinGameScreen.uxml" />
    <ui:Template name="ManualConnectScreen" src="ManualConnectScreen.uxml" />
    <Style src="TitleScreenUI.uss" />
    <TitleScreenManager name="TitleScreenManager" class="screen" style="left: auto; top: auto; position: relative; right: auto; bottom: auto;">
        <ui:Instance template="TitleScreen" name="TitleScreen" class="screen" />
        <ui:Instance template="HostGameScreen" name="HostGameScreen" class="screen" style="display: none;" />
        <ui:Instance template="JoinGameScreen" name="JoinGameScreen" class="screen" style="display: none;" />
        <ui:Instance template="ManualConnectScreen" name="ManualConnectScreen" class="screen" style="display: none;" />
    </TitleScreenManager>
</ui:UXML>

```

* Paste the code snippet below into your TitleScreenManager.cs (cVE) file:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class TitleScreenManager : VisualElement
{
    VisualElement m_TitleScreen;
    VisualElement m_HostScreen;
    VisualElement m_JoinScreen;
    VisualElement m_ManualConnectScreen;
    
    public new class UxmlFactory : UxmlFactory<TitleScreenManager, UxmlTraits> { }

    public TitleScreenManager()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        m_TitleScreen = this.Q("TitleScreen");
        m_HostScreen = this.Q("HostGameScreen");
        m_JoinScreen = this.Q("JoinGameScreen");
        m_ManualConnectScreen = this.Q("ManualConnectScreen");

        m_TitleScreen?.Q("host-local-game")?.RegisterCallback<ClickEvent>(ev => EnableHostScreen());
        m_TitleScreen?.Q("join-local-game")?.RegisterCallback<ClickEvent>(ev => EnableJoinScreen());
        m_TitleScreen?.Q("manual-connect")?.RegisterCallback<ClickEvent>(ev => EnableManualScreen());

        m_HostScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());
        m_JoinScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());
        m_ManualConnectScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    public void EnableHostScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.Flex;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.None;

    }

    public void EnableJoinScreen()
    {
        Debug.Log("Enable join screen trigger");
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.Flex;
        m_ManualConnectScreen.style.display = DisplayStyle.None;
    }

    public void EnableManualScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.Flex;
    }

    public void EnableTitleScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.Flex;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.None;
    }

}

```

* Paste the code snippet below into your TitleScreen.uxml file:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <ui:VisualElement name="screen" class="screen HostGameScreen">
            <ui:VisualElement name="header" class="header">
                <ui:IMGUIContainer name="moetsi-logo" class="logo" />
                <ui:Button text="Quit" name="quit-button" class="quit-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content">
                <ui:Label text="3D XR Asteroids" name="title" class="title" />
                <ui:VisualElement name="join-local-title" class="section-title-container">
                    <ui:Label text="Join A Local Game" name="join-a-local-game" class="section-title" />
                </ui:VisualElement>
                <ui:VisualElement name="local-games-list-container" class="local-games-list-container">
                    <ui:ListView name="local-games-list" item-height="100" class="local-games-list" />
                </ui:VisualElement>
                <ui:Button text="Connect to a Local Game" name="manual-connect" class="button blue-button" />
                <ui:Label text="Or" name="or" class="or" />
                <ui:Button text="Host A Local Game" name="host-local-game" class="button green-button" />
            </ui:VisualElement>
        </ui:VisualElement>
    </ui:ScrollView>
</ui:UXML>

```

* Paste the code snippet below into your HostGameScreen.uxml file:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <HostGameScreen name="HostGameScreen" class="screen HostGameScreen">
            <ui:VisualElement name="header" class="header">
                <ui:Button text="Main Menu" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content" style="top: 108px; left: auto; position: absolute;">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Host a Local Game" display-tooltip-when-elided="True" name="section-title" class="section-title" style="color: rgb(160, 194, 114);" />
                </ui:VisualElement>
                <ui:VisualElement name="game-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="HostNameValue" text="GameName" name="game-name" class="data-section-input" />
                    <ui:Label text="Your Game Name" display-tooltip-when-elided="True" name="game-name-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="HostIPValue" text="127.0.0.1" name="game-ip" readonly="false" class="data-section-input" />
                    <ui:Label text="Your Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="PlayerName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Host Game" display-tooltip-when-elided="True" name="launch-host-game" class="green-button" />
            </ui:VisualElement>
        </HostGameScreen>
    </ui:ScrollView>
</ui:UXML>

```

* Paste the code snippet below into your HostGameScreen.cs file (cVE):

```
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.SceneManagement;

public class HostGameScreen : VisualElement
{
    TextField m_GameName;
    TextField m_GameIp;
    TextField m_PlayerName;
    String m_HostName = "";
    IPAddress m_MyIp;

    public new class UxmlFactory : UxmlFactory<HostGameScreen, UxmlTraits> { }

    public HostGameScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        // 
        // PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
        // 
        m_GameName = this.Q<TextField>("game-name");
        m_GameIp = this.Q<TextField>("game-ip");
        m_PlayerName = this.Q<TextField>("player-name");

        //  CLICKING CALLBACKS
        this.Q("launch-host-game")?.RegisterCallback<ClickEvent>(ev => ClickedHostGame());


        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void ClickedHostGame()
    {

        Debug.Log("clicked host game");
    }

}

```

* Paste the code snippet below into your JoinGameScreen.uxml file:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen">
        <JoinGameScreen name="JoinGameScreen" class="screen JoinGameScreen">
            <ui:VisualElement name="header" class="header" style="height: 108px; top: 0;">
                <ui:Button text="Back Button" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Join A Local Game" display-tooltip-when-elided="True" name="section-title" class="section-title" />
                </ui:VisualElement>
                <ui:VisualElement name="game-name-container" class="data-section">
                    <ui:Label text="ThereHostName" display-tooltip-when-elided="True" name="game-name" class="data-section-input" style="border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 0;" />
                    <ui:Label text="Game Name" display-tooltip-when-elided="True" name="game-name-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:Label text="192.168.156" display-tooltip-when-elided="True" name="game-ip" class="data-section-input" style="border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 0;" />
                    <ui:Label text="Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="YourHostName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Join Game" display-tooltip-when-elided="True" name="launch-join-game" class="blue-button" style="height: 120px;" />
            </ui:VisualElement>
        </JoinGameScreen>
    </ui:ScrollView>
</ui:UXML>

```

* Paste the code snippet below into your JoinGameScreen.cs file (cVE):

```
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.SceneManagement;

public class JoinGameScreen : VisualElement
{
    Label m_GameName;
    Label m_GameIp;
    TextField m_PlayerName;
    String m_HostName = "";
    IPAddress m_MyIp;

    public new class UxmlFactory : UxmlFactory<JoinGameScreen, UxmlTraits> { }

    public JoinGameScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        // 
        // PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
        // 
        m_GameName = this.Q<Label>("game-name");
        m_GameIp = this.Q<Label>("game-ip");
        m_PlayerName = this.Q<TextField>("player-name");

        //  CLICKING CALLBACKS
        this.Q("launch-host-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());



        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void ClickedJoinGame()
    {
        Debug.Log("clicked client game");
    }


}
```

* Paste the code snippet below into your ManualConnectScreen.uxml file:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <ManualConnectScreen name="ManualConnectScreen" class="screen ManualConnectScreen">
            <ui:VisualElement name="header" class="header">
                <ui:Button text="Back Button" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Manually Connect" display-tooltip-when-elided="True" name="section-title" class="section-title" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="HostIPValue" text="127.0.0.1" name="game-ip" readonly="false" class="data-section-input" style="background-color: rgb(255, 255, 255);" />
                    <ui:Label text="Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="PlayerName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Join Game" display-tooltip-when-elided="True" name="launch-connect-game" class="blue-button" style="height: 120px;" />
            </ui:VisualElement>
        </ManualConnectScreen>
    </ui:ScrollView>
</ui:UXML>

```

* Paste the code snippet below into your ManualConnectScreen.cs file (cVE):

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

public class ManualConnectScreen : VisualElement
{
    TextField m_GameIp;
    TextField m_PlayerName;
    string m_HostName = "";
    IPAddress m_MyIp;

    public new class UxmlFactory : UxmlFactory<ManualConnectScreen, UxmlTraits> { }

    public ManualConnectScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        // 
        // PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
        // 
        m_GameIp = this.Q<TextField>("game-ip");
        m_PlayerName = this.Q<TextField>("player-name");

        //  CLICKING CALLBACKS
        this.Q("launch-connect-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void ClickedJoinGame()
    {
        Debug.Log("clicked manual connect");
    }

}

```

* Make sure to save all of those files you just updated
  * If you chose not to use the provided Moetsi font and Moetsi logo from earlier in this section, you will need to go back and update those sections with your different fonts and images, or else you will get errors

![Updating UI files pt1](/files/-MR2fl4FDZzG1DC6LM5B)

![Updating UI files pt2](/files/-MR2fqDVj71Xnl_Atpqj)

{% hint style="warning" %}
In our testing nearly every time after this update you must quit Unity and reopen the project to have it update.
{% endhint %}

* Hit play and check out navigation between the screens

![](/files/-MR2g0aWINpgposMS3gt)

{% hint style="success" %}
We now have our styled TitleScreenManager
{% endhint %}

**Github branch link:**&#x20;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`cd Unity-DOTS-Multiplayer-XR-Sample`\
`git checkout 'Styling-a-View'`

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Create a ListView

Code and workflows for creating a dynamic UI ListView in UI Builder

## What you'll develop on this page

![Dynamic ListView that displays the name of GameObjects in a scene](/files/-MR3GNca2Qh6yipAaiVK)

Use UI Builder to create a dynamic UI ListView that displays GameObjects in a scene and responds to which item is clicked.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Creating-a-List>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## What our ListView will do

We are going to build a ListView that displays the number of GameObjects with a "LocalGame" tag and the names of the corresponding GameObjects. When we click on the item in the list view we will be taken to the JoinGameScreen.

Just to be upfront, the approach we take to build this ListView is overkill for the number of GameObjects we will be counting. ListViews are good when all you want to do is bind data to visible items currently in the view and there are hundreds of items. As we scroll through the list, data will bind and appear and then unbind as we scroll past it. Again, this is overkill for just a few items, but we at Moetsi *personally* could not find any other resources published online explaining how to do this from scratch, so that just leaves us to do it!!!&#x20;

We will create a new uxml file named "ListItem" that we will add and remove in the ListView. This is called ["Instancing UI Documents as Templates" by Unity in their documentation](https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/uib-structuring-ui-templates.html). Next, we will make a ne                                                                                         w script named "LocalGamesFinder" that will power this new ListView. The reason it is called LocalGamesFinder is because we will be using it to find LAN games in the next Multiplayer section.

There are 3 spells you must cast to conjure ListView:

* itemsSource (declares the data source of the items)
* makeItem (creates the list item visual element from our uxml)
* bindItem (attach the data from our itemsSource to our item)

When you update the data in itemsSource you can call Refresh() to update the list.&#x20;

We do this differently in the next Multiplayer section, where we'll be listening for game broadcasts, and as new ones are found, we update our list.

In this page we will look for GameObjects in our scene and refresh our itemsSource 1x per second. This is not an optimal approach because it refreshes even when no items have changed, but it is fine to simply demonstrate the purposes of ListView.

## Building our ListView

### Creating ListItem uxml

Since we've already explained how to create and style uxmls using UI Builder on the previous page ("Styling a View"), we're going to just jump straight to the code here.

* Create a new uxml file named ListItem (right-click in the UI folder, select Create, then UI Toolkit, and select UI Document)
* &#x20;and paste in the code snippet below:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:VisualElement name="row" focusable="false" class="row">
        <ui:VisualElement name="game-name-data" class="game-name-data">
            <ui:Label text="Mary&apos;s Game" name="game-name" class="list-item-game-name" />
            <ui:Label text="Game Name" name="game-name-label" class="list-item-game-name-label" />
        </ui:VisualElement>
        <ui:Button text="Join" display-tooltip-when-elided="True" name="join-local-game" class="blue-button list-item-button" />
    </ui:VisualElement>
</ui:UXML>
```

* Add the following classes at the bottom of TitleScreenUI USS (don't paste this snippet in over all your code!! Just add it to the bottom!)

```
.row {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    height: 74px;
}

.game-name-data {
    margin-left: 17px;
}

.list-item-game-name {
    margin-left: 0;
    font-size: 24px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    color: rgb(150, 191, 208);
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
}

.list-item-game-name-label {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 10px;
}

.list-item-button {
    width: 141px;
    height: 46px;
    margin-left: 0;
    margin-right: 12px;
    margin-top: 0;
    margin-bottom: 0;
    border-left-color: rgba(0, 0, 0, 0);
    border-right-color: rgba(0, 0, 0, 0);
    border-top-color: rgba(0, 0, 0, 0);
    border-bottom-color: rgba(0, 0, 0, 0);
}
```

![Creating the ListItem uxml file and then adding more classes to TitleScreenUI](/files/-MR2x4p6AQl3qp7VjTVQ)

* We will be taking the names of found GameObjects with "LocalGame" tags and inserting them in the label #game-name from the uxml
* When we click on #join-local-game we will be taken to the local games view

### Creating LocalGamesFinder

* In NavigationScene, create an empty GameObject in the Hierarchy named "LocalGamesDiscovery"
* Add component to LocalGamesDiscovery that is a new script named "LocalGamesFinder"
  * First make the file by right-clicking in the Scripts and Prefabs folder > Create > C# Script and name the file "LocalGamesFinder"
  * Then Navigate to LocalGamesDiscovery in Hierarchy and click Add Component in Inspector to add LocalGamesFinder

![Creating LocalGamesDiscovery and LocalGamesFinder](/files/-MR2yCbQ3iIUDgrc5U_d)

* Paste the code snippet below into LocalGamesFinder.cs:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor;

public class LocalGamesFinder : MonoBehaviour
{
    //We will be pulling in our SourceAsset from TitleScreenUI GameObject so we can reference Visual Elements
    public UIDocument m_TitleUIDocument;

    //When we grab the rootVisualElement of our UIDocument we will be able to query the TitleScreenManager Visual Element
    private VisualElement m_titleScreenManagerVE;

    //We will query for our TitleScreenManager cVE by its name "TitleScreenManager"
    private TitleScreenManager m_titleScreenManagerClass;

    //Within TitleScreenManager (which is everything) we will query for our list-view by name
    //We don't have to query for the TitleScreen THEN list-view because it is one big tree of elements
    //We can call any child from the parent, very convenient! But you must be mindful about being dilligent about
    //creating unique names or else you can get back several elements (which at times is the point of sharing a name)
    private ListView m_ListView;

    //Although this variable name doesn't make sense for this use case it will in the Multiplayer section
    //Here we will store our discovered GameObjects
    //This will be used as our itemSource
    private GameObject[] discoveredServerInfoObjects;

    //This is our ListItem uxml that we will drag to the public field
    //We need a reference to the uxml so we can build it in makeItem
    public VisualTreeAsset m_localGameListItemAsset;

    //These variables are used in Update() to pace how often we check for GameObjects
    public float perSecond = 1.0f;
    private float nextTime = 0; 

    void OnEnable()
    {
        //Here we grab the SourceAsset rootVisualElement
        //This is a MAJOR KEY, really couldn't find this key step in information online
        //If you want to reference your active UI in a script make a public UIDocument variable and 
        //then call rootVisualElement on it, from there you can query the Visual Element tree by names
        //or element types
        m_titleScreenManagerVE = m_TitleUIDocument.rootVisualElement;
        //Here we grab the TitleScreenManager by querying by name
        m_titleScreenManagerClass = m_titleScreenManagerVE.Q<TitleScreenManager>("TitleScreenManager");
        //From within TitleScreenManager we query local-games-list by name
        m_ListView = m_titleScreenManagerVE.Q<ListView>("local-games-list");

    }

    // Start is called before the first frame update
    void Start()
    {
        //We start by looking for any GameObjects with a LocalGame tag
        discoveredServerInfoObjects = GameObject.FindGameObjectsWithTag("LocalGame");
        
        
        // The three spells you must cast to conjure a list view
        m_ListView.makeItem = MakeItem;
        m_ListView.bindItem = BindItem;
        m_ListView.itemsSource = discoveredServerInfoObjects;

    }

    private VisualElement MakeItem()
    {
        //Here we take the uxml and make a VisualElement
        VisualElement listItem = m_localGameListItemAsset.CloneTree();
        return listItem;

    }

    private void BindItem(VisualElement e, int index)
    {
        //We add the game name to the label of the list item
        e.Q<Label>("game-name").text = discoveredServerInfoObjects[index].name;

        //Here we create a call back for clicking on the list item and provide data to a function
        e.Q<Button>("join-local-game").RegisterCallback<ClickEvent>(ev => ClickedJoinGame(discoveredServerInfoObjects[index]));

    }

    void ClickedJoinGame(GameObject localGame)
    {
        //We query our JoinGameScreen cVE and call a new function LoadJoinScreenForSelectedServer and pass our GameObject
        //This is an example of clicking a list item and passing through data to a new function with that click
        //You will see in our JoinGameScreen cVE that we use this data to fill labels in the view
        m_titleScreenManagerClass.Q<JoinGameScreen>("JoinGameScreen").LoadJoinScreenForSelectedServer(localGame);

        //We then call EnableJoinScreen on our TitleScreenManager cVE (which displays JoinGameScreen)
        m_titleScreenManagerClass.EnableJoinScreen();

    }
  
    // Update is called once per frame
    void Update()
    {
        if (Time.time >= nextTime)
        {   
            //We check for GameObjects with a localGame tag
            discoveredServerInfoObjects = GameObject.FindGameObjectsWithTag("LocalGame");

            //We again set our itemsSource to our array (if the array changes it must be reset)
            m_ListView.itemsSource = discoveredServerInfoObjects;
            //We then must refresh the listView on this new data source
            //(don't worry it doesn't make the list jump, ListView is cool like that)
            m_ListView.Refresh();

            //We increment
            nextTime += (1/perSecond);
        }

    }

}
```

![Updating LocalGamesFinder](/files/-MR3B667mt7jLQhOIsIS)

* Let's also update our JoinGameScreen custom Visual Element (cVE) with the function that our ListItem button (join-local-game) calls, "LoadJoinScreenForSelectedServer"
* To update, paste the code snippet below into JoinGameScreen.cs (cVE):

```
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.SceneManagement;

public class JoinGameScreen : VisualElement
{
    Label m_GameName;
    Label m_GameIp;
    TextField m_PlayerName;
    String m_HostName = "";
    IPAddress m_MyIp;

    public new class UxmlFactory : UxmlFactory<JoinGameScreen, UxmlTraits> { }

    public JoinGameScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        // 
        // PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
        // 
        m_GameName = this.Q<Label>("game-name");
        m_GameIp = this.Q<Label>("game-ip");
        m_PlayerName = this.Q<TextField>("player-name");

        //  CLICKING CALLBACKS
        this.Q("launch-join-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());



        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void ClickedJoinGame()
    {
        Debug.Log("clicked join game");
    }

    public void LoadJoinScreenForSelectedServer(GameObject localGame)
    {
        m_GameName = this.Q<Label>("game-name");
        m_GameIp = this.Q<Label>("game-ip");
        m_GameName.text = localGame.name;
        m_GameIp.text = localGame.name;
    }

}
```

{% hint style="info" %}

#### 🔑MAJOR KEY ALERT🔑

The next few steps we take with LocalGamesFinder will show you a good approach for making scripts that interact with active UI. The general steps to follow are:

* Make a public UI Document variable in your script

* Drag your UI SourceAsset into that field in the Inspector from the scene's Hierarchy

* Call .rootVisualElement on that variable

* You can now query your entire Visual Element tree
  {% endhint %}

* Select LocalGamesDiscovery so LocalGamesFinder is visible in the Inspector
  * Drag the TitleScreenUI **GameObject** from the **Hierarchy** onto the "Title UI Document" field under Local Games Finder in Inspector&#x20;
  * Drag ListItem from the Assets/UI folder in the Project folder to the "Local Game List Item Asset" field

![Setting the public variables on LocalGamesFinder](/files/-MR3Da-NU6Y3Nd1YxrRz)

* In LoadJoinScreenForSelectedServer in JoinGameScreen cVE
  * We query for "game-name" and "game-ip" labels
  * We then set the text value of those labels to the name of our GameObjects
* Delete the Debug.Log in "EnableJoinScreen()" in TitleScreenManager cVE

![Updating JoinGameScreen cVE and ](/files/-MR3BLaNGVQ1s726wJ4C)

* Now let's create an empty GameObject called LocalGame
* Add a "LocalGame" tag to the LocalGame GameObject
  * To do this, you need to click the dropdown next to "Tag" in the Inspector when LocalGame is selected and choose the last option "Add Tag..." and click the + under Tags and type "LocalGame" to make a new tag
* Copy and paste to make 10 copies
* Let's hit play
  * Scroll up and down the list to see all our "LocalGames"
  * Copy and paste and make more GameObjects and see the ListView include those items
  * Click on Join button on one of the ListItems and check out the JoinGameScreen
  * Return back and click on another ListItem to see new data load

![Creating LocalGame tagged GameObjects and seeing our ListView work](/files/-MR3EbWZhAORYaeoPILH)

* Place the LocalGamesFinder script in Assets/Scripts and Prefabs/Multiplayer Setup
  * No gif here, we believe in you 💪

{% hint style="success" %}
We now have our working ListView that dynamically updates and can pass data when an item is clicked

* We created ListItem uxml and added its classes to TitleScreenUI USS
* We created LocalGamesDiscovery
* We created LocalGamesFinder
* **We pulled in the UI GameObject in the scene to a public LocalGamesFinder field to be able to access the UI from a MonoBehaviour (major key!)**
* We updated JoinGameScreen cVE
* We updated TitleScreenManager cVE (barely)
* We created a new tag and GameObjects to replicate finding new local games
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Creating-a-List'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Responsive Game UI

Code and workflow to make a responsive game UI in UI Builder

## What you'll develop on this page

![Game UI overlay responding to click events](/files/-MR6JawfkvSgKWvMUo65)

We will make a Game UI that will respond to click events as well as display game and player data and add it to our Project.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Game-UI-Overlay>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## What will appear in our GameUI

![The game UI overlay we will create in this page](/files/-MR64vMjpbpaG4ByoBp2)

We will make a simple game UI overlay that provides the user the ability to return to NavigationScene as well as see game and player information.

Our GameUI uxml will use TitleScreenUI for its .screen and .header selector stylings. Although this isn't totally necessary because we will actually make a new selector class that modifies both of these selectors (.screen needs to be updated for white text and the header needs to be updated to expand to fit its contents), we decided to still include TitleScreenUI USS  in GameUI uxml just to demonstrate how to use 2 USSs in a single UXML in UI Builder... It just seems like an entire section in a gitbook dedicated to UI should have *at least one* (albeit nominal) example of multiple USS sheets, you know?

We will create a new USS called "GameUI" that will hold GameUI specific stylings.

Our GameUI will have a footer that hugs the bottom of the screen similar to how the header hugs the top of the screen.

The right side of the header will contain game information. Although the game information will be static in this section, we'll make it dynamic in the next Multiplayer section.

The bottom left of the screen will display static instructions on how to navigate the game. If you end up updating the game controls please be sure to update these instructions as well.

The bottom right of the screen will display player-specific information. Although the game information will be static in this section, we'll make it dynamic in the next Multiplayer section..

## Creating our GameUI

* Now that we are ready to start switching between scenes let's give our game scene the respect it deserves and change its name from "SampleScene" to "MainScene" 🏋️‍♂️
* Navigate to Assets/Scenes
  * Right-click SampleScene and rename to MainScene
  * If you have a SampleSceneSettings file in your Project folder, then right-click on SampleSceneSettings and rename it to MainSceneSettings
  * Right-click on SampleScene/ (folder) and rename to MainScene
* Open up our newly respected MainScene **Scene** (not folder) so that MainScene is in Hierarchy

![Updating SampleScene to MainScene](/files/-MR5uy4kQx8hTZwT6pQu)

* Right-click in the Hierarchy and create an empty GameObject named "GameUI"
  * Now there is a GameUI GameObject in the MainScene
  * Add a "Input System Event System (UI Toolkit)" component
  * Add a UI Document component
    * Drag "PanelSettings" from Assets/UI onto Panel Settings
* Right-click in the Assets/UI folder and create a new UI Document named "GameUIManager" (Create > UI Toolkit > UI Document)
  * Drag GameUIManager uxml onto Source Asset field in the UI Document component in Inspector when the GameUI GameObject is selected in Hierarchy
* Create a new script for our GameUIManager custom Visual Element (cVE), "GameUIManager"
  * Right-click in UI > Create > C# Script
* Paste the code snippet below into GameUIManager.cs (cVE):

```
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Networking.Transport;
using Unity.NetCode;

public class GameUIManager : VisualElement
{
    VisualElement m_LeaveArea;

    public new class UxmlFactory : UxmlFactory<GameUIManager, UxmlTraits> { }

    public GameUIManager()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        m_LeaveArea = this.Q("quit-game");

        m_LeaveArea?.RegisterCallback<ClickEvent>(ev => ClickedButton());
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    void  ClickedButton() {

        Debug.Log("Clicked quit game");
    }  
}
```

![Setting up our GameUI](/files/-MR6Hu29c2x2AcjtAaIw)

* Open up UI Builder (Window > UI Toolkit > UI Builder)
* Navigate to the Library section of the left panel, click the Project tab and under UI Documents (UXML) within the Asset/UI folder, hover over the GameUIManager uxml for the open icon on the right to appear, and click the icon to open GameUIManager.uxml in Hierarchy
* In StyleSheets (top left) hit the "+" and
  * Add Existing USS "TitleScreenUI"
    * Sometimes we found in our testing that searching "TitleScreenUI" in Finder's search bar led to no results. If that happens to you just use the old fashion way of finding TitleScreenUI by opening up the Assets folder, then opening up the UI folder, and then scrolling to find TitleScreenUI.uss
  * Create New USS "GameUI" (save it in Assets/UI folder, which you may need to expand Finder in order to do so)
* Find GameUIManager in the Custom Controls (C#) section of Library > Project tab and drag it into Hierarchy
* While #GameUIManager is highlighted in Hierarchy, go to Inspector and type "GameUIManager" into the Name field
* Save the GameUIManager uxml (best to save while GameUIManager.uxml is selected in Hierarchy -- for some reason it's less confusing to UI Builder that way!)

![Setting up GameUI uxml in UI Builder](/files/-MR6I0ns0ewvU4nKbOxZ)

{% hint style="info" %}
You will notice we are following the same "ScreenManager" pattern we used in NavigationScene.

Although we do not switch between views like we do in TitleScreenManager, we want to set up our game UI so it is easy to extend in the future.
{% endhint %}

We have already reviewed how to create and style views in the "Styling a View" section of our gitbook, so for the next few files we provide you with the code rather than re-creating the views piece-by-piece (not a lot to gain from more of the same).&#x20;

* Paste the code snippet below into GameUIManager.uxml:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <Style src="GameUI.uss" />
    <GameUIManager name="GameUIManager" style="width: 100%; height: 100%;">
        <ui:VisualElement name="screen" class="screen game-ui-screen">
            <ui:VisualElement name="header" class="header game-ui-header">
                <ui:Button text="Quit Game" name="quit-game" class="quit-game-button" />
                <ui:VisualElement name="top-right-container" class="top-right-container">
                    <ui:Label text="Frank&apos;s Game" name="game-name" class="top-right-values" />
                    <ui:Label text="GAME NAME" name="game-name-label" class="top-right-labels" />
                    <ui:VisualElement name="spacer" class="spacers" />
                    <ui:Label text="127.0.0.1" name="game-ip" class="top-right-values" />
                    <ui:Label text="IP ADDRESS" name="ip-address-label" class="top-right-labels" />
                    <ui:VisualElement name="spacer" class="spacers" />
                    <ui:Label name="highest-score" class="top-right-values" />
                    <ui:Label text="HIGHEST SCORE" name="highest-score-label" class="top-right-labels" />
                </ui:VisualElement>
            </ui:VisualElement>
            <ui:VisualElement name="footer" class="footer">
                <ui:VisualElement name="bottom-left" class="bottom-left">
                    <ui:Label text="Use &quot;p&quot; key to self-destruct" name="instructions-1" class="instruction-text" />
                    <ui:Label text="Use space bar to fire missles" name="instructions-2" class="instruction-text" />
                    <ui:Label text="Right-click and move mouse to rotate" name="instructions-3" class="instruction-text" />
                    <ui:Label text="Use WASD keys to thrust" name="instructions-4" class="instruction-text" />
                </ui:VisualElement>
                <ui:VisualElement name="bottom-right" style="margin-right: 10px;">
                    <ui:Label text="Jonathan" name="player-name" class="top-right-values" />
                    <ui:Label text="PLAYER NAME" name="game-name-label" class="top-right-labels" />
                    <ui:VisualElement name="spacer" class="spacers" />
                    <ui:Label text="0" name="current-score" class="top-right-values" />
                    <ui:Label text="CURRENT SCORE" name="current-score-label" class="top-right-labels" />
                    <ui:VisualElement name="spacer" class="spacers" />
                    <ui:Label text="0" name="high-score" class="top-right-values" />
                    <ui:Label text="HIGH SCORE" name="current-score-label" class="top-right-labels" />
                </ui:VisualElement>
            </ui:VisualElement>
        </ui:VisualElement>
    </GameUIManager>
</ui:UXML>

```

* Paste the code snippet below into GameUI.uss:

```
.quit-game-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}

.quit-game-button:active {
    background-color: rgb(255, 255, 255);
    color: rgb(0, 0, 0);
}

.quit-game-button {
    flex-direction: column-reverse;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    background-color: rgba(0, 0, 0, 0);
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    height: 68px;
    border-left-color: rgb(255, 255, 255);
    border-right-color: rgb(255, 255, 255);
    border-top-color: rgb(255, 255, 255);
    border-bottom-color: rgb(255, 255, 255);
    font-size: 24px;
    color: rgb(255, 255, 255);
    white-space: normal;
    max-width: 140px;
    width: 100%;
}

.game-ui-screen {
    background-color: rgba(0, 0, 0, 0);
    justify-content: space-between;
    color: rgb(255, 255, 255);
}

.game-ui-header {
    height: auto;
}

.top-right-container {
    margin-right: 10px;
    margin-top: 10px;
}

.top-right-values {
    color: rgb(255, 255, 255);
    -unity-text-align: upper-right;
    font-size: 18px;
}

.top-right-labels {
    -unity-text-align: upper-right;
    color: rgb(255, 255, 255);
    font-size: 10px;
}

.spacers {
    height: 3px;
}

.footer {
    bottom: 0;
    flex-direction: row;
    justify-content: space-between;
    position: absolute;
    flex-grow: 1;
    width: 100%;
    height: auto;
    padding-bottom: 10px;
}

.bottom-left {
    flex-direction: column-reverse;
    margin-left: 10px;
}

.instruction-text {
    color: rgb(255, 255, 255);
    white-space: normal;
    font-size: 14px;
}

```

* Once you've updated the GameUI uxml and Game UI USS with the code above, return to MainScene

![Updating GameUIManager uxml and GameUI USS](/files/-MR6I9MGiEjp8nYnqDh1)

* Hit "play" in MainScene and checkout the GameUI in action
  * You can click on the "Quit Game" button which will trigger a console log

![Out GameUI working](/files/-MR6JVlg9tOlDUF4mBo4)

{% hint style="success" %}
We've now built responsive GameUI

* We renamed our SampleScene to MainScene
* We created our GameUI GameObject
  * We added Event System (UI Toolkit)
  * We added UI Document
* We created GameUIManager uxml and placed it in UI Document's "Source Asset"
* We dragged our Panel Settings to UI Document's "Panel Settings"
* We created GameUIManager uxml and c VE
* We added TitleScreenUI and new GameUI as USS to GameUIManager
* We updated GameUIManager uxml and GameUI USS
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Game-UI-Overlay'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Navigate Between Scenes Using ClientServerBootstrap

Code and workflows to navigate between NavigationScene and MainScene using UI buttons

## What will be developed on this page

![Navigating between scenes triggered by UI Toolkit elements and creating client/server worlds appropriately](/files/-MR6m6LwmsEvtJkIiXsS)

We will adding logic to navigate between NavigationScene and MainScene and properly handle creating and destroying ECS worlds.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Navigating-Between-Scenes>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Navigating from NavigationScene to MainScene

We have 3 views that could take us to MainScene:

* HostGameScreen
* JoinGameScreen
* ManualConnectScreen

![Asteroid NavigationScene view flow diagram](/files/-MQyOalNZixdjhh6i0V6)

Rather than have separate logic in each of their associated custom Visual Elements (cVEs), we are going to create a new script that handles all transitions from NavigationScene to MainScene, named "ClientServerLauncher." We will pull in references to each of these views and will add callbacks to the "Host Game" and "Join Game" buttons that will trigger the transition. Like most software engineering, this is an opiniated decision for where to put the "brains" for transition. We have gotten feedback that we maybe we have abstracted *too* much, and made it more confusing which is a fair critique. We wanted to show all the different ways elements can interact in this UI section.

Later on in the next Multiplayer section we will add additional logic to ClientServerLauncher so that we can select which IP address we connect to as a client.

In this page we will implement ClientServerBootstrap to prevent triggering Client/Server world creation until we are ready to transition to MainScene and click one of our transition buttons.

Why? Well right now in the NavigationScene DOTS NetCode bootstrap does its thing and creates server/client worlds. We want to create a build where a user can "host" (which means be both client and server) or "join" (which means they are just a client. So we cannot make the decisions whether to build a server world in the NavigationScene, that decision should be made in MainScene (once we know what decision has been made).

> ### Bootstrap
>
> he default bootstrap creates client server Worlds automatically at startup. It populates them with the systems defined in the attributes you have set. This is useful when you are working in the Editor, but in a standalone game, you might want to delay the World creation so you can use the same executable as both a client and server.
>
> To do this, you can create a class that extends `ClientServerBootstrap` to override the default bootstrap. Implement `Initialize` and create the default World. To create the client and server worlds manually, call `ClientServerBootstrap.CreateClientWorld(defaultWorld, "WorldName");` or `ClientServerBootstrap.CreateServerWorld(defaultWorld, "WorldName");`.
>
> The following code example shows how to override the default bootstrap to prevent automatic creation of the client server worlds:
>
> ```
> public class ExampleBootstrap : ClientServerBootstrap
> {
>     public override bool Initialize(string defaultWorldName)
>     {
>         var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
>         GenerateSystemLists(systems);
>
>         var world = new World(defaultWorldName);
>         World.DefaultGameObjectInjectionWorld = world;
>
>         DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, ExplicitDefaultWorldSystems);
>         ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world);
>         return true;
>     }
>
> }
> ```
>
> \
> From [NetCode's Bootstrap documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/client-server-worlds.html)

* Currently, because we have the NetCode package installed, NetCode automatically creates Client/Server worlds when the application starts
  * If you don't have the NetCode package installed, please go visit the previous "DOTS NetCode" section for installation instructions
* Click play while NavigationScene is open then navigate to the DOTS Windows and see that both Server and Client worlds are created

![Client and Server worlds automatically being created in NavigationScene](/files/-MR6SKb21iOLmL2VnY_c)

* We want to limit the worlds to DefaultWorld while in NavigationScene
  * This is a preemptive measure we are taking because later on, in the Multiplayer section, we won't know whether we want to join a game as just a client or as a client/server, so we want to hold off on creating these NetCode worlds
  * So we will implement NetCodeBootstrap
  * I know it seems like we have repeated ourselves like 3 times but we have gotten feedback that "World creation delay" was a big confusing so we wanted to hammer the point home!
* Un-click Play and right-click inside the Multiplayer Setup folder, select "Create" to create a new C# script called NetCodeBootstrap
* Paste the code snippet below into NetCodeBootstrap.cs:

```
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;
#if UNITY_EDITOR
using Unity.NetCode.Editor;
#endif

public class NetCodeBootstrap : ClientServerBootstrap
{
    public override bool Initialize(string defaultWorldName)
    {

        var world = new World(defaultWorldName);
        World.DefaultGameObjectInjectionWorld = world;

        var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
        GenerateSystemLists(systems);

        DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, ExplicitDefaultWorldSystems);
#if !UNITY_DOTSRUNTIME
        ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world);
#endif
        return true;
    }
}
```

![](/files/-MR6TwOYJHDbfcS-J6og)

* Please be patient! We noticed in our testing that it takes a couple tries for Unity to pick up this new bootstrap script ⏳
* Now hit play in NavigationScene, then navigate to DOTS Windows to take a look at the available worlds

![](/files/-MR6UMr4L91-MnmdLoyv)

* Good, NetCodeBootstrap has prevented automatic creation of worlds 👍
  * Only DefaultWorld is available
* Now let's move onto creating ClientServerLauncher that will manually build (the previously automatically-created) Client and Server worlds
* Right-click in the NavigationScene Hierarchy and create a new empty GameObject called "ClientServerLauncher"
  * Move it just below "LocalGamesDiscovery" in the scene Hierarchy
  * Add a component to the GameObject that is a new script named "ClientServerLauncher"
    * First create the file by right-clicking in the Multiplayer Setup folder and selecting Create > C# Script

![](/files/-MR6_sm1Cz2iBiOA-LZc)

{% hint style="info" %}

#### 🔑MAJOR KEY ALERT 🔑

It is recommended by Unity to create the Client and Server worlds BEFORE navigating to a scene with converted SubScenes (as opposed to first navigating to MainScene *then* creating client and server worlds).

*When the scene is loaded it automatically triggers loading of all SubScenes it contains into all worlds. If you manually create the worlds you need to do so before you load the scene with your content or they will not stream in any sub-scenes. It would also be possible to manually trigger SubScene streaming - but in this case it would mean you need to manually keep track of all SubScenes.*

\- [From Tim Johansson (NetCode lead)](https://forum.unity.com/threads/dots-multiplayer-discussion.694669/page-6#post-6600691)
{% endhint %}

* Now let's update ClientServerLauncher
* Paste the code snippet below into ClientServerLauncher.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;

public class ClientServerLauncher : MonoBehaviour
{
    //These are the variables that will get us access to the UI views 
    //This is how we can grab active UI into a script
    //If this is confusing checkout the "Making a List" page in the gitbook
    
    //This is the UI Document from the Hierarchy in NavigationScene
    public UIDocument m_TitleUIDocument;
    private VisualElement m_titleScreenManagerVE;
    //These variables we will set by querying the parent UI Document
    private HostGameScreen m_HostGameScreen;
    private JoinGameScreen m_JoinGameScreen;
    private ManualConnectScreen m_ManualConnectScreen;



    void OnEnable()
    {

        //Here we set our variables for our different views so we can then add call backs to their buttons
        m_titleScreenManagerVE = m_TitleUIDocument.rootVisualElement;
        m_HostGameScreen = m_titleScreenManagerVE.Q<HostGameScreen>("HostGameScreen");
        m_JoinGameScreen = m_titleScreenManagerVE.Q<JoinGameScreen>("JoinGameScreen");
        m_ManualConnectScreen = m_titleScreenManagerVE.Q<ManualConnectScreen>("ManualConnectScreen");

        //Host Game Screen callback
        m_HostGameScreen.Q("launch-host-game")?.RegisterCallback<ClickEvent>(ev => ClickedHostGame());
        //Join Game Screen callback
        m_JoinGameScreen.Q("launch-join-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());
        //Manual Connect Screen callback
        m_ManualConnectScreen.Q("launch-connect-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());
    }
    
    // Update is called once per frame
    void Update()
    {
        
    }

    void ClickedHostGame()
    {
        //When we click "Host Game" that means we want to be both a server and a client
        //So we will trigger both functions for the server and client
        ServerLauncher();
        ClientLauncher();

        //This function will trigger the MainScene
        StartGameScene();
    }

    void ClickedJoinGame()
    {
        //When we click 'Join Game" that means we want to only be a client
        //So we do not trigger ServerLauncher
        ClientLauncher();

        //This function triggers the MainScene
        StartGameScene();
    }



    public void ServerLauncher()
    {
        //CreateServerWorld is a method provided by ClientServerBootstrap for precisely this reason
        //Manual creation of worlds

        //We must grab the DefaultGameObjectInjectionWorld first as it is needed to create our ServerWorld
        var world = World.DefaultGameObjectInjectionWorld;
#if !UNITY_CLIENT || UNITY_SERVER || UNITY_EDITOR
        ClientServerBootstrap.CreateServerWorld(world, "ServerWorld");

#endif
    }

    public void ClientLauncher()
    {
        //First we grab the DefaultGameObjectInjectionWorld because it is needed to create ClientWorld
        var world = World.DefaultGameObjectInjectionWorld;

        //We have to account for the fact that we may be in the Editor and using ThinClients
        //We initially start with 1 client world which will not change if not in the editor
        int numClientWorlds = 1;
        int totalNumClients = numClientWorlds;

        //If in the editor we grab the amount of ThinClients from ClientServerBootstrap class (it is a static variable)
        //We add that to the total amount of worlds we must create
#if UNITY_EDITOR
        int numThinClients = ClientServerBootstrap.RequestedNumThinClients;
        totalNumClients += numThinClients;
#endif
        //We create the necessary number of worlds and append the number to the end
        for (int i = 0; i < numClientWorlds; ++i)
        {
            ClientServerBootstrap.CreateClientWorld(world, "ClientWorld" + i);
        }
#if UNITY_EDITOR
        for (int i = numClientWorlds; i < totalNumClients; ++i)
        {
            var clientWorld = ClientServerBootstrap.CreateClientWorld(world, "ClientWorld" + i);
            clientWorld.EntityManager.CreateEntity(typeof(ThinClientComponent));
        }
#endif
    }

    void StartGameScene()
    {
        //Here we trigger MainScene
#if UNITY_EDITOR
        if(Application.isPlaying)
#endif
            SceneManager.LoadSceneAsync("MainScene");
#if UNITY_EDITOR
        else
            Debug.Log("Loading: " + "MainScene");
#endif
    }
}
```

* You'll notice that there are 3 key functions in ClientServerLauncher:
  * ServerLauncher (creates server world)
  * ClientLauncher (creates client world(s) if Thin Clients exist)
  * StartGameScene (triggers loading MainScene)
* Depending on whether we click "Host Game" or "Join Game" we trigger 2 or 3 of these functions

  * We always trigger StartGameScene and ClientLauncher
  * We launch ServerLauncher only if we are a host&#x20;

![](/files/-MR6eMCsbqHLlPOEG_1p)

* Select ClientServerLauncher in the Hierarchy, click Add Component in Inspector and add Client Server Launcher
* Drag TitleScreenUI GameObject from the Hierarchy onto the "Title UI Document" field under the Client Server Launcher component in Inspector while ClientServerLauncher is still selected in Hierarchy
* We now need to add our scenes to our build settings so we can navigate to them
  * Navigate to MainScene, go to "Build Settings..." and click "Add Open Scenes"
  * Return to NavigationScene, go to "Build Settings..." and click "Add Open Scenes"
* Go to Multiplayer > PlayMode Tools >  set the Num Thin Clients equal to 1

![Updating Title UI Document, build settings, and Num Thin Clients](/files/-MR6gU-9a38R6XzOoCLZ)

* Hit play, and then click Host Game

![Navigating to MainScene with the proper amount of ClientWorlds](/files/-MR6gmUEiMFnjpK5FnPu)

{% hint style="success" %}
We are now able to transition to MainScene with the proper amount of NetCode worlds

* We created NetCodeBootstrap
* We created ClientServerLauncher GameObject
  * Added a new script ClientServerLauncher
* We updated ClientServerLauncher to trigger creation of client/server worlds and trigger a scene
* We added our scenes to our build settings
* We dragged TitleUI to our ClientServerLauncher's "Title UI Document" field&#x20;
  {% endhint %}

## Navigating from MainScene to NavigationScene

Similar to how we created client/server worlds to transition to MainScene, we now need to destroy those client/server worlds when we return to NavigationScene. We will also delete all entities created so we can start again in NavigationScene with a "blank slate".

* Let's update our ClientServerConnectionHandler to be able to take over these new "clean-up" duties
  * We think this is a good place to put "clean-up" functionality because it's part of handling the connection between servers and clients
* Paste the code snippet below into ClientServerConnectionHandler.cs:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;

public class ClientServerConnectionHandler : MonoBehaviour
{
    //this is the store of server/client info
    public ClientServerInfo ClientServerInfo;

    // these are the launch objects from Navigation scene that tells what to set up
    private GameObject[] launchObjects;

    //these will gets access to the UI views 
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;

    void OnEnable()
    {
        // This will put callback on "Quit Game" button
        // This triggers the clean up function (ClickedQuitGame)
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_GameManagerUIVE.Q("quit-game")?.RegisterCallback<ClickEvent>(ev => ClickedQuitGame());
    }

    void Awake()
    {
        launchObjects = GameObject.FindGameObjectsWithTag("LaunchObject");
        foreach(GameObject launchObject in launchObjects)
        {
            ///  
            // checks for server launch object
            // does set up for the server for listening to connections and player scores
            //
            if(launchObject.GetComponent<ServerLaunchObjectData>() != null)
            {
                //sets the gameobject server data (mono)
                ClientServerInfo.IsServer = true;
                
                //sets the component server data in server world(dots)
                //ClientServerConnectionControl (server) will run in server world
                //it will pick up this component and use it to listen on the port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ServerSimulationSystemGroup
                    //we move forward (because that is the server world)
                    if (world.GetExistingSystem<ServerSimulationSystemGroup>() != null)
                    {
                        var ServerDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ServerDataEntity, new ServerDataComponent
                        {
                            GamePort = ClientServerInfo.GamePort
                        });
                        //create component that allows server initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeServerComponent));
                    }
                }
            }

            // 
            // checks for client launch object
            //  does set up for client for dots and mono
            // 
            if(launchObject.GetComponent<ClientLaunchObjectData>() != null)
            {
                //sets the gameobject data in ClientServerInfo (mono)
                //sets the gameobject data in ClientServerInfo (mono)
                ClientServerInfo.IsClient = true;
                ClientServerInfo.ConnectToServerIp = launchObject.GetComponent<ClientLaunchObjectData>().IPAddress;                

                //sets the component client data in server world(dots)
                //ClientServerConnectionControl (client) will run in client world
                //it will pick up this component and use it connect to IP and port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ClientSimulationSystemGroup
                    //we move forward (because that is the client world)
                    if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                    {
                        var ClientDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ClientDataEntity, new ClientDataComponent
                        {
                            ConnectToServerIp = ClientServerInfo.ConnectToServerIp,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //create component that allows client initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeClientComponent));
                    }
                }
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
   //This function will navigate us to NavigationScene
    void ClickedQuitGame()
    {

#if UNITY_EDITOR
        if(Application.isPlaying)
#endif
            SceneManager.LoadSceneAsync("NavigationScene");
#if UNITY_EDITOR
        else
            Debug.Log("Loading: " + "NavigationScene");
#endif
    }

    //When the OnDestroy method is called (because of our transition to NavigationScene) we
    //must delete all our entities and our created worlds to go back to a blank state
    //This way we can move back and forth between scenes and "start from scratch" each time
    void OnDestroy()
    {
        //This query deletes all entities
        World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(World.DefaultGameObjectInjectionWorld.EntityManager.UniversalQuery);
        //This query deletes all worlds
        World.DisposeAllWorlds();

        //We return to our initial world that we started with, defaultWorld
        var bootstrap = new NetCodeBootstrap();
        bootstrap.Initialize("defaultWorld"); 

    }
}
```

* Within MainScene, click on ClientServerConnectionHandler in the Hierarchy and drag the GameUI GameObject (also in Hierarchy) onto the "Game UI Document" field under Client Server Connection Handler component in Inspector, save, and navigate back to NavigationScene

![Updating ClientServerConnectionHandler and returning to NavigationScene](/files/-MR6kw-_iOYpSEKZhHBx)

* Hit play, host a game, play around, quit, and host again

![Navigation between scenes functioning](/files/-MR6lviIsO_B8gERn2Tl)

* **Note that hitting play in MainScene will no longer trigger starting the gameplay**
  * **This is because client/server worlds are created in NavigationScene**
* Note that hitting "Join Game" will not trigger gameplay because we are not connected to a server
  * This will be updated in the Multiplayer section

{% hint style="success" %}
We are now able to navigate back to NavigationScene by hitting the "Quit Game" button in MainScene

* We updated ClientServerConnectionHandler
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Navigating-Between-Scenes'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Intro to Unity NetCode Multiplayer

Code and workflows to find/create/join/leave LAN games with authoritative scorekeeping

## What you'll develop in the Multiplayer section

![Ability to join a LAN game through broadcasting and receive ghosts within a certain radius](/files/-MSA9Tc7LlOeRxJVASg_)

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/GhostRelevancyMode-and-Clean-Up>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

### Functionalities included

* Using launch GameObjects to configure server and client connections
  * Trigger starting a game as a NetCode host
  * Trigger starting a game but joining as a NetCode client to a specific IP address
* Sending and receiving broadcast messages
  * Automatically send a broadcast UDP packet of game information including IP address on LAN for players to join
  * Listen and receive broadcast UDP packet information and populating a ListView of available LAN games
* Run Threads to listen for UDP packets
  * Be able to run non-Unity handled threads in-game
* Graceful handling of joining and leaving games
  * Add NetworkStreamRequestDisconnect component on both host and client when quitting a game
  * Handling timeouts by querying for NetworkStreamDisconnected components from host and client to clean up entities and return to title screen
* Server-authoritative scorekeeping
  * Create PlayerScore ghosts and HighestScore ghosts to keep player scores authoritative and in sync
  * Associate scores with NCE NetworkIdComponent values
* UI Toolkit + DOTS
  * Updating Game UI through a MonoBehaviour by pulling DOTS data from ghosts
* Using GhostRelevancyMode to be mindful of network transmission
  * We will implement a server-side system that will send only send ghosts within a certain radius to players
  * We will also include the ability to override this behavior to send player scores to all players
* UI Toolkit's new **data binding** to keep UI up-to-date with MonoBehavior variables

## How we'll handle finding/joining/hosting games

### Joining and hosting games

We must pass data between NavigationScene and MainScene in our project. This data includes:

* Game name
* Server IP
* Player name
* Broadcast port

These are the same values that are currently in our ClientLaunchObject and ServerLaunchObject in MainScene.

We will create ClientLaunchObject and ServerLaunchObject in NavigationScene before we transition to MainScene and use [DontDestroyOnLoad()](https://docs.unity3d.com/2020.3/Documentation/ScriptReference/Object.DontDestroyOnLoad.html) so they "survive" the transition between scenes.

{% hint style="info" %}
Unity has started to recommend using a ["manager scene" by using additive scene loading](https://docs.unity3d.com/2020.1/Documentation/Manual/MultiSceneEditing.html). This is a solid approach, but it's just a bit overkill for this tutorial so you won't see it in this gitbook.
{% endhint %}

### Finding games

We will be using .NET's [UdpClient](https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.udpclient?view=net-5.0) class to send broadcast packets across the local area network's router. This will not work if the player is not connected to WiFi.

We will also be using .NET's [Thread](https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread?view=net-5.0) class to create listening threads for broadcast packets so it does not interrupt Unity's threading.

## Linking DOTS and MonoBehaviour

NetCode will handle the tracking of scores of each player in the game. We want to take this data from ECS and populate the game UI which runs on MonoBehaviours.

In this section we will demonstrate our approach of pulling data from ECS into MonoBehaviours by using the EntityManager.

In the next AR Foundation section, we will also demonstrate how we provide data from MonoBehaviours (the AR Camera's "pose") to ECS.

**With both of these techniques it is possible to use any classic Unity MonoBehaviours with ECS. (big deal!)**

They key is to understand that a ECS System cannot "push" values directly into a GameObject; instead, a MonoBehaviour must "pull" data using an EntityManager. **<- BIG INSIGHT**

## Unity resources

Unity documentation for NetCode 0.50.01-preview\.19: <https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/index.html> **Refer to this for more information on NetCode.**

Unity samples for NetCode: <https://github.com/Unity-Technologies/multiplayer> **This is the official Unity Asteroids sample that we based  our gitbook off of.**

Unity thread for NetCode: <https://forum.unity.com/forums/dots-netcode.425/>**Unity is responsive here.**

Unity documentation for UI Builder 1.0.0-preview\.18: <https://docs.unity3d.com/Packages/com.unity.ui.builder@1.0/manual/index.html> **Refer to this for more information.**

Unity documentation for UI Toolkit: <https://docs.unity3d.com/2020.3/Documentation/Manual/UIElements.html> **Refer to this for more information on  UI Toolkit.**

Microsoft's documentation for **UdpClient**: <https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.udpclient?view=net-5.0> **Refer to this for more info on broadcasting.**

Microsoft's documentation for **Thread**: <https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread?view=net-5.0> **Refer to this for more info on multi-threading.**

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Host or Join a Multiplayer Session on LAN

Code and workflows for hosting and/or joining a multiplayer session on LAN, and gracefully handling hosts/clients leaving

## What you'll develop on this page

![Host/join a game and gracefully handle host/client leaving](/files/-MRfZlUpWhje92EJAt5L)

We will add logic to our transition between NavigateScene and MainScene that configures whether  or not MainScene loads up ServerWorld and what IP address ClientWorld connects to.

We will also gracefully handle hosts or clients leaving a game.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Hosting-Joining-and-Leaving-a-Game>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Hosting and joining

#### First, some background

We have 3 views that could take us to MainScene:

* HostGameScreen
* JoinGameScreen
* ManualConnectScreen

![Asteroid NavigationScene view flow diagram](/files/-MQyOalNZixdjhh6i0V6)

We will update the custom Visual Elements (cVE) of HostGameScreen and ManualConnectScreen views to initially populate their values with system data. "Player Name" and "Game Name" will default to the host name of the machine running the application. We will update JoinGameScreen in the next section, "Broadcasting and Joining on LAN," when we work on broadcasting.

We will also update our LocalGamesFinder script (which we use to populate the table) with two new public variables, "Broadcast Ip Address" and "Broadcast Port." These values will be used in the next section, but we will update our ServerLaunchObject with these values now in this section to avoid doubling back to this flow diagram (which would be annoying for us and not really teach us anything).

We have a single script called "ClientServerLauncher" that handles the callbacks for these 3 views mentioned above. We will update ClientServerLauncher so that it will create the ClientLaunchObject and ServerLaunchObject that currently exist in our MainScene.

We will also update our ClientLaunchObjectData and ServerLaunchObjectData with our new broadcast, game, and player fields. ClientServerConnectionHandler will then pass this data onto our ClientServerInfo object.

![](/files/-MRXnoVFKrdpdTMplf3j)

We will update ClientDataComponent and ServerDataComponent to hold this additional information. We will also make GameNameComponent to be used by the client to store the game name.

To pass the game name from the server to the client we will update our load game workflow.

Finally, we will create GameOverlayUpdater to update the Game UI on the client with the new game and player information.

#### Now let's implement

* Let's update LocalGamesFinder used by LocalGamesDiscovery in NavigateScene to be the "source of truth" for which IP address and port our server will broadcast UDP packets on
  * This is similar to how the game port is stored in MainScene in the ClientServerInfo GameObject
* Add these lines to LocalGamesFinder.cs. Put them before **OnEnable()**

```
    ///The broadcast ip address and port to be used by the server across the LAN
    public string BroadcastIpAddress = "255.255.255.255";
    public ushort BroadcastPort = 8014;
```

![Update the LocalGamesFinder file with 2 new public variables](/files/-MReqIT5SXff5jUIiGa7)

* Now let's update HostGameScreen to automatically populate data based on host name and IP address

* &#x20;To do this, we will first update the uxml so that the game's IP address is read-only
  * This way, the host is not able to configure which IP address their machine can bind on
  * Why didn't we just build it this way in the first place?!

    * We thought there would be more "oomph" to this tutorial if we point out that a machine cannot configure which IP address they can start a server on in Unity 😉

* With the code snippet below, we are updating the HostGameScreen uxml by changing a TextField VisualElement to a Label VisualElement

* Paste the code snippet below into HostGameScreen.uxml:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <HostGameScreen name="HostGameScreen" class="screen HostGameScreen">
            <ui:VisualElement name="header" class="header">
                <ui:Button text="Main Menu" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content" style="top: 108px; left: auto; position: absolute;">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Host a Local Game" display-tooltip-when-elided="True" name="section-title" class="section-title" style="color: rgb(160, 194, 114);" />
                </ui:VisualElement>
                <ui:VisualElement name="game-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="HostNameValue" text="GameName" name="game-name" class="data-section-input" />
                    <ui:Label text="Your Game Name" display-tooltip-when-elided="True" name="game-name-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:Label text="127.0.0.1" display-tooltip-when-elided="True" name="game-ip" class="data-section-input" style="border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 0;" />
                    <ui:Label text="Your Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="PlayerName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Host Game" display-tooltip-when-elided="True" name="launch-host-game" class="green-button" />
            </ui:VisualElement>
        </HostGameScreen>
    </ui:ScrollView>
</ui:UXML>
```

![Updating HostGameScreen uxml to have an IP address as a label](/files/-MRepsE9Bm8PEJ8CyzjX)

* Now let's update the HostGameScreen custom VisualElement (cVE)
  * We will pull the host name data and place it in both our Game Name field and our Player Name field
  * We will pull the host IP address and place it in our IP Address label
* Paste the code snippet below into HostGameScreen.cs (cVE):

```
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.SceneManagement;

public class HostGameScreen : VisualElement
{
    //We will update these fields with system data
    TextField m_GameName;
    Label m_GameIp;
    TextField m_PlayerName;

    //These are the system data variables we will be using
    String m_HostName = "";
    IPAddress m_MyIp;

    public new class UxmlFactory : UxmlFactory<HostGameScreen, UxmlTraits> { }

    public HostGameScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        // 
        // PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
        // 
        m_GameName = this.Q<TextField>("game-name");
        m_GameIp = this.Q<Label>("game-ip");
        m_PlayerName = this.Q<TextField>("player-name");

        // 
        // INITIALIZE ALL THE TEXT FIELD WITH NETWORK INFORMATION
        //        
        m_HostName = Dns.GetHostName();
        // "best tip of all time award" to MichaelBluestein
        // somehow this is the best way to get your IP address on all the internet
        foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces()) {
            if (netInterface.OperationalStatus == OperationalStatus.Up &&  
                (netInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
                netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet)) {
                foreach (var addrInfo in netInterface.GetIPProperties().UnicastAddresses) {
                    if (addrInfo.Address.AddressFamily == AddressFamily.InterNetwork) {

                        m_MyIp = addrInfo.Address;
                    }
                }
            }  
        }

        //Now we set our VisualElement fields
        m_GameName.value = m_HostName;
        m_GameIp.text = m_MyIp.ToString();
        m_PlayerName.value = m_HostName;

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }
}

```

![Updating HostGameScreen cVE to populate fields](/files/-MRepPwQPrV8Wj_Jre1-)

* We also need to update our ManualConnectScreen uxml to have default data of 127.0.0.1
* Paste the code snippet below into ManualConnectScreen.uxml:

```
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <ManualConnectScreen name="ManualConnectScreen" class="screen ManualConnectScreen">
            <ui:VisualElement name="header" class="header">
                <ui:Button text="Back Button" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Manually Connect" display-tooltip-when-elided="True" name="section-title" class="section-title" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="127.0.0.1" text="127.0.0.1" name="game-ip" readonly="false" class="data-section-input" style="background-color: rgb(255, 255, 255);" />
                    <ui:Label text="Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="PlayerName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Join Game" display-tooltip-when-elided="True" name="launch-connect-game" class="blue-button" style="height: 120px;" />
            </ui:VisualElement>
        </ManualConnectScreen>
    </ui:ScrollView>
</ui:UXML>
```

![Updating ManualConnectScreen uxml IP address default value](/files/-MRes20a0UhAnTdRGWkC)

* Previously ManualConnectScreen uxml had a "Value" of "HostIPValue" even though the text read "127.0.0.1"
  * This illustrates that there can be a difference between what TextField "shows" and what "value" is saved
    * Once you update the TextField the value updates to what text is entered (automatically)
* Next, update our ManualConnectScreen cVE
  * Here we only set our Player Name
  * We will not automatically set the IP address using any information
    * We leave the local host IP address as the default to hopefully inform our user that if this address is not updated, the client will try and connect to itself without a running server, which will not work
* Paste the code snippet below into ManualConnectScreen.cs (cVE):

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

public class ManualConnectScreen : VisualElement
{
    //We will update these fields with system data
    TextField m_GameIp;
    TextField m_PlayerName;

    //These are the system data variables we will be using
    string m_HostName = "";

    public new class UxmlFactory : UxmlFactory<ManualConnectScreen, UxmlTraits> { }

    public ManualConnectScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        // 
        // PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
        // 
        m_GameIp = this.Q<TextField>("game-ip");
        m_PlayerName = this.Q<TextField>("player-name");

        // 
        // INITIALIZE ALL THE TEXT FIELD WITH NETWORK INFORMATION
        // 
        m_HostName = Dns.GetHostName();

        //Now we set our VisualElement fields
        m_PlayerName.value = m_HostName;

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }
}
```

![Updating ManualConnectScreen cVE to populate with system data](/files/-MRes91TTb_bsPrlIwoi)

* With NavigationScene section, hit play
* Navigate to the Host Game view and the Manual Connect view

![Checking out Host and Manual screen values population](/files/-MResFFNiWSYMT8svhuA)

* Great, our system data populates in the appropriate fields
* Now we need to update the data we will be passing through to MainScene in these scripts:
  * ClientLaunchObjectData
  * ServerLaunchObjectData
* First start by pasting the code snippet below into ClientLaunchObjectData.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;

public class ClientLaunchObjectData : MonoBehaviour
{
    //This will be set by ClientServerLauncher in NavigationScene
    //It will then be pulled out in MainScene and put into ClientServerInfo
    public string PlayerName;
    public string IPAddress;
}
```

* Next, paste this code snippet into ServerLaunchObjectData.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;

public class ServerLaunchObjectData : MonoBehaviour
{
    //This will be set by ClientServerLauncher in NavigationScene
    //It will then be pulled out in MainScene and put into ClientServerInfo
    public string GameName;
    public string BroadcastIpAddress;
    public ushort BroadcastPort;    
}
```

![Updating our launch objects' data to hold new values](/files/-MRetm1el4DxVHTeq23G)

* Now go to Main Scene
* Drag our ClientLaunchObject and ServerLaunchObject from MainScene into our Scripts and Prefabs folder to make them prefabs
* Then delete them from MainScene
  * We will now be able to reference them in our ClientServerLauncher script

![](/files/-MRetszeBTtT0fUu287N)

* Great, now let's update ClientServerLauncher to grab data from the views and populate them in our launch objects
* Paste the code snippet below into ClientServerLauncher.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;

public class ClientServerLauncher : MonoBehaviour
{
    //These will be used to grab the broadcasting port and address
    public LocalGamesFinder GameBroadcasting;
    private string m_BroadcastIpAddress;
    private ushort m_BroadcastPort;

    //These are the variables that will get us access to the UI views 
    //This is how we can grab active UI into a script
    //If this is confusing checkout the "Making a List" page in the gitbook
    
    //This is the UI Document from the Hierarchy in NavigationScene
    public UIDocument m_TitleUIDocument;
    private VisualElement m_titleScreenManagerVE;
    //These variables we will set by querying the parent UI Document
    private HostGameScreen m_HostGameScreen;
    private JoinGameScreen m_JoinGameScreen;
    private ManualConnectScreen m_ManualConnectScreen;

    //These will persist through the scene transition
    //MainScene will look for 1 or both of the objects
    //Based on what MainScene finds it will initialize as Server/Client
    public GameObject ServerLauncherObject;
    public GameObject ClientLauncherObject;

    //These pieces of data will be taken from the views
    //and put into the launch objects that persist between scenes
    public TextField m_GameName;
    public TextField m_GameIp;
    public Label m_GameIpLabel;
    public TextField m_PlayerName;


    void OnEnable()
    {

        //Here we set our variables for our different views so we can then add call backs to their buttons
        m_titleScreenManagerVE = m_TitleUIDocument.rootVisualElement;
        m_HostGameScreen = m_titleScreenManagerVE.Q<HostGameScreen>("HostGameScreen");
        m_JoinGameScreen = m_titleScreenManagerVE.Q<JoinGameScreen>("JoinGameScreen");
        m_ManualConnectScreen = m_titleScreenManagerVE.Q<ManualConnectScreen>("ManualConnectScreen");

        //Host Game Screen callback
        m_HostGameScreen.Q("launch-host-game")?.RegisterCallback<ClickEvent>(ev => ClickedHostGame());
        //Join Game Screen callback
        m_JoinGameScreen.Q("launch-join-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());
        //Manual Connect Screen callback
        m_ManualConnectScreen.Q("launch-connect-game")?.RegisterCallback<ClickEvent>(ev => ClickedConnectGame());
    }
    
    // Start is called before the first frame update
    void Start()
    {
        //We are grabbing the broadcasting information from the discover script
        //We are going to bundle it with the server launch object so it can broadcast at that information
        m_BroadcastIpAddress = GameBroadcasting.BroadcastIpAddress;
        m_BroadcastPort = GameBroadcasting.BroadcastPort;
    }

    void ClickedHostGame()
    {
        //This gets the latest values on the screen
        //Our HostGameScreen cVE defaults these values but player name and game name can be updated
        //We set these VisualElement variables OnClick instead of OnEnable because this way
        //we don't need to make a variable for player name for every view, just 1 and set which view
        //we get it from OnClick (which is when we need it)
        m_GameName = m_HostGameScreen.Q<TextField>("game-name");
        m_GameIpLabel = m_HostGameScreen.Q<Label>("game-ip");
        m_PlayerName = m_HostGameScreen.Q<TextField>("player-name");

        //Now we grab the values from the VisualElements
        var gameName = m_GameName.value;
        var gameIp = m_GameIpLabel.text;
        var playerName = m_PlayerName.value;

        //When we click "Host Game" that means we want to be both a server and a client
        //So we will trigger both functions for the server and client
        ServerLauncher(gameName);
        ClientLauncher(playerName, gameIp);

        //This function will trigger the MainScene
        StartGameScene();
    }

    void ClickedJoinGame()
    {
        //This gets the latest values on the screen
        //Our JoinGameScreen cVE defaults these values but player name can be updated
        //We set these VisualElement variables OnClick instead of OnEnable because this way
        //we don't need to make a variable for player name for every view, just 1 and set which view
        //we get it from OnClick (which is when we need it)
        m_GameIpLabel = m_JoinGameScreen.Q<Label>("game-ip");
        m_PlayerName = m_JoinGameScreen.Q<TextField>("player-name");

        //Now we grab the values from the VisualElements
        var gameIp = m_GameIpLabel.text;
        var playerName = m_PlayerName.value;

        //When we click "Join Game" that means we want to be only a client
        ClientLauncher(playerName, gameIp);

        //This function will trigger the MainScene
        StartGameScene();
    }

    void ClickedConnectGame()
    {
        //This gets the latest values on the screen
        //Our ManualConnectScreen cVE defaults these values but player name and IP address be updated
        //We set these VisualElement variables OnClick instead of OnEnable because this way
        //we don't need to make a variable for player name for every view, just 1 and set which view
        //we get it from OnClick (which is when we need it)
        m_GameIp = m_ManualConnectScreen.Q<TextField>("game-ip");
        m_PlayerName = m_ManualConnectScreen.Q<TextField>("player-name");

        //Now we grab the values from the VisualElements
        var gameIp = m_GameIp.value;
        var playerName = m_PlayerName.value;

        //When we click "Join Game" that means we want to be only a client
        ClientLauncher(playerName, gameIp);

        //This function will trigger the MainScene
        StartGameScene();
    }



    public void ServerLauncher(string gameName)
    {
        //Here we create the launch GameObject and load it with necessary data
        GameObject  serverObject = Instantiate(ServerLauncherObject);
        DontDestroyOnLoad(serverObject);

        //This sets up the server object with all its necessary data
        serverObject.GetComponent<ServerLaunchObjectData>().GameName = gameName;
        serverObject.GetComponent<ServerLaunchObjectData>().BroadcastIpAddress = m_BroadcastIpAddress;
        serverObject.GetComponent<ServerLaunchObjectData>().BroadcastPort = m_BroadcastPort;

        //CreateServerWorld is a method provided by ClientServerBootstrap for precisely this reason
        //Manual creation of worlds

        //We must grab the DefaultGameObjectInjectionWorld first as it is needed to create our ServerWorld
        var world = World.DefaultGameObjectInjectionWorld;
#if !UNITY_CLIENT || UNITY_SERVER || UNITY_EDITOR
        ClientServerBootstrap.CreateServerWorld(world, "ServerWorld");

#endif
    }

    public void ClientLauncher(string playerName, string ipAddress)
    {
        //Here we create the launch GameObject and load it with necessary data
        GameObject  clientObject = Instantiate(ClientLauncherObject);
        DontDestroyOnLoad(clientObject);
        clientObject.GetComponent<ClientLaunchObjectData>().PlayerName = playerName;
        clientObject.GetComponent<ClientLaunchObjectData>().IPAddress = ipAddress;

        //We grab the DefaultGameObjectInjectionWorld because it is needed to create ClientWorld
        var world = World.DefaultGameObjectInjectionWorld;

        //We have to account for the fact that we may be in the Editor and using ThinClients
        //We initially start with 1 client world which will not change if not in the editor
        int numClientWorlds = 1;
        int totalNumClients = numClientWorlds;

        //If in the editor we grab the amount of ThinClients from ClientServerBootstrap class (it is a static variable)
        //We add that to the total amount of worlds we must create
#if UNITY_EDITOR
        int numThinClients = ClientServerBootstrap.RequestedNumThinClients;
        totalNumClients += numThinClients;
#endif
        //We create the necessary number of worlds and append the number to the end
        for (int i = 0; i < numClientWorlds; ++i)
        {
            ClientServerBootstrap.CreateClientWorld(world, "ClientWorld" + i);
        }
#if UNITY_EDITOR
        for (int i = numClientWorlds; i < totalNumClients; ++i)
        {
            var clientWorld = ClientServerBootstrap.CreateClientWorld(world, "ClientWorld" + i);
            clientWorld.EntityManager.CreateEntity(typeof(ThinClientComponent));
        }
#endif
    }

    void StartGameScene()
    {
        //Here we trigger MainScene
#if UNITY_EDITOR
        if(Application.isPlaying)
#endif
            SceneManager.LoadSceneAsync("MainScene");
#if UNITY_EDITOR
        else
            Debug.Log("Loading: " + "MainScene");
#endif
    }
}
```

![Updating ClientServerLauncher to pass through new data](/files/-MRetw_iol9fPTm6ns-5)

* Now let's drag our ClientLaunchObject and ServerLaunchObject prefabs from the Scripts and Prefabs folder into the appropriate fields in our ClientServerLauncher GameObject in NavigationScene
  * As a reminder you can find these fields in Inspector when ClientServerLauncher is selected in Hierarchy
  * Lastly, let's also drag our LocalGamesDiscovery GameObject (in Hierarchy) into the Game Broadcasting field

![Updating our ClientServerLauncher GameObject](/files/-MReugrTH_fOEN4uL_cO)

* Let's hit play, navigate to Host Game, click Host, and check it out

![Checking out hosting configuration through launch objects](/files/-MReuYbvJLraGJ1QZQEj)

* Now we are able to create our launch objects and our proper worlds are created 👍
* Now let's go to Manual Connect screen and join an IP address

![Checking out joining configuration through launch objects](/files/-MReuo4XoCe_-Tl8a0NQ)

* We can see from the logs that we have attempted to connect to the proper IP address

{% hint style="success" %}
We are now able to take configurations from our NavigationScene and use them to create launch objects that are interpreted by our MainScene

* We updated LocalGamesFinder
* We updated our HostGameScreen uxml
* We updated HostGameScreen and ManualConnectScreen cVEs to populate with default system data
* We updated ClientLaunchObjectData and ServerLaunchObject data to take in more configuration data
* We turned ClientLaunchObjectData and ServerLaunchObject into prefabs, and removed them from MainScene
* We updated ClientServerLauncher to pull data and put them into our launch objects
  {% endhint %}

## Updating our Game UI

* Now let's update ClientServerInfo to be able to take in the additional information provided by the launch objects
* Paste the code snippet below into ClientServerInfo.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using System;
 
public class ClientServerInfo : MonoBehaviour
{
    public bool IsServer = false;
    public bool IsClient = false;
    public string ConnectToServerIp;
    public ushort GamePort = 5001;

    public string GameName;
    public string PlayerName;

    public string BroadcastIpAddress;
    public ushort BroadcastPort;

}
```

![Updating ClientServerInfo to take in more data](/files/-MRevWirdrgaWV0_OlR2)

* Let's also update our ServerDataComponent to take in additional information: game name
  * We want our server to send the game name data to our client
  * A client manually connects to an IP address; they must be sent the name
* Paste the code snippet below into ServerDataComponent.cs:

```
using Unity.Entities;
using Unity.Collections;


 public struct ServerDataComponent : IComponentData
{
    public FixedString64Bytes GameName;
    public ushort GamePort;
}
```

![](/files/-MRevYuodBAONCEmyI8u)

* Now we will update ClientDataComponent to take in additional information: player name
  * We will make use of this in the "Scorekeeping" section when the player name needs to be sent to the server in order to keep score&#x20;
* Paste the code snippet below into ClientDataComponent.cs:

```
using System;
using Unity.Entities;
using Unity.Collections;

public struct ClientDataComponent : IComponentData
{
    //Must used "FixedStringN" instead of stirng in IComponentData
    //This is a DOTS requirement because IComponentData must be a struct
    public FixedString64Bytes ConnectToServerIp;
    public ushort GamePort;
    public FixedString64Bytes PlayerName;
}
```

![](/files/-MRev_vPFC21IFb-CBge)

* We must also create a new component called GameNameComponent in Multiplayer Setup to store the game name on the client
  * Right-click in Multiplayer Setup > Create > C# Script > name it "GameNameComponent"
  * It may seem weird to create a component for game name because we already have that data set in ClientServerInfo when hosting a session. However, please remember that we are building both a host build and a client build, and the client will not immediately have this information if they manually connect to an IP address
  * OK, then why aren't we just putting GameName inside ClientDataComponent? Why are we making a new component? Don't we have enough of these components already?!
  * Let us explain ourselves: .GameName is a FixedString64Bytes string, which means that it faces a limitation if and when it's used in a component. [The limitation of using FixedString in a component is that you cannot "tell" if the value of the FixedString has been set](https://forum.unity.com/threads/ecs-is-it-possible-to-check-if-a-components-data-is-not-null-has-been-set.1042660/#post-6748750) if it's in a component
    * FixedStringByte's default value is equal to an empty string
    * So to circumvent this limitation, we need to create an entirely separate component just to store the value of GameName. To see if its value has been set, we check for the existence of the entire component
    * This mouthful is just to explain and show you that you cannot check to see if a FixedStringByte field has been updated in a component
* Paste the code snippet below into GameNameComponent.cs:

```
using Unity.Entities;
using Unity.Collections;

public struct GameNameComponent : IComponentData
{
    //Must used "FixedStringN" instead of stirng in IComponentData
    //This is a DOTS requirement because IComponentData must be a struct
    public FixedString64Bytes GameName;
}
```

![Creating GameNameComponent](/files/-MRexbLmhs84UEDVa2nE)

* Now let's update our ClientServerConnectionHandler to pass through more data from the launch objects to ClientServerInfo as well as ClientDataComponent and ServerDataComponent
* Paste the code snippet below into ClientServerConnectionHandler.cs:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;

public class ClientServerConnectionHandler : MonoBehaviour
{
    //This is the store of server/client info
    public ClientServerInfo ClientServerInfo;

    //These are the launch objects from Navigation scene that tells what to set up
    private GameObject[] launchObjects;

    //These will gets access to the UI views 
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;

    void OnEnable()
    {
        //This will put callback on "Quit Game" button
        //This triggers the clean up function (ClickedQuitGame)
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_GameManagerUIVE.Q("quit-game")?.RegisterCallback<ClickEvent>(ev => ClickedQuitGame());
    }

    void Awake()
    {
        launchObjects = GameObject.FindGameObjectsWithTag("LaunchObject");
        foreach(GameObject launchObject in launchObjects)
        {
            ///  
            //Checks for server launch object
            //If it exists it creates ServerDataComponent InitializeServerComponent and
            //passes through server data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ServerLaunchObjectData>() != null)
            {
                //This sets the gameobject server data  in ClientServerInfo (mono)
                ClientServerInfo.IsServer = true;
                ClientServerInfo.GameName = launchObject.GetComponent<ServerLaunchObjectData>().GameName;
                ClientServerInfo.BroadcastIpAddress = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastIpAddress;
                ClientServerInfo.BroadcastPort = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastPort;

                //This sets the component server data in server world(dots)
                //ClientServerConnectionControl (server) will run in server world
                //it will pick up this component and use it to listen on the port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ServerSimulationSystemGroup
                    //we move forward (because that is the server world)
                    if (world.GetExistingSystem<ServerSimulationSystemGroup>() != null)
                    {
                        var ServerDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ServerDataEntity, new ServerDataComponent
                        {
                            GameName = ClientServerInfo.GameName,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows server initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeServerComponent));
                    }
                }
            }

            // 
            //Checks for client launch object
            //If it exists it creates ClientDataComponent, InitializeServerComponent and
            // passes through client data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ClientLaunchObjectData>() != null)
            {
                //This sets the gameobject data in ClientServerInfo (mono)
                ClientServerInfo.IsClient = true;
                ClientServerInfo.ConnectToServerIp = launchObject.GetComponent<ClientLaunchObjectData>().IPAddress;                
                ClientServerInfo.PlayerName = launchObject.GetComponent<ClientLaunchObjectData>().PlayerName;

                //This sets the component client data in server world (dots)
                //ClientServerConnectionControl (client) will run in client world
                //it will pick up this component and use it connect to IP and port
                foreach (var world in World.All)
                {
                    //We cycle through all the worlds, and if the world has ClientSimulationSystemGroup
                    //we move forward (because that is the client world)
                    if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                    {
                        var ClientDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ClientDataEntity, new ClientDataComponent
                        {
                            PlayerName = ClientServerInfo.PlayerName,
                            ConnectToServerIp = ClientServerInfo.ConnectToServerIp,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows client initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeClientComponent));
                    }
                }
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
   //This function will navigate us to NavigationScene
    void ClickedQuitGame()
    {

#if UNITY_EDITOR
        if(Application.isPlaying)
#endif
            SceneManager.LoadSceneAsync("NavigationScene");
#if UNITY_EDITOR
        else
            Debug.Log("Loading: " + "NavigationScene");
#endif
    }

    //When the OnDestroy method is called (because of our transition to NavigationScene) we
    //must delete all our entities and our created worlds to go back to a blank state
    //This way we can move back and forth between scenes and "start from scratch" each time
    void OnDestroy()
    {
        //This query deletes all entities
        World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(World.DefaultGameObjectInjectionWorld.EntityManager.UniversalQuery);
        //This query deletes all worlds
        World.DisposeAllWorlds();

        //We return to our initial world that we started with, defaultWorld
        var bootstrap = new NetCodeBootstrap();
        bootstrap.Initialize("defaultWorld"); 

    }
}
```

{% hint style="info" %}
Why do we duplicate data in ServerDataComponent/ClientDataComponent and ClientServerInfo? That seems super redundant...

Sometimes we want to use data in ECS, and because of that we save data in components so our systems can easily access it. At other times we want to use the data in a MonoBehaviour, so we save it in a GameObject to make it easily accessible to scripts.

This is not a rock-solid approach because it can be easy to "forget" to update data in one of the places we store it and not the other. The approach we take in this gitbook is to first always save any updates to ClientServerInfo and only push data into ECS from thereafter, as you might notice in ClientServerConnectionHandler.
{% endhint %}

![Updating ClientServerConnectionHandler to pass through more data](/files/-MRexf60IEXRCzLbzif8)

* Now let's hit play and join through Host Game and Manual Connect and check out the updates to ClientServerInfo
  * Play around! For example, go ahead and change up the input fields on the view to see the updates in ClientServerInfo (see gif below for ideas)

![Changing hosting and joining values and seeing the data in ClientServerInfo](/files/-MRexrMunao4NxNG6Obm)

* Now let's update our SendClientGameRpc to include the game name
* Paste the code snippet below into SendClientGameRpc:

```
using AOT;
using Unity.Burst;
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Entities;
using Unity.Collections;
using System.Collections;
using System;

public struct SendClientGameRpc : IRpcCommand
{
    public int levelWidth;
    public int levelHeight;
    public int levelDepth;
    public float playerForce;
    public float bulletVelocity;
    public FixedString64Bytes gameName;
}
```

![Update SendClientGameRpc](/files/-MReyf569qxwBjAsbixQ)

* We need to include the updated information in ServerSendGameSystem
* Paste the code snippet below into ServerSendGameSystem.cs:

```
using Unity.Entities;
using Unity.Jobs;
using Unity.Collections;
using Unity.NetCode;
using UnityEngine;

//This component is only used by this system so we define it in this file
public struct SentClientGameRpcTag : IComponentData
{
}

//This system should only be run by the server (because the server sends the game settings)
//By sepcifying to update in group ServerSimulationSystemGroup it also specifies that it must
//be run by the server
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
[UpdateBefore(typeof(RpcSystem))]
public partial class ServerSendGameSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_Barrier;

    protected override void OnCreate()
    {
        m_Barrier = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
        RequireSingletonForUpdate<GameSettingsComponent>();
        RequireSingletonForUpdate<ServerDataComponent>();
    }

    protected override void OnUpdate()
    {
        var commandBuffer = m_Barrier.CreateCommandBuffer();

        var serverData = GetSingleton<GameSettingsComponent>();
        var gameNameData = GetSingleton<ServerDataComponent>();

        Entities
        .WithNone<SentClientGameRpcTag>()
        .ForEach((Entity entity, in NetworkIdComponent netId) =>
        {
            commandBuffer.AddComponent(entity, new SentClientGameRpcTag());
            var req = commandBuffer.CreateEntity();
            commandBuffer.AddComponent(req, new SendClientGameRpc
            {
                levelWidth = serverData.levelWidth,
                levelHeight = serverData.levelHeight,
                levelDepth = serverData.levelDepth,
                playerForce = serverData.playerForce,
                bulletVelocity = serverData.bulletVelocity,
                gameName = gameNameData.GameName
            });

            commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent {TargetConnection = entity});
        }).Schedule();

        m_Barrier.AddJobHandleForProducer(Dependency);
    }
}
```

![Updating ServerSendGameSystem with new data to send](/files/-MReyjQzWg-b6L0JIlVK)

* Now let's update the ClientLoadGameSystem to update ClientDataComponent with the game name. This will update our Game UI with the game name
  * Paste the code snippet below into ClientLoadGameSystem.cs:

```
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;

//This will only run on the client because it updates in ClientSimulationSystemGroup (which the server does not have)
[UpdateInGroup(typeof(ClientSimulationSystemGroup))]
[UpdateBefore(typeof(RpcSystem))]
public partial class ClientLoadGameSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    protected override void OnCreate()
    {
        //We will be using the BeginSimECB
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //Requiring the ReceiveRpcCommandRequestComponent ensures that update is only run when an NCE exists
        RequireForUpdate(GetEntityQuery(ComponentType.ReadOnly<SendClientGameRpc>(), ComponentType.ReadOnly<ReceiveRpcCommandRequestComponent>()));   
        //This is just here to make sure the Sub Scene is streamed in before the client sets up the level data
        RequireSingletonForUpdate<GameSettingsComponent>();
        RequireSingletonForUpdate<ClientDataComponent>();
    }

    protected override void OnUpdate()
    {

        //We must declare our local variables before using them within a job (.ForEach)
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        var rpcFromEntity = GetBufferFromEntity<OutgoingRpcDataStreamBufferComponent>();
        var gameSettingsEntity = GetSingletonEntity<GameSettingsComponent>();
        var getGameSettingsComponentData = GetComponentDataFromEntity<GameSettingsComponent>();

        Entities
        .ForEach((Entity entity, in SendClientGameRpc request, in ReceiveRpcCommandRequestComponent requestSource) =>
        {
            //This destroys the incoming RPC so the code is only run once
            commandBuffer.DestroyEntity(entity);

            //Check for disconnects before moving forward
            if (!rpcFromEntity.HasComponent(requestSource.SourceConnection))
                return;

            //Set the game size (unnecessary right now but we are including it to show how it is done)
            getGameSettingsComponentData[gameSettingsEntity] = new GameSettingsComponent
            {
                levelWidth = request.levelWidth,
                levelHeight = request.levelHeight,
                levelDepth = request.levelDepth,
                playerForce = request.playerForce,
                bulletVelocity = request.bulletVelocity
            };


            //Here we create a new singleton entity for GameNameComponent
            //We could add this component to the singleton entity that has the GameSettingsComponent
            //but we will keep them separate in case we want to change workflows in the future and don't
            //want these components to be dependent on the same entity
            var gameNameEntity= commandBuffer.CreateEntity();
            commandBuffer.AddComponent(gameNameEntity, new GameNameComponent {
                GameName = request.gameName
            });

            //These update the NCE with NetworkStreamInGame (required to start receiving snapshots) and
            //PlayerSpawningStateComponent, which we will use when we spawn players
            commandBuffer.AddComponent(requestSource.SourceConnection, new PlayerSpawningStateComponent());
            commandBuffer.AddComponent(requestSource.SourceConnection, default(NetworkStreamInGame));
            
            //This tells the server "I loaded the level"
            //First we create an entity called levelReq that will have 2 necessary components
            //Next we add the RPC we want to send (SendServerGameLoadedRpc) and then we add
            //SendRpcCommandRequestComponent with our TargetConnection being the NCE with the server (which will send it to the server)
            var levelReq = commandBuffer.CreateEntity();
            commandBuffer.AddComponent(levelReq, new SendServerGameLoadedRpc());
            commandBuffer.AddComponent(levelReq, new SendRpcCommandRequestComponent {TargetConnection = requestSource.SourceConnection});

        }).Schedule();

        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

![Updating ClientLoadGameSystem to pass through new data](/files/-MReyoOac-AOMYDtsV0L)

* Right-click in the Assets/UI folder and Create a new C# Script named GameOverlayUpdater
* GameOverlapUpdater will be responsible for updating our GameUI overlay and pulling the ClientDataComponent and setting ClientServerInfo GameName
  * It will update the game name and player name shown in the game UI&#x20;
  * It will also be responsible for updating player scores
* Paste the code snippet below into the newly created GameOverlayUpdater.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using Unity.Collections;
using Unity.Jobs;

public class GameOverlayUpdater : MonoBehaviour
{
    //This is how we will grab access to the UI elements we need to update
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;
    private Label m_GameName;
    private Label m_GameIp;
    private Label m_PlayerName;
    private Label m_CurrentScoreText;
    private Label m_HighScoreText;
    private Label m_HighestScoreText;
    
    //We will need ClientServerInfo to update our VisualElements with appropriate valuess
    public ClientServerInfo ClientServerInfo;
    private ClientSimulationSystemGroup m_ClientWorldSimulationSystemGroup;

    //Will check for GameNameComponent
    private EntityQuery m_GameNameComponentQuery;
    private bool gameNameIsSet = false;

    void OnEnable()
    {

        //We set the labels that we will need to update
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_GameName = m_GameManagerUIVE.Q<Label>("game-name");
        m_GameIp = m_GameManagerUIVE.Q<Label>("game-ip");
        m_PlayerName = m_GameManagerUIVE.Q<Label>("player-name");

        //Scores will be updated in a future section
        m_CurrentScoreText = m_GameManagerUIVE.Q<Label>("current-score");
        m_HighScoreText = m_GameManagerUIVE.Q<Label>("high-score");
        m_HighestScoreText = m_GameManagerUIVE.Q<Label>("highest-score");
    }

    // Start is called before the first frame update
    void Start()
    {
        //We set the initial client data we already have as part of ClientDataComponent
        m_GameIp.text = ClientServerInfo.ConnectToServerIp;
        m_PlayerName.text = ClientServerInfo.PlayerName;
        
        //If it is not the client, stop running this script (unnecessary)
        if (!ClientServerInfo.IsClient)
        {
            this.enabled = false;         
        }
        
        //Now we search for the client world and the client simulation system group
        //so we can communicated with ECS in this MonoBehaviour
        foreach (var world in World.All)
        {
            if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
            {
                m_ClientWorldSimulationSystemGroup = world.GetExistingSystem<ClientSimulationSystemGroup>();
                m_GameNameComponentQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<GameNameComponent>());
            }
        }
    }


    // Update is called once per frame
    void Update()
    {
        //We do not need to continue if we do not have a GameNameComponent yet
        if(m_GameNameComponentQuery.IsEmptyIgnoreFilter)
            return;

        //If we have a GameNameComponent we need to update ClientServerInfo and then our UI
        //We only need to do this once so we have a boolean flag to prevent this from being ran more than once
        if(!gameNameIsSet)
        {
                ClientServerInfo.GameName = m_ClientWorldSimulationSystemGroup.GetSingleton<GameNameComponent>().GameName.ToString();
                m_GameName.text = ClientServerInfo.GameName;
                gameNameIsSet = true;
        }
    }
}
```

* We already covered most of the techniques used here (grabbing UI Document, querying for Visual Elements, setting them to values) in the "Creating a ListView" page in the "UI Builder and UI Toolkit" section of the gitbook
  * So, if what we are doing *here* in *this* section is blowing your mind and you don't feel comfortable moving forward, we implore you to visit our earlier section, "UI Builder and UI Toolkit," where we take you step-by-step through UI Builder and UI Toolkit

![Creating GameOverlayUpdater](/files/-MRezWNBRYrQlkSnX4Ug)

* Let's add GameOverlayUpdater as a component to the GameUI GameObject in MainScene
  * Click Add Component in Inspector while GameUI is selected in Hierarchy and add GameOverlayUpdater
* Now let's drag the GameUI GameObject and the ClientServerInfo GameObjects into the appropriate fields on the component and save the scene, then return to NavigationScene

![Updating GameUI GameObject with GameOverlayUpdater](/files/-MRezxfIMKtDDeULlGkb)

* Let's hit play, join a game, and see how our game UI gets updated

![Game UI updating when hosting or joining](/files/-MRf-fNNGFStu5jUtsNQ)

{% hint style="success" %}
We now have our Game UI updated with player and game information

* We updated ClientServerInfo to take in additional data
* We updated ServerDataComponent and ClientDataComponent to take in additional data
* We created GameNameComponent
* We updated ClientServerConnectionHandler to pass through more data from the launch objects
* We updated SendClientGameRpc to include the game name
* Updated ServerSendGameSystem to add the game name to the RPC
* We updated ClientLoadGameSystem to create GameNameComponent when receiving the RPC
* We created GameOverLayUpdater to update the Game UI
  {% endhint %}

## Updating build configurations

Now that we have 2 scenes, we need to update our build configurations so that we can test how our game responds to clients/hosts leaving games.

* Select BaseBuildConfiguration in the Assets/BuildSettings folder in your Project
* Go the Inspector. Under "Scene List" click the drop down icon next to "Scene Infos" and click "+ Add Element"
* Update Element 0 to be NavigationScene (drag "NavigationScene" from your Scenes folder into the Scene field)
* Update Element 1 to be MainScene (drag "MainScene" from your Scenes folder into the Scene field)
* Uncheck "Build Current Scene" at the top of Scene List if it isn't already unchecked
* Uncheck "Auto Load" for Element 0 if it isn't already unchecked
* Click "Apply" at the bottom
* Go to File > Build Settings and click the "Player Settings..." button in the bottom left corner
* When Player is selected on the left, go to the Resolution and Presentation section and set FullScreen Mode to "Windowed"
  * This will make it easier for testing
  * If a scroll bar appears in your window increase the Default Screen Height to 1000px
    * This sometimes happens on retina screens
  * Also make the screen resizeable
* In your Project folder, go to the BuildSettings folder, click on the file with your development platform name (i.e. macOS) then hit "Build and Run" in Inspector

![Updating build settings for our 2 scenes and to be windowed](/files/-MRf0T2weD_6q3kXVWRz)

![Increasing our default window height to 1000px](/files/-MRfPDroq8gxDBi-ubH_)

* Open your Unity editor, hit player, and host a game
* In your running build manually join the game
* In your running build quit the game

![A floating corpse!](/files/-MRfQ216RAhnQzefl6QL)

* Notice that our player has not disappeared from the game when the client disconnected
  * A floating corpse!

{% hint style="success" %}
We have updated our build configurations

* We updated BaseBuildConfiguration
* We updated Player Settings
  {% endhint %}

## **Leaving a game**

These are two ways to handle leaving a game:

1. Hitting the "Quit Game" button at the top of the game UI
2. Timing out (through either quitting the app or losing network connectivity)

When a client or server times out, NetCode automatically adds a "NetworkStreamDisconnected" component to the NCE "on the other side" that is still in the game. So if a client times out, the server gets it on their NCE, if a server times out, the client gets it on their NCE.

> ## Network connection <a href="#network-connection" id="network-connection"></a>
>
> The network connection uses the [Unity Transport package](https://docs.unity3d.com/Packages/com.unity.transport@latest) and stores each connection as an entity. Each connection entity has a [NetworkStreamConnection](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NetworkStreamConnection.html) component with the `Transport` handle for the connection. The connection also has a `NetworkStreamDisconnected` component for one frame, after it disconnects and before the entity is destroyed.
>
> To request disconnect, add a `NetworkStreamRequestDisconnect` component to the entity. Direct disconnection through the driver is not supported. Your game can mark a connection as being in-game, with the `NetworkStreamInGame` component. Your game must do this; it is never done automatically.
>
> From [NetCode's Network connection documentation](https://docs.unity3d.com/Packages/com.unity.netcode@0.50/manual/network-connection.html)

NetCode provides NetworkStreamDisconnect automatically if a client/server times out. We can *also* trigger a NetworkStreamDisconnect automatically by adding a NetworkStreamRequestDisconnect tag. If the client or host clicks the "Quit Game" button on-screen, we can add NetworkStreamRequestDisconnect to inform everyone of the departure. The host must tell all clients. The client only needs to tell the server.

If the client notices a server disconnect, they'll be taken back to the NavigationScene as if they clicked the "Quit Game" button.

We are going to "clean" up any disconnected players on the server if a client leaves by deleting their player entity. We will do this by checking for a "NetworkStreamDisconnected" component on any NCEs.

### Leaving as a client

When a client leaves, it must tell the server "I am leaving, goodbye!" before it goes. (Irish exiting is a great method of leaving parties, but it's not great for keeping a clean server game 👋)

* First, we will add NetworkStreamRequestDisconnect to our client NCE before we leave. This will happen in ClientServerConnectionHandler
  * This will allow the server to follow a clean-up workflow
* We will also update ClientServerConnectionHandler to delete our launch objects
* Paste the code snippet below into ClientServerConnectionHandler.cs:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;

public class ClientServerConnectionHandler : MonoBehaviour
{
    //This is the store of server/client info
    public ClientServerInfo ClientServerInfo;

    //These are the launch objects from Navigation scene that tells what to set up
    private GameObject[] launchObjects;

    //These will gets access to the UI views 
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;

    //We will use these variables for hitting Quit Game
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;
    private World m_ClientWorld;
    private EntityQuery m_NetworkIdComponentQuery;

    void OnEnable()
    {
        //This will put callback on "Quit Game" button
        //This triggers the clean up function (ClickedQuitGame)
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_GameManagerUIVE.Q("quit-game")?.RegisterCallback<ClickEvent>(ev => ClickedQuitGame());
    }

    void Awake()
    {
        launchObjects = GameObject.FindGameObjectsWithTag("LaunchObject");
        foreach(GameObject launchObject in launchObjects)
        {
            ///  
            //Checks for server launch object
            //If it exists it creates ServerDataComponent InitializeServerComponent and
            //passes through server data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ServerLaunchObjectData>() != null)
            {
                //This sets the gameobject server data  in ClientServerInfo (mono)
                ClientServerInfo.IsServer = true;
                ClientServerInfo.GameName = launchObject.GetComponent<ServerLaunchObjectData>().GameName;
                ClientServerInfo.BroadcastIpAddress = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastIpAddress;
                ClientServerInfo.BroadcastPort = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastPort;

                //This sets the component server data in server world(dots)
                //ClientServerConnectionControl (server) will run in server world
                //it will pick up this component and use it to listen on the port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ServerSimulationSystemGroup
                    //we move forward (because that is the server world)
                    if (world.GetExistingSystem<ServerSimulationSystemGroup>() != null)
                    {
                        var ServerDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ServerDataEntity, new ServerDataComponent
                        {
                            GameName = ClientServerInfo.GameName,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows server initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeServerComponent));

                    }
                }
            }

            // 
            //Checks for client launch object
            //If it exists it creates ClientDataComponent, InitializeServerComponent and
            // passes through client data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ClientLaunchObjectData>() != null)
            {
                //This sets the gameobject data in ClientServerInfo (mono)
                ClientServerInfo.IsClient = true;
                ClientServerInfo.ConnectToServerIp = launchObject.GetComponent<ClientLaunchObjectData>().IPAddress;                
                ClientServerInfo.PlayerName = launchObject.GetComponent<ClientLaunchObjectData>().PlayerName;

                //This sets the component client data in server world (dots)
                //ClientServerConnectionControl (client) will run in client world
                //it will pick up this component and use it connect to IP and port
                foreach (var world in World.All)
                {
                    //We cycle through all the worlds, and if the world has ClientSimulationSystemGroup
                    //we move forward (because that is the client world)
                    if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                    {
                        var ClientDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ClientDataEntity, new ClientDataComponent
                        {
                            PlayerName = ClientServerInfo.PlayerName,
                            ConnectToServerIp = ClientServerInfo.ConnectToServerIp,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows client initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeClientComponent));

                        //We will now set the variables we need to clean up during QuitGame()
                        m_ClientWorld = world;
                        m_ClientSimulationSystemGroup = world.GetExistingSystem<ClientSimulationSystemGroup>();
                        m_NetworkIdComponentQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkIdComponent>());

                    }
                }
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
   //This function will navigate us to NavigationScene
    void ClickedQuitGame()
    {
        //If we were able to create an NCE we must add a request disconnect
        if (!m_NetworkIdComponentQuery.IsEmptyIgnoreFilter)
        {
            var clientNCE = m_ClientSimulationSystemGroup.GetSingletonEntity<NetworkIdComponent>();
            m_ClientWorld.EntityManager.AddComponentData(clientNCE, new NetworkStreamRequestDisconnect());

        }

#if UNITY_EDITOR
        if(Application.isPlaying)
#endif
            SceneManager.LoadSceneAsync("NavigationScene");
#if UNITY_EDITOR
        else
            Debug.Log("Loading: " + "NavigationScene");
#endif
    }

    //When the OnDestroy method is called (because of our transition to NavigationScene) we
    //must delete all our entities and our created worlds to go back to a blank state
    //This way we can move back and forth between scenes and "start from scratch" each time
    void OnDestroy()
    {
        for (var i = 0; i < launchObjects.Length; i++)
        {
            Destroy(launchObjects[i]);
        }

        //This query deletes all entities
        World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(World.DefaultGameObjectInjectionWorld.EntityManager.UniversalQuery);
        //This query deletes all worlds
        World.DisposeAllWorlds();

        //We return to our initial world that we started with, defaultWorld
        var bootstrap = new NetCodeBootstrap();
        bootstrap.Initialize("defaultWorld"); 

    }
}
```

![Updating ClientServerConnectionHandler to support client leaving](/files/-MRfRBZFqKp7uh-D0rX5)

* Now we need to create DisconnectSystem in the Server/Systems folder to handle the clean-up of any player entities from disconnected clients
  * Right-click in the Server/Systems folder > Create > C# Script
* Paste the code snippet below into DisconnectSystem.cs:

```
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.NetCode;
using UnityEngine;
using Unity.Physics;
using Unity.Physics.Stateful;

[UpdateInWorld(TargetWorld.Server)]
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
public partial class DisconnectSystem : SystemBase
{
    //We are going to want to playback adding our "DestroyTag" omponent in EndFixedStepSimEcb
    //similar to adding destroy tags from collisions with bullets
    private EndFixedStepSimulationEntityCommandBufferSystem m_CommandBufferSystem;
    
    //We will need a query of all entities with NetworkStreamDisconnected components
    private EntityQuery m_DisconnectedNCEQuery;

    protected override void OnCreate()
    {
        //We set our variables
        m_CommandBufferSystem = World.GetOrCreateSystem<EndFixedStepSimulationEntityCommandBufferSystem>();
        m_DisconnectedNCEQuery = GetEntityQuery(ComponentType.ReadWrite<NetworkStreamDisconnected>());

        //We only need to run this if there are disconnected NCEs
        RequireForUpdate(m_DisconnectedNCEQuery);
    }

    protected override void OnUpdate()
    {

        //We need a command buffer because we are making a structural change (adding a DestroyTag)
        var commandBuffer = m_CommandBufferSystem.CreateCommandBuffer();    

        //There is a dependency on our "ToEntityArrayAsync" because
        //we are depending on this to get done for us to run our .ForEach()
        JobHandle disconnectNCEsDep;

        //We query for all entities that have a NetworkStreamDisconnected component and save it in a native array
        var disconnectedNCEsNative = m_DisconnectedNCEQuery.ToEntityArrayAsync(Allocator.TempJob, out disconnectNCEsDep);
        //We will need to pull the NetworkIdComponent from these entities within our .ForEach so we
        //declare the local variable now
        var getNetworkIdComponentData = GetComponentDataFromEntity<NetworkIdComponent>();

        //We are going to save the JobHandle required from this .ForEach as "cleanPlayersJob"
        //We pass through our native array as read only, and ask .ForEach to dispose of our array on completion
        var cleanPlayersJob = Entities
        .WithReadOnly(disconnectedNCEsNative)
        .WithDisposeOnCompletion(disconnectedNCEsNative)
        .WithAll<PlayerTag>()
        .ForEach((Entity entity, in GhostOwnerComponent ghostOwner) => {

            //We navigate through our disconnected NCE's and see if any player entities match
            for (int i = 0; i < disconnectedNCEsNative.Length; i++)
            {
                if (getNetworkIdComponentData[disconnectedNCEsNative[0]].Value == ghostOwner.NetworkId)
                {
                    //If they do match we add a DestroyTag to delete
                    commandBuffer.AddComponent<DestroyTag>(entity);
                }             
            }
        }).Schedule(JobHandle.CombineDependencies(Dependency, disconnectNCEsDep));

        //We set our Dependency of this sytem to cleanPlayersJob
        Dependency = cleanPlayersJob;

        //And we add our dependency to our command buffer
        m_CommandBufferSystem.AddJobHandleForProducer(Dependency);
    }
}

```

![Creating DisconnectSystem for server to clean up player entities of disconnected NCEs](/files/-MRfRK9hY9iqSPfPaSw-)

* Find your development platform of-choice in the Assets/BuildSettings folder again, then click Build and Run in Inspector

![](/files/-MRfRXAn9nWVgRHkMxM6)

* Now let's host a game in our editor and join the game in the build
* Create a player then quit the game on the running build

![DisconnectSystem destroys player entities that have disconnected](/files/-MRfSXviI8FcfCbw5Rdi)

* Great- now our session can handle clients disconnecting
* Now host a game in the build and join it in the editor
* Quit the game on the running build

![A host quitting does not immediately disconnect the client](/files/-MRfSfirD3IPROvmdTI5)

* We are "stuck" in the game. Pretty lame
* Let's fix that

### Leaving as a host

If the server disconnects, we want our clients to return to NavigationScene. The clients must be "informed" that the server disconnects similar to how that server was "informed" when a client disconnected.

In ClientServerConnectionHandler, we will put in a check on the client to see if its NCE has a "NetworkStreamDisconnected" tag on it.

Also in ClientServerConnectionHandler, we will add to the ClickedQuitGame() method so that if the host clicks a button,  tell all the clients that we disconnected by adding the "NetworkStreamRequestDisconnect" tag.

* Paste the code snippet below into ClientServerConnectionHandler.cs and save:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;
using Unity.Collections;

public class ClientServerConnectionHandler : MonoBehaviour
{
    //This is the store of server/client info
    public ClientServerInfo ClientServerInfo;

    //These are the launch objects from Navigation scene that tells what to set up
    private GameObject[] launchObjects;

    //These will gets access to the UI views 
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;

    //We will use these variables for hitting Quit Game on client or if server disconnects
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;
    private World m_ClientWorld;
    private EntityQuery m_ClientNetworkIdComponentQuery;
    private EntityQuery m_ClientDisconnectedNCEQuery;

    //We will use these variables for hitting Quit Game on server
    private World m_ServerWorld;
    private EntityQuery m_ServerNetworkIdComponentQuery;

    void OnEnable()
    {
        //This will put callback on "Quit Game" button
        //This triggers the clean up function (ClickedQuitGame)
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_GameManagerUIVE.Q("quit-game")?.RegisterCallback<ClickEvent>(ev => ClickedQuitGame());
    }

    void Awake()
    {
        launchObjects = GameObject.FindGameObjectsWithTag("LaunchObject");
        foreach(GameObject launchObject in launchObjects)
        {
            ///  
            //Checks for server launch object
            //If it exists it creates ServerDataComponent InitializeServerComponent and
            //passes through server data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ServerLaunchObjectData>() != null)
            {
                //This sets the gameobject server data  in ClientServerInfo (mono)
                ClientServerInfo.IsServer = true;
                ClientServerInfo.GameName = launchObject.GetComponent<ServerLaunchObjectData>().GameName;
                ClientServerInfo.BroadcastIpAddress = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastIpAddress;
                ClientServerInfo.BroadcastPort = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastPort;

                //This sets the component server data in server world(dots)
                //ClientServerConnectionControl (server) will run in server world
                //it will pick up this component and use it to listen on the port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ServerSimulationSystemGroup
                    //we move forward (because that is the server world)
                    if (world.GetExistingSystem<ServerSimulationSystemGroup>() != null)
                    {
                        var ServerDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ServerDataEntity, new ServerDataComponent
                        {
                            GameName = ClientServerInfo.GameName,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows server initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeServerComponent));

                        //For handling server disconnecting by hitting the quit button
                        m_ServerWorld = world;
                        m_ServerNetworkIdComponentQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkIdComponent>());

                    }
                }
            }

            // 
            //Checks for client launch object
            //If it exists it creates ClientDataComponent, InitializeServerComponent and
            // passes through client data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ClientLaunchObjectData>() != null)
            {
                //This sets the gameobject data in ClientServerInfo (mono)
                ClientServerInfo.IsClient = true;
                ClientServerInfo.ConnectToServerIp = launchObject.GetComponent<ClientLaunchObjectData>().IPAddress;                
                ClientServerInfo.PlayerName = launchObject.GetComponent<ClientLaunchObjectData>().PlayerName;

                //This sets the component client data in server world (dots)
                //ClientServerConnectionControl (client) will run in client world
                //it will pick up this component and use it connect to IP and port
                foreach (var world in World.All)
                {
                    //We cycle through all the worlds, and if the world has ClientSimulationSystemGroup
                    //we move forward (because that is the client world)
                    if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                    {
                        var ClientDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ClientDataEntity, new ClientDataComponent
                        {
                            PlayerName = ClientServerInfo.PlayerName,
                            ConnectToServerIp = ClientServerInfo.ConnectToServerIp,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows client initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeClientComponent));

                        //We will now set the variables we need to clean up during QuitGame()
                        m_ClientWorld = world;
                        m_ClientSimulationSystemGroup = world.GetExistingSystem<ClientSimulationSystemGroup>();
                        m_ClientNetworkIdComponentQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkIdComponent>());
                        //This variable is used to check if the server disconnected
                        m_ClientDisconnectedNCEQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadWrite<NetworkStreamDisconnected>());

                    }
                }
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //The client checks if the NCE has a NetworkStreamDisconnected component
        //If it does we act like they quit the game manually
        if(m_ClientDisconnectedNCEQuery.IsEmptyIgnoreFilter)
            return;
        else
            ClickedQuitGame();
    }

   //This function will navigate us to NavigationScene and connected with the clients/server about leaving
    void ClickedQuitGame()
    {
        //As a client if we were able to create an NCE we must add a request disconnect
        if (!m_ClientNetworkIdComponentQuery.IsEmptyIgnoreFilter)
        {
            var clientNCE = m_ClientSimulationSystemGroup.GetSingletonEntity<NetworkIdComponent>();
            m_ClientWorld.EntityManager.AddComponentData(clientNCE, new NetworkStreamRequestDisconnect());

        }

        //As a server if we were able to create an NCE we must add a request disconnect to all NCEs
        //We must to see if this was a host build
        if (m_ServerWorld != null)
        {
            //First we grab the array of NCEs
            var nceArray = m_ServerNetworkIdComponentQuery.ToEntityArray(Allocator.TempJob);
            for (int i = 0; i < nceArray.Length; i++)
            {
                //Then we add our NetworkStreamDisconnect component to tell the clients we are leaving
                m_ServerWorld.EntityManager.AddComponentData(nceArray[i], new NetworkStreamRequestDisconnect());
            }
            //Then we dispose of our array
            nceArray.Dispose();
        }

#if UNITY_EDITOR
        if(Application.isPlaying)
#endif
            SceneManager.LoadSceneAsync("NavigationScene");
#if UNITY_EDITOR
        else
            Debug.Log("Loading: " + "NavigationScene");
#endif
    }

    //When the OnDestroy method is called (because of our transition to NavigationScene) we
    //must delete all our entities and our created worlds to go back to a blank state
    //This way we can move back and forth between scenes and "start from scratch" each time
    void OnDestroy()
    {
        for (var i = 0; i < launchObjects.Length; i++)
        {
            Destroy(launchObjects[i]);
        }

        //This query deletes all entities
        World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(World.DefaultGameObjectInjectionWorld.EntityManager.UniversalQuery);
        //This query deletes all worlds
        World.DisposeAllWorlds();

        //We return to our initial world that we started with, defaultWorld
        var bootstrap = new NetCodeBootstrap();
        bootstrap.Initialize("defaultWorld"); 

    }
}
```

* Now again: go back to BuildSettings folder, choose your development platform, then click Build and Run in Inspector

![Updating ClientServerConnectionHandler for host leaving and building and running](/files/-MRfUMzHi4_rQyFwIjPU)

* Now Host a Game on the build (the one in the "Sample" window) and the Join the game in the Editor (in Unity)
* Click "Quit Game" in the build version&#x20;

![Disconnected host kicks the client to the NavigationScene](/files/-MRfUe3WMHZztAcKU12w)

If you are running with a non-zero amount of Thin Clients (in PlayMode Tools) you will notice some harmless errors about sending RPCs. This is because we are not gracefully handling Thin Clients when hosts disconnect mid-game. The Thin Client is inputting "spacebar" as the entities/systems are being destroyed causing RPCs to be sent but with no connections. &#x20;

{% hint style="success" %}
We are now able to handle clients or hosts leaving the game

* We updated ClientServerConnectionHandler
* We created DisconnectSystem to be run by the server
  {% endhint %}

## Hitting the "Quit" button on the main menu

Although it may be hard to believe, people might want to quit playing this super basic game 😱so let's add functionality to the "Quit" button at the top of the Title Screen.

* Select the TitleScreenUI GameObject in NavigationScene
* Add a new script as a component called "QuitButtonHandler"
* Move the new script to Assets/UI
* Paste the code snippet below into QuitButtonHandler.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class QuitButtonHandler : MonoBehaviour
{
    //This is the UI Document from the Hierarchy in NavigationScene
    public UIDocument m_TitleUIDocument;
    private VisualElement m_titleScreenManagerVE;
    //Button we will set by querying the parent UI Document
    private Button m_QuitButton;

    void OnEnable()
    {
        //This will put callback on "Quit Game" button
        //This triggers the clean up function (ClickedQuitGame)
        m_titleScreenManagerVE = m_TitleUIDocument.rootVisualElement;
        m_titleScreenManagerVE.Q("quit-button")?.RegisterCallback<ClickEvent>(ev => ClickedQuit());

    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void ClickedQuit()
    {
     // save any game data here
#if UNITY_EDITOR
         // Application.Quit() does not work in the editor so
         // UnityEditor.EditorApplication.isPlaying need to be set to false to end the game
         UnityEditor.EditorApplication.isPlaying = false;
#else
         Application.Quit();
#endif
    }
}
```

![Adding QuitButtonHandler as a component on TitleScreenUI](/files/-MRfWxonCChZPTwcvo_X)

* Now let's drag the TitleScreenUI GameObject from the Hierarchy into the appropriate field in the QuitButtonHandler component
* Let's navigate back to BuildSettings, choose the configuration of your development platform, and Build and Run our game

![Setting QuitButtonHandler component's field and build and run](/files/-MRfX455VefUbQmvEeAe)

* Hit the "Quit" button at the top right&#x20;

![Quit functionality now working](/files/-MRfXiGpx_isn0qGw7pd)

{% hint style="success" %}
We are now able to quit the application

* We created QuitButtonHandler and added it to TitleScreenUI
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Hosting-Joining-and-Leaving-a-Game'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Broadcast a LAN Multiplayer Game

Code and workflows to send and receive broadcast packets to join multiplayer sessions

## What you'll develop on this page

![Broadcasting and joining a game on two different machines that are on the same LAN](/files/-MRi6VncWWIB3Kl9nELN)

Our server will broadcast its IP address over LAN and clients will be able to see all LAN servers broadcasting so they can select and join.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Broadcasting-and-Joining-on-LAN>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Creating UdpConnection

### UdpClient

We are going to create a new class called UdpConnection which will have methods to both send *and* receive UDP broadcast packets. Our implementation works off of [MattijsKneppers](https://forum.unity.com/members/mattijskneppers.650094/)' great start [in the Unity forums](https://forum.unity.com/threads/simple-udp-implementation-send-read-via-mono-c.15900/#post-3645256).

The core component of broadcasting will be [UdpClient from .NET.](https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.udpclient?view=net-5.0) It is important to note that this broadcasting method is not "Unity-approved"; we are using a Microsoft technology to get this done.

On the server side we will create a new UdpClient. We will create a "sendToEndpoint," which will be our broadcasting IP address and broadcasting port. We will then use UdpClient's "send" method to send data over to that endpoint.

{% hint style="info" %}
The broadcasting IP address we recommend is "255.255.255.255"

There is material online that says this is a bad address to use because some routers ignore broadcasts to this address. This has not been the case in our testing.

If you find that IP address to be wonky in your development, or know of a better approach, please let us know [on our Discord](https://discord.com/invite/88j758eUvs).
{% endhint %}

On the client side we will also create a UdpClient. We will bind on the broadcasting port to receive the messages sent in a broadcast at that port. We will then listen for messages.

Doesn't this mean that we'll just be broadcasting to ourselves and as a result we'll just be constantly receiving the messages we just sent, because we are binding and sending on the same port?!

Great point! And that's why we will only create a UdpConnection to broadcast in MainScene (the game) when we are the server. There'll be no clients that will also be broadcasting when joining the server. We will only be "listening" for broadcasts in NavigationScene (title menu).

For testing on the same machine we will make a build, then hardwire a port change for our editor player just to check if it works.

### Thread

Listening for broadcast packets is a "blocking" activity, which means that this activity blocks a thread while it awaits a message.

We do not want to block all of Unity while we listen for broadcast messages. So, we're going to create a separate thread to handle listening for messages. Enter: [Microsoft's Thread class](https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread?view=net-5.0).

Although this approach is also not "sanctioned" by Unity, it has never caused an issue throughout all of our testing.

### Serialized JSON

Finally, what kind of message are we sending?

We will be serializing our data as JSON objects which contain\
\- game name\
\- server IP address\
\- timestamp of when message was sent (server tick)

We will be serializing and deserializing our data using JsonUtility. [This approach is a common pattern shown in the Unity docs](https://docs.unity3d.com/Manual/JSONSerialization.html).

{% hint style="warning" %}

#### NETWORKING IS HARD

We have tested this broadcasting on Windows/Linux/Mac desktops and iOS devices.

The broadcasting/pick up works great in "normal" cases (super majority of cases). "Normal" cases mean when devices are using "built in" wifi or ethernet.

We have gotten mixed results with "wacky" cases. "Wacky" cases are like a desktop computer using a usb dongle for wifi, or like if you are connecting through a VM so everything is "virtualized." In these types of cases, you may experience trouble with broadcasting or picking up broadcasts when using our methods in this gitbook.

Why? Well when machines have a [Network Interface Controller (NIC)](https://en.wikipedia.org/wiki/Network_interface_controller) and additional methods to connect to the internet, it is hard to "automatically" know which IP endpoint to bind to. (Don't worry you don't need to know this stuff, just know "wacky" situations mean it's hard to know the right answer without asking the user).

To handle these "wacky" situations we can: 1) build a UI component into the app that asks the user and potentially add confusion, 2) program a more robust sending mechanism [that sends across all interfaces](https://stackoverflow.com/a/44624161) or, 3) choose the most likely one and hope for the best.

In this gitbook,we went with approach #3. (If it turns out that we chose the wrong option for you all, [please let us know in our Discord ](https://discord.com/invite/88j758eUvs)and we will update).

Additionally, you may find (like we did) a lot of answers on Stack Overflow telling you not to do broadcasting, and instead to do multicasting! It is "way better and recommended!"

Do not listen to those charlatans! You will spend countless hours testing, and retesting, and then after days of using Wireshark, and tracing packets, and learning more than you would ever like about networking data layers and [IGMP packets](https://en.wikipedia.org/wiki/Internet_Group_Management_Protocol), and learn that it is completely unknown how many routers support this functionality and that there is no way to really configure a router to see if it can support it.&#x20;

Oh us? No, no we're not bitter....
{% endhint %}

### UdpConnection

Without further ado...

* Create a new script in the Multiplayer Setup folder called UdpConnection
* Paste this code snippet into UdpConnection.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading;


// 
// "silent hero award" for this implementation
// https://forum.unity.com/threads/simple-udp-implementation-send-read-via-mono-c.15900/#post-3645256
// 
public class UdpConnection
{
    //In this variable we will store our actual udpClient object (from Microsoft)
    private UdpClient udpClient;

    //This is the broadcast address that we will be passed from the server for where to send our messages
    private string sendToIp;
    //This is the broadcast port, we either bind to it and listen on it as a client
    //or we bind to it and SEND to it as the server
    //It actually doesn't matter whhat port we bind on as the server, as long as we send to this port
    //but we decided to just bind to it as well to keep track of less numbers (it does mean we need to do)
    private int sendOrReceivePort;
 
    //This is a Queue of our messages (where we store received broadcast messages)
    private readonly Queue<string> incomingQueue = new Queue<string>();
    //This is the thread we will start to "listen" on
    Thread receiveThread;
    //We need to know if we were listening on a thread so we know whether to turn it off when we don't need it
    //If we are the server this will stay false
    private bool threadRunning = false;
    //The server will need to find its IP address so it can send it out to clients
    private IPAddress m_MyIp;

    //We call this method as a way to initialize our UdpConnection
    //We pass through the broadcast address (sendToIp) and the broadcast port (sendOrReceivePort)
    public void StartConnection(string sendToIp, int sendOrReceivePort)
    {
        //We create our udpClient by binding it to the sendOrReceivePort
        //Binding to the broadcast port really only matters if you are a client listening for our broadcast messages
        //The server could actually bind to any port
        try { udpClient = new UdpClient(sendOrReceivePort); }
        catch (Exception e)
        {
            Debug.Log("Failed to listen for UDP at port " + sendOrReceivePort + ": " + e.Message);
            return;
        }
        // "best tip of all time award" to MichaelBluestein
        // https://forums.xamarin.com/discussion/comment/1206/#Comment_1206
        // somehow you get IP address
        foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces()) {
            if (netInterface.OperationalStatus == OperationalStatus.Up &&  
                netInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
                netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet) {
                foreach (var addrInfo in netInterface.GetIPProperties().UnicastAddresses) {
                    if (addrInfo.Address.AddressFamily == AddressFamily.InterNetwork) {

                        //We will use this address to broadcast out to clients as the server
                        m_MyIp = addrInfo.Address;
                    }
                }
            }  
        }
        //Now we configure our udpClient to be able to broadcast
        udpClient.EnableBroadcast = true;

        //We set our broadcast IP and broadcast port
        this.sendToIp = sendToIp;
        this.sendOrReceivePort = sendOrReceivePort;
    }
 
    //This will only be called by the client in order to start "listening"
    public void StartReceiveThread()
    {
        //We create our new thread that be running the method "ListenForMessages"
        receiveThread = new Thread(() => ListenForMessages(udpClient));
        //We configure the thread we just created
        receiveThread.IsBackground = true;
        //We note that it is running so we don't forget to turn it off
        threadRunning = true;
        //Now we start the thread
        receiveThread.Start();
    }
 
    //This method is called by StartReceiveThread()
    private void ListenForMessages(UdpClient client)
    {
        //We create our listening endpoint
        IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
 
        //We will continue running this until we turn "threadRunning" to false (which is when we don't need to listen anymore)
        while (threadRunning)
        {
            try
            {
                //A little console log to know we have started listening
                Debug.Log("starting receive on " + m_MyIp.ToString() +" and port " +sendOrReceivePort.ToString());
                
                // Blocks until a message returns on this socket from a remote host.
                Byte[] receiveBytes = client.Receive(ref remoteIpEndPoint);
                //We grab our byte stream as UTF8 encoding
                string returnData = Encoding.UTF8.GetString(receiveBytes);
                
                //We enqueue our received byte stream 
                lock (incomingQueue)
                {
                    incomingQueue.Enqueue(returnData);
                }
            }
            //Error handling
            catch (SocketException e)
            {
                // 10004 thrown when socket is closed
                if (e.ErrorCode != 10004) Debug.Log("Socket exception while receiving data from udp client: " + e.Message);
            }
            catch (Exception e)
            {
                Debug.Log("Error receiving data from udp client: " + e.Message);
            }

            //We take a pause after receiving a message and run it again
            Thread.Sleep(1);
        }
    }
 
    //This is another method the client will call to grab all the messages that have been received by listening
    public ServerInfoObject[] getMessages()
    {
        //We created an array of pending messages
        string[] pendingMessages = new string[0];
        //We create an array where we will store our ServerInfoObjects (how we store the JSON)
        ServerInfoObject[] pendingServerInfos = new ServerInfoObject[0];
        //While we get this done we need to lock the Queue
        lock (incomingQueue)
        {
            //We set our pending messages the length of our queue of byte stream
            pendingMessages = new string[incomingQueue.Count];
            //We set our pending server infos the length of our queue of byte stream
            pendingServerInfos = new ServerInfoObject[incomingQueue.Count];
            
            //We will go through all our messages and update to get an array of ServerInfoObjects
            int i = 0;
            while (incomingQueue.Count != 0)
            {
                //We start moving data from the queue to our pending messages
                pendingMessages[i] = incomingQueue.Dequeue();
                //We then take our pending message
                string jsonObject = pendingMessages[i];
                //And use FromJson to create our array of ServerInfoObjects
                pendingServerInfos[i] = JsonUtility.FromJson<ServerInfoObject>(jsonObject);
                i++;
            }
        }
        //We return an array of ServerInfoObjects to the client that called this method
        return pendingServerInfos;
    }

    //We will only call this method on the server and provide it the game name and time
    //We don't provide the game name in StartConnection because the client won't have it and 
    //both the client and server use StartConnection
    public void Send(float floatTime, string gameName)
    {
        //All our values need to be string to be able to be serialized
        string stringTime = floatTime.ToString();

        //We need to create our destination endpoint
        //It will be at the provided broadcast IP address and port
        IPEndPoint sendToEndpoint = new IPEndPoint(IPAddress.Parse(sendToIp), sendOrReceivePort);

        //We create a new ServerInfoObject which we will use to store our data
        ServerInfoObject thisServerInfoObject = new ServerInfoObject();
        //We populate our ServerInfoObject with data
        thisServerInfoObject.gameName = gameName;
        thisServerInfoObject.ipAddress = m_MyIp.ToString();
        thisServerInfoObject.timeStamp = stringTime;

        //Then we turn it into JSON
        string json = JsonUtility.ToJson(thisServerInfoObject);
        //Then we create a sendBytes array the size of the bytes of the JSON
        Byte[] sendBytes = Encoding.UTF8.GetBytes(json);
        //We then call send method on udpClient to send our byte array
        udpClient.Send(sendBytes, sendBytes.Length, sendToEndpoint);
    }
 
    public void Stop()
    {
        // Not always UdpClients are used for listening
        // Which is what requires the running thread to listen
        if (threadRunning == true)
        {
            threadRunning = false;
            receiveThread.Abort();
        }
        udpClient.Close();
        udpClient.Dispose();
    }
}

//This is our ServerInfoObject that we will be using to help us send data as JSON
[Serializable]
public class ServerInfoObject
{
    public string gameName = "";
    public string ipAddress = "";
    public string timeStamp = "";

}
```

![Creating UdpConnection](/files/-MRhkpQKYVBhOPne-YEQ)

{% hint style="success" %}
We have a class that allows either the server to send messages or a client to receive messages

* We created UdpConnection
  {% endhint %}

## Broadcasting from the server

Now that we have UdpConnection we need to create a script to pull the broadcast IP address, port, and game name from ClientServerInfo and send a broadcast message&#x20;

* Navigate to MainScene, right-click on the Hierarchy, and create an empty GameObject called GameBroadcasting
* With GameBroadcasting selected in Hierarchy, click "Add Component" in the Inspector and make a new script called "GameServerBroadcasting"
  * Move GameServerBroadcasting into the Multiplayer Setup folder

![Creating GameServerBroadcasting in GameBroadcasting](/files/-MRhktk1JbEEAOaPdgXq)

* Paste the code snippet below into GameServerBroadcasting.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading;

public class GameServerBroadcasting : MonoBehaviour
{
    //We will be using our UdpConnection class to send messages
    private UdpConnection connection;

    //This will decide how often we send a broadcast messages
    //We found that sending a broadcast message once every 2 seconds worked well
    //you don't want to flood the network with broadcast packets
    public float perSecond = .5f;
    private float nextTime = 0;

    //We will pull in game and broadcast information through ClientServerInfo
    public ClientServerInfo ClientServerInfo;
 
    void Start()
    {
        //We only need to run this if we are the server, otherwise disable
        if (!ClientServerInfo.IsServer)
        {
            this.enabled = false;         
        }

        //Get the broadcasting address and port from ClientServerInfo
        string sendToIp = ClientServerInfo.BroadcastIpAddress;
        int sendToPort = ClientServerInfo.BroadcastPort;
 
        //First we create our class
        connection = new UdpConnection();
        //Then we run the initialization method and provide the broadcast IP address and port
        connection.StartConnection(sendToIp, sendToPort);
    }
 
    void Update()
    {
        //We check if it is time to send another broadcast
        if (Time.time >= nextTime)
        {
            //If it is we provide the Send method the game name and time
            //These will be bundled with the server's IP address (which is generated in StartConnection)
            //to be included in the broadcast packet
            connection.Send(nextTime, ClientServerInfo.GameName);
            nextTime += (1/perSecond);
        }
    }

    void OnDestroy()
    {
        //If the server destroys this scene (by returning to NavigationScene) we will call the clean up method
        connection.Stop();
    }
}
```

![Updating GameServerBroadcasting](/files/-MRhl0yKxUKYKB1IV-Ed)

* Now select GameBroadcasting in Hierarchy, and drag the ClientServerInfo GameObject (also in Hierarchy) into the appropriate field in the GameServerBroadcasting component in Inspector

![Updating GameBroadcasting GameObject](/files/-MRhl5UEAgb7aJI5nzFi)

{% hint style="success" %}
Now the server can send broadcast messages when in MainScene

* We created a new GameBroadcasting GameObject in MainScene
* We added GameServerBroadcasting component
  {% endhint %}

## Listening and joining on the client

We are going to need to listen for the broadcast messages in NavigationScene. Instead of having LocalGamesFinder search for GameObjects, we will update LocalGamesFinder to listen for broadcast messages.

We will then need to update what happens when we click on the list item, so we need to populate JoinGameScreen with the data sent in the broadcast packet.

* Navigate to NavigationScene and then open LocalGamesFinder
* Paste the code snippet below into LocalGamesFinder.cs:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor;

public class LocalGamesFinder : MonoBehaviour
{
    //We will be pulling in our SourceAsset from TitleScreenUI GameObject so we can reference Visual Elements
    public UIDocument m_TitleUIDocument;

    //When we grab the rootVisualElement of our UIDocument we will be able to query the TitleScreenManager Visual Element
    private VisualElement m_titleScreenManagerVE;

    //We will query for our TitleScreenManager cVE by its name "TitleScreenManager"
    private TitleScreenManager m_titleScreenManagerClass;

    //Within TitleScreenManager (which is everything) we will query for our list-view by name
    //We don't have to query for the TitleScreen THEN list-view because it is one big tree of elements
    //We can call any child from the parent, very convenient! But you must be mindful about being dilligent about
    //creating unique names or else you can get back several elements (which at times is the point of sharing a name)
    private ListView m_ListView;

    //This is where we will store our received broadcast messages
    private List<ServerInfoObject> discoveredServerInfoObjects = new List<ServerInfoObject>();

    //This is our ListItem uxml that we will drag to the public field
    //We need a reference to the uxml so we can build it in makeItem
    public VisualTreeAsset m_localGameListItemAsset;

    //These variables are used in Update() to pace how often we check for GameObjects
    public float perSecond = 1.0f;
    private float nextTime = 0; 

    ///The broadcast ip address and port to be used by the server across the LAN
    public string BroadcastIpAddress = "255.255.255.255";
    public ushort BroadcastPort = 8014;

    //We will be storing our UdpConnection class as connection
    private UdpConnection connection;

    void OnEnable()
    {
        //Here we grab the SourceAsset rootVisualElement
        //This is a MAJOR KEY, really couldn't find this key step in information online
        //If you want to reference your active UI in a script make a public UIDocument variable and 
        //then call rootVisualElement on it, from there you can query the Visual Element tree by names
        //or element types
        m_titleScreenManagerVE = m_TitleUIDocument.rootVisualElement;
        //Here we grab the TitleScreenManager by querying by name
        m_titleScreenManagerClass = m_titleScreenManagerVE.Q<TitleScreenManager>("TitleScreenManager");
        //From within TitleScreenManager we query local-games-list by name
        m_ListView = m_titleScreenManagerVE.Q<ListView>("local-games-list");

    }

    // Start is called before the first frame update
    void Start()
    {
        //First we pull our broadcast IP address and port from our source of truth, which is this component
        //We actually don't need the broadcast IP address when listening but we provide it anyway because the method
        //requires both arguments (we could provide any IP address and it wouldn't matter, only the server needs to provide the right one)
        string broadcastIp = BroadcastIpAddress;
        //This is the port we will be listening on (this has to be the same as the port the server is sending on)
        int receivePort = BroadcastPort;
 
        //Next we create our UdpConnection class
        connection = new UdpConnection();
        //We provide our broadcast IP adress and port
        connection.StartConnection(broadcastIp, receivePort);
        //Then we start our receive thread which means "listen"
        //This will start creating our queue of received messages that we will call in update
        connection.StartReceiveThread();
        
        
        // The three spells you must cast to conjure a list view
        m_ListView.makeItem = MakeItem;
        m_ListView.bindItem = BindItem;
        m_ListView.itemsSource = discoveredServerInfoObjects;

    }

    private VisualElement MakeItem()
    {
        //Here we take the uxml and make a VisualElement
        VisualElement listItem = m_localGameListItemAsset.CloneTree();
        return listItem;

    }

    private void BindItem(VisualElement e, int index)
    {
        //We add the game name to the label of the list item
        e.Q<Label>("game-name").text = discoveredServerInfoObjects[index].gameName;

        //Here we create a call back for clicking on the list item and provide data to a function
        e.Q<Button>("join-local-game").RegisterCallback<ClickEvent>(ev => ClickedJoinGame(discoveredServerInfoObjects[index]));

    }

    void ClickedJoinGame(ServerInfoObject localGame)
    {
        //We query our JoinGameScreen cVE and call a new function LoadJoinScreenForSelectedServer and pass our GameObject
        //This is an example of clicking a list item and passing through data to a new function with that click
        //You will see in our JoinGameScreen cVE that we use this data to fill labels in the view
        m_titleScreenManagerClass.Q<JoinGameScreen>("JoinGameScreen").LoadJoinScreenForSelectedServer(localGame);

        //We then call EnableJoinScreen on our TitleScreenManager cVE (which displays JoinGameScreen)
        m_titleScreenManagerClass.EnableJoinScreen();

    }
  
    // Update is called once per frame
    void Update()
    {
        if (Time.time >= nextTime)
        {   
            //We grab our array of ServerInfoObjects from our UdpConnection class
            foreach (ServerInfoObject serverInfo in connection.getMessages())
            {
                //We call ReceivedServerInfo so we can check if this ServerInfoObject contains new information
                //We don't use it immediatly and add it to our list because it might already be in the list
                ReceivedServerInfo(serverInfo);
            }
            //We increment
            nextTime += (1/perSecond);
        }
    }

    void ReceivedServerInfo(ServerInfoObject serverInfo)
    {
        //Filter to see if this ServerInfoObject matches with previous broadcasts
        //We will start by thinking that it does not exist
        bool ipExists = false;

        foreach (ServerInfoObject discoveredInfo in discoveredServerInfoObjects)
        {
            //Check if this discovered ip address is already known
            if (serverInfo.ipAddress == discoveredInfo.ipAddress)
            {
                ipExists = true;

                //If a ServerInfoObject with this IP address has been discovered, when did we hear about it?
                float receivedTime = float.Parse(serverInfo.timeStamp);
                //What about this broadcast?
                float storedTime = float.Parse(discoveredInfo.timeStamp);

                //We will update to the latest information from the IP address that has been broadcast
                //The host might have quit and started a new game and we want to display the latest info
                if (receivedTime > storedTime)
                {
                    //Set the data to the new data
                    discoveredInfo.gameName = serverInfo.gameName;
                    discoveredInfo.timeStamp = serverInfo.timeStamp;
                    //Now we need to update the table
                    m_ListView.Refresh();
                }
            }

        }
        //If the ip didn't already exist, add it to the known list
        if (!ipExists)
        {
            //We add it to the list
            discoveredServerInfoObjects.Add(serverInfo);
            //We refresh our list to display the new data
            m_ListView.Refresh();
        }
    }

    //We must call the clean up function on UdpConnection or else the thread will keep running!
    void OnDestroy()
    {
        connection.Stop();
    }
}
```

![Updating LocalGamesFinder to listen for messages](/files/-MRhllaOfCtAc6M93k6A)

* Now that we have updated LocalGamesFinder to update the list with the latest unique broadcasts we need to update JoinGameScreen custom Visual Element (cVE) to take in the new passed-through ServerInfoObject data
* Paste the code snippet below into JoinGameScreen.cs (cVE):

```
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.SceneManagement;

public class JoinGameScreen : VisualElement
{
    Label m_GameName;
    Label m_GameIp;
    TextField m_PlayerName;
    String m_HostName = "";
    IPAddress m_MyIp;

    public new class UxmlFactory : UxmlFactory<JoinGameScreen, UxmlTraits> { }

    public JoinGameScreen()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        // 
        // PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
        // 
        m_GameName = this.Q<Label>("game-name");
        m_GameIp = this.Q<Label>("game-ip");
        m_PlayerName = this.Q<TextField>("player-name");

        //Grab the system name
        m_HostName = Dns.GetHostName();
        //Set the value equal to the host name to start
        m_PlayerName.value = m_HostName;

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    public void LoadJoinScreenForSelectedServer(ServerInfoObject localGame)
    {

        m_GameName = this.Q<Label>("game-name");
        m_GameIp = this.Q<Label>("game-ip");
        m_GameName.text = localGame.gameName;
        m_GameIp.text = localGame.ipAddress;
    }
}
```

* Now delete all the LocalGame GameObjects in the NavigationScene Hierarchy
* Save the NavigationScene
* Navigate to the BuildSettings folder, select the configuration file of your development platform (i.e. "macOS - Build"), then go to Inspector and hit Build and Run

![](/files/-MRhsWobQMVUWxIT2zAz)

* Change the port in line 47 in UdpConnection to 9001
  * We make this change so that we don't bind on same port when testing

```
try { udpClient = new UdpClient(9001); }
```

* Hit play in the editor
* Host a game in the editor and check out the build
  * Remember to host in the **editor, not the build, when testing**
  * The editor must send to the hardwired broadcast port

![Changing the used port in the editor and hosting a game](/files/-MRhxyFWdyysDK1dWMkn)

* Great, we see our broadcasted game
* In the editor: quit the game, and then start a new game with a new name

![](/files/-MRhy83egpWKLI84NBP6)

* We can see our game name change in our build
* In the build: click on the broadcasted game and join the game

![Joining a broadcasted game](/files/-MRhyhSdWFV_hMwdVxWy)

* Undo the port change in UdpConnection and we are set!
  * If you want to test again you will need to build then change like we did in this section

{% hint style="success" %}
&#x20;We are now able to join broadcasted games

* We updated LocalGamesFinder
* We updated JoinGameScreen cVE
* We updated the port in UdpConnection for testing
* We built our project and was able to broadcast and join a game
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Broadcasting-and-Joining-on-LAN'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Keep Score and Update Game UI

Code and workflows to keep score between players in a multiplayer game on a LAN

## What you'll develop on this page

![Server keeping score and sending the score to clients to update their UI](/files/-MS0ciCRcK3jOkMJjQVI)

The server will set and adjust player scores based on bullet collisions. It will also track the highest score.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Score-Keeping>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## How we'll be keeping score

### Scoring

1 point for shooting asteroids (as many as you can get through before disappear)

10 points for  shooting a player

### How the server will keep score

Server will be keeping score by updating ghosted HighScore ghosts and a single HighestScore ghost. When players join, a new object is created.

HighestScore is pre-spawned in ConvertedScene empty (does it have to be prefab?)

PlayerScores are created when a new player joins.

### Recycling HighScore ghosts

NCEs re-use network ids, so we will need to keep track of this.

If a player joins the session and it is re-using a network id, we clear that player score

## Server creating scores

We are going to start by setting up our HighestScore in ConvertedSubScene.

* First, let's create the component we will be referencing and updating to keep track of the server's highest score
* Create HighestScoreComponent in the Mixed/Components folder
* Paste the code snippet below into HighestScoreComponent.cs:

```
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;

[GenerateAuthoringComponent]
public struct HighestScoreComponent : IComponentData
{
    [GhostField]
    public FixedString64Bytes playerName;

    [GhostField]
    public int highestScore;
}
```

![](/files/-MS0QA3PJbXGnt-AJJqX)

* Navigate to ConvertedSubScene and create an empty GameObject called HighestScore
* Add a GhostAuthoringComponent (by clicking Add Component button in Inspector when HighestScore is selected in Hierarchy)
  * Name = HighestScore
  * Importance = 500
  * Supported Ghost Mode = Interpolated
  * Optimization Mode = Static
* Next add HighestScoreComponent
* When done with those steps, drag HighestScore GameObject from Hierarchy into Scripts and Prefabs

![Creating HighScore GameObject and prefab](/files/-MS0QDudnR8CEC-4EpU7)

* Even though we will not make another HighestScore, NetCode requires that the ghosted GameObject be a prefab
  * [That's because a ghost cannot be made unless it is currently a prefab](https://forum.unity.com/threads/dots-multiplayer-discussion.694669/page-6#post-6766174)
* Now let's create the PlayerScore ghosts that we will be using to keep track of individual player scores
* Create PlayerScoreAuthoringComponent in Server/Components
* Paste the code snippet below into PlayerScoreAuthoringComponent.cs:&#x20;

```
using Unity.Entities;

[GenerateAuthoringComponent]
public struct PlayerScoreAuthoringComponent : IComponentData
{
    public Entity Prefab;
}
```

![Creating PlayerScoreAuthoringComponent](/files/-MS0QHn4RvO-JUqmNKbs)

* Next let's create the component that will be storing the actual player data named PlayerScoreComponent in the Mixed/Components folder
* Paste the code snippet below into PlayerScoreComponent.cs:

```
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Burst;
using Unity.Entities;
using Unity.Collections;

[GenerateAuthoringComponent]
public struct PlayerScoreComponent : IComponentData
{
    [GhostField]
    public int networkId;
    [GhostField]
    public FixedString64Bytes playerName;
    [GhostField]
    public int currentScore;
    [GhostField]
    public int highScore;
}
```

![Creating PlayerScoreComponent](/files/-MS0SX93WOKaZztl7Mpv)

* Create an empty GameObject called PlayerScore in the Hierarchy (doesn't matter which one)
* Add PlayerScoreComponent to PlayerScore
* Add GhostAuthoringComponent
  * Name = PlayerScore
  * Importance = 500
  * Supported Ghost Modes = Interpolated
  * Optimization Mode = Static
* Drag it into Scripts and Prefabs
* Delete it from the hierarchy

![Creating PlayerScore prefab](/files/-MS0S_ti8yKOFVyUinGw)

* Navigate to ConvertedSubScene and add PlayerScoreAuthoringComponent to the PrefabCollection GameObject
* Drag the PlayerScore prefab from the Scripts and Prefabs folder into the "Prefab" field in the Player Score Authoring Component in Inspector when PrefabCollection is selected in Hierarchy

![Updating PrefabCollection with PlayerScore](/files/-MS0SgpSd0RwsA6Z2-rE)

* Now we have our ghosts ready
  * HighestScore is part of the ConvertedSubScene
  * PlayerScore is referenced as part of PrefabCollection

We are going to kick off the process of setting up scores by adding a new RPC to be sent in ClientLoadGameSystem.

We *could* add this flow to existing flows that are kicked off by ClientLoadGameSystem (like SendServerGameLoadedRpc), but we are going to keep it separate to keep this flow concerned only with score setup.

* Let's create SendServerPlayerNameRpc in the Mixed/Commands folder
* Paste the code snippet below into SendServerPlayerNameRpc.cs:

```
using AOT;
using Unity.Burst;
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Entities;
using Unity.Collections;
using System.Collections;
using System;

public struct SendServerPlayerNameRpc : IRpcCommand
{
    public FixedString64Bytes playerName;
}
```

![Creating SendServerPlayerNameRpc](/files/-MS0TzqkX_Mj-3-AMlsa)

* Now let's update ClientLoadGameSystem to send this RPC as part of the game loading process
* Paste the code snippet below into ClientLoadGameSystem.cs:

```
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;

//This will only run on the client because it updates in ClientSimulationSystemGroup (which the server does not have)
[UpdateInGroup(typeof(ClientSimulationSystemGroup))]
[UpdateBefore(typeof(RpcSystem))]
public partial class ClientLoadGameSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    protected override void OnCreate()
    {
        //We will be using the BeginSimECB
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //Requiring the ReceiveRpcCommandRequestComponent ensures that update is only run when an NCE exists and a SendClientGameRpc has come in
        RequireForUpdate(GetEntityQuery(ComponentType.ReadOnly<SendClientGameRpc>(), ComponentType.ReadOnly<ReceiveRpcCommandRequestComponent>()));   
        //This is just here to make sure the Sub Scene is streamed in before the client sets up the level data
        RequireSingletonForUpdate<GameSettingsComponent>();
        //We will make sure we have our ClientDataComponent so we can send the server our player name
        RequireSingletonForUpdate<ClientDataComponent>();
    }

    protected override void OnUpdate()
    {

        //We must declare our local variables before using them within a job (.ForEach)
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        var rpcFromEntity = GetBufferFromEntity<OutgoingRpcDataStreamBufferComponent>();
        var gameSettingsEntity = GetSingletonEntity<GameSettingsComponent>();
        var getGameSettingsComponentData = GetComponentDataFromEntity<GameSettingsComponent>();
        var clientData = GetSingleton<ClientDataComponent>(); //We will use this to send the player name to server

        Entities
        .ForEach((Entity entity, in SendClientGameRpc request, in ReceiveRpcCommandRequestComponent requestSource) =>
        {
            //This destroys the incoming RPC so the code is only run once
            commandBuffer.DestroyEntity(entity);

            //Check for disconnects before moving forward
            if (!rpcFromEntity.HasComponent(requestSource.SourceConnection))
                return;

            //Set the game size (unnecessary right now but we are including it to show how it is done)
            getGameSettingsComponentData[gameSettingsEntity] = new GameSettingsComponent
            {
                levelWidth = request.levelWidth,
                levelHeight = request.levelHeight,
                levelDepth = request.levelDepth,
                playerForce = request.playerForce,
                bulletVelocity = request.bulletVelocity
            };


            //Here we create a new singleton entity for GameNameComponent
            //We could add this component to the singleton entity that has the GameSettingsComponent
            //but we will keep them separate in case we want to change workflows in the future and don't
            //want these components to be dependent on the same entity
            var gameNameEntity= commandBuffer.CreateEntity();
            commandBuffer.AddComponent(gameNameEntity, new GameNameComponent {
                GameName = request.gameName
            });

            //These update the NCE with NetworkStreamInGame (required to start receiving snapshots) and
            //PlayerSpawningStateComponent, which we will use when we spawn players
            commandBuffer.AddComponent(requestSource.SourceConnection, new PlayerSpawningStateComponent());
            commandBuffer.AddComponent(requestSource.SourceConnection, default(NetworkStreamInGame));
            
            //This tells the server "I loaded the level"
            //First we create an entity called levelReq that will have 2 necessary components
            //Next we add the RPC we want to send (SendServerGameLoadedRpc) and then we add
            //SendRpcCommandRequestComponent with our TargetConnection being the NCE with the server (which will send it to the server)
            var levelReq = commandBuffer.CreateEntity();
            commandBuffer.AddComponent(levelReq, new SendServerGameLoadedRpc());
            commandBuffer.AddComponent(levelReq, new SendRpcCommandRequestComponent {TargetConnection = requestSource.SourceConnection});

            // this tells the server "This is my name and Id" which will be used for player score tracking
            var playerReq = commandBuffer.CreateEntity();
            commandBuffer.AddComponent(playerReq, new SendServerPlayerNameRpc {playerName = clientData.PlayerName});
            commandBuffer.AddComponent(playerReq, new SendRpcCommandRequestComponent {TargetConnection = requestSource.SourceConnection});

        }).Schedule();

        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

![Updating ClientLoadLevelSystem](/files/-MS0UROwV5KRFmCuNQOM)

* Although there will be only one HighestScore entity, there will be a PlayerScore created for each unique NCE network id value
  * Remember that NetCode recycles network id's once players have disconnected
  * So a player may have its score tracked during gameplay, then leave the game, and then a new player may join with that same network id (i.e. it's been 'recycled')
  * So we have to set up a process that checks our existing PlayerScores network id values to see if they match the new SendServerPlayerNameRpc's NCE Network id
    * If they do match, we will reset that PlayerScore to 0 and update it with the sent RPCs provided name
* Create SetupScoreSystem in the Server/Systems folder
* Paste the code snippet below into SetupScoreSystem.cs:

```
using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;
using Unity.Burst;
using Unity.Jobs;

//The server will be keeping score
//The client will only read the scores and update overlay
[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
public partial class SetupScoreSystem : SystemBase
{
    //We will be making structural changes so we need a command buffer
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //This will be the query for the highest score
    private EntityQuery m_HighestScoreQuery;

    //This will be the query for the player scores
    private EntityQuery m_PlayerScoresQuery;

    //This will be the prefab used to create PlayerScores
    private Entity m_Prefab;

    protected override void OnCreate()
    {
        //We set the command buffer
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //This will be used to check if there is already HighestScore (initialization)
        m_HighestScoreQuery = EntityManager.CreateEntityQuery(ComponentType.ReadOnly<HighestScoreComponent>());

        //This will be used to check if there are already PlayerScores (initialization)
        m_PlayerScoresQuery = EntityManager.CreateEntityQuery(ComponentType.ReadOnly<PlayerScoreComponent>());

        //We are going to wait to initialize and update until the first player connects and sends their name
        RequireForUpdate(GetEntityQuery(ComponentType.ReadOnly<SendServerPlayerNameRpc>(), ComponentType.ReadOnly<ReceiveRpcCommandRequestComponent>()));        
    }

    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_Prefab == Entity.Null)
        {
            //We grab the converted PrefabCollection Entity's PlayerScoreAuthoringComponent
            //and set m_Prefab to its Prefab value
            m_Prefab = GetSingleton<PlayerScoreAuthoringComponent>().Prefab;
            //We then initialize by creating the first PlayerScore
            var initialPlayerScore = EntityManager.Instantiate(m_Prefab);
            //We set the initial player score to 1 so the first player will be assigned this PlayerScore
            EntityManager.SetComponentData<PlayerScoreComponent>(initialPlayerScore, new PlayerScoreComponent{
                networkId = 1
            });
            //we must "return" after setting this prefab because if we were to continue into the Job
            //we would run into errors because the variable was JUST set (ECS funny business)
            //comment out return and see the error
            return;
        }
        
        //We need to declare our local variables before the .ForEach()
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        //We use this to check for disconnects
        var rpcFromEntity = GetBufferFromEntity<OutgoingRpcDataStreamBufferComponent>();
        //We are going to grab all existing player scores because we need to check if the new player has an old NetworkId
        var currentPlayerScoreEntities = m_PlayerScoresQuery.ToEntityArray(Allocator.TempJob);
        //We are going to need to grab the Player score from the entity
        var playerScoreComponent = GetComponentDataFromEntity<PlayerScoreComponent>();
        //We grab the prefab in case we need to create a new PlayerScore for a new NetworkId
        var scorePrefab = m_Prefab;
        //We are going to need to be able to grab the NetworkIdComponent from the RPC source to know what the player's NetworkId is
        var networkIdFromEntity = GetComponentDataFromEntity<NetworkIdComponent>();
        
        Entities
        .WithDisposeOnCompletion(currentPlayerScoreEntities)
        .ForEach((Entity entity, in SendServerPlayerNameRpc request, in ReceiveRpcCommandRequestComponent requestSource) =>
        {
            //Delete the rpc
            commandBuffer.DestroyEntity(entity);
            
            //Check for disconnects
            if (!rpcFromEntity.HasComponent(requestSource.SourceConnection))
                return;

            //Grab the NetworkIdComponent's Value
            var newPlayersNetworkId = networkIdFromEntity[requestSource.SourceConnection].Value;

            //We create a clean PlayerScore component with the player's name and the player's NetworkId value
            var newPlayerScore = new PlayerScoreComponent{
                networkId = newPlayersNetworkId,
                playerName = request.playerName,
                currentScore = 0,
                highScore = 0
            };

            //Now we are going to check all current PlayerScores and see if this NetworkId has been used before
            //If it has we set it to our new PlayerScoreComponent
            bool uniqueNetworkId = true;
            for (int i = 0; i < currentPlayerScoreEntities.Length; i++)
            {
                //We call the data componentData just to make it more legible on the if() line
                var componentData = playerScoreComponent[currentPlayerScoreEntities[i]];
                if(componentData.networkId == newPlayersNetworkId)
                {
                    commandBuffer.SetComponent<PlayerScoreComponent>(currentPlayerScoreEntities[i], newPlayerScore);
                    uniqueNetworkId = false;
                }
                
            }
            //If this NetworkId has not been used before we create a new PlayerScore
            if (uniqueNetworkId)
            {
                var playerScoreEntity = commandBuffer.Instantiate(scorePrefab);
                //We set the initial player score to 1 so the first player will be assigned this PlayerScore
                commandBuffer.SetComponent<PlayerScoreComponent>(playerScoreEntity, newPlayerScore);
            }
            
        }).Schedule();
    }
}
```

![Creating SetupScoreSystem](/files/-MS0UqyiFWweaQ1cSTHH)

* Let's get back to NavigationScene, hit Play, click Host a Game, and then go to DOTS Windows
* Check out the ClientWorld (by selecting the drop down menu in the top left that currently has "Default World" selected, then choose ClientWorld)
  * Find our PlayerScore and HighestScore components

![Checking out Entity Debugger to see PlayerScore and HighestScore appearing on the client](/files/-MS0V-GPBlKoU7lZXeq5)

* Great! We see that PlayerScore and HighestScore were created and that PlayerScore is updated to the players name

{% hint style="success" %}
&#x20;We have set up scoring

* We created HighestScoreComponent
* We created HighestScore prefab
* We created PlayerScoreComponent
* We created PlayerScoreAuthoringComponent
* We created PlayerScore prefab
* We created SendServerPlayerNameRpc
* We updated ClientLoadGameSystem
* We created SetupScoreSystem
  {% endhint %}

## Getting the Server to update scores

We are going to use a similar approach for updating scores as the one we used in ChangeMaterialAndDestroySystem when we added DestroyTags to entities bullets that had collisions.

When we have an OnEnter collision we will do analysis of who the bullet owner is and what it collided with. Based on the results we will update the bullet owner's PlayerScore and possibly the HighestScore.

* Let's create AdjustScoresFromBulletCollisionsSystem in the Server/Systems folder
* Paste the code snippet below into AdjustScoresFromBulletCollisionsSystem.cs:

```
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics.Stateful;
using UnityEngine;
using Unity.NetCode;


[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
public partial class AdjustPlayerScoresFromBulletCollisionSystem : SystemBase
{
    private EndSimulationEntityCommandBufferSystem m_CommandBufferSystem;
    private StatefulTriggerEventBufferSystem m_TriggerSystem;
    private EntityQueryMask m_NonTriggerMask;
    private EntityQuery m_PlayerScores;
    private EntityQuery m_HighestScore;

    protected override void OnCreate()
    {
        m_CommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
        m_TriggerSystem = World.GetOrCreateSystem<StatefulTriggerEventBufferSystem>();
        
        m_NonTriggerMask = EntityManager.GetEntityQueryMask(
            GetEntityQuery(new EntityQueryDesc
            {
                None = new ComponentType[]
                {
                    typeof(StatefulTriggerEvent)
                }
            })
        );
        //We set our queries
        m_PlayerScores = GetEntityQuery(ComponentType.ReadWrite<PlayerScoreComponent>());
        m_HighestScore = GetEntityQuery(ComponentType.ReadWrite<HighestScoreComponent>());
        //We wait to update until we have our converted entities
        RequireForUpdate(m_PlayerScores);
        RequireForUpdate(m_HighestScore);
    }

    protected override void OnUpdate()
    {
        // Need this extra variable here so that it can
        // be captured by Entities.ForEach loop below
        var nonTriggerMask = m_NonTriggerMask;

        //We grab all the player scores because we don't know who will need to be assigned points
        var playerScoreEntities = m_PlayerScores.ToEntityArray(Allocator.TempJob);
        //we will need to grab the PlayerScoreComponent from our player score entities to compare values
        var playerScoreComponent = GetComponentDataFromEntity<PlayerScoreComponent>();

        //We grab the 1 HighestScore engity
        var highestScoreEntities = m_HighestScore.ToEntityArray(Allocator.TempJob);
        //We will need to grab the HighestScoreComponent from our highest score entity to compare values
        var highestScoreComponent = GetComponentDataFromEntity<HighestScoreComponent>();

        //We are going to use this to pull the GhostOwnerComponent from the bullets to see who they belong to
        var ghostOwner = GetComponentDataFromEntity<GhostOwnerComponent>();
        
        //We need to dispose our entities
        Entities
        .WithDisposeOnCompletion(playerScoreEntities)
        .WithDisposeOnCompletion(highestScoreEntities)
        .WithName("ChangeMaterialOnTriggerEnter")
        .ForEach((Entity e, ref DynamicBuffer<StatefulTriggerEvent> triggerEventBuffer) =>
        {
            for (int i = 0; i < triggerEventBuffer.Length; i++)
            {
                //Here we grab our bullet entity and the other entity it collided with
                var triggerEvent = triggerEventBuffer[i];
                var otherEntity = triggerEvent.GetOtherEntity(e); 

                // exclude other triggers and processed events
                if (triggerEvent.State == StatefulEventState.Stay || !nonTriggerMask.Matches(otherEntity))
                {
                    continue;
                }

                //We want our code to run on the first intersection of Bullet and other entity
                else if (triggerEvent.State == StatefulEventState.Enter)
                {

                    //We grab the NetworkId of the bullet so we know who to assign points to
                    var bulletsPlayerNetworkId = ghostOwner[e].NetworkId;

                    //We start with 0 points to add
                    int pointsToAdd = 0;
                    if (HasComponent<PlayerTag>(otherEntity))
                    {
                        //Now we check if the bullet came from the same player
                        if (ghostOwner[otherEntity].NetworkId == bulletsPlayerNetworkId)
                        {
                            //If it is from the same player no points
                            return;
                        }
                        pointsToAdd += 10;
                    }

                    if (HasComponent<AsteroidTag>(otherEntity))
                    {
                        //Bullet hitting an Asteroid is 1 point
                        pointsToAdd += 1;
                    }
                    
                    //After updating the points to add we check the PlayerScore entities and find the one with the
                    //correct NetworkId so we can update the scores for the PlayerScoreComponent
                    //If the updated score is higher than the highest score it updates the highest score
                    for (int j = 0; j < playerScoreEntities.Length; j++)
                    {
                        //Grab the PlayerScore
                        var  currentPlayScoreComponent = playerScoreComponent[playerScoreEntities[j]];
                        if(currentPlayScoreComponent.networkId == bulletsPlayerNetworkId)
                        {
                            //We create a new component with updated values
                            var newPlayerScore = new PlayerScoreComponent{
                                networkId = currentPlayScoreComponent.networkId,
                                playerName = currentPlayScoreComponent.playerName,
                                currentScore = currentPlayScoreComponent.currentScore + pointsToAdd,
                                highScore = currentPlayScoreComponent.highScore
                                };
                            //Here we check if the player beat their own high score
                            if (newPlayerScore.currentScore > newPlayerScore.highScore)
                            {
                                newPlayerScore.highScore = newPlayerScore.currentScore;
                            }

                            //Here we check if the player beat the highest score
                            var currentHighScore = highestScoreComponent[highestScoreEntities[0]];
                            if (newPlayerScore.highScore > currentHighScore.highestScore)
                            {
                                //If it does we make a new HighestScoreComponent
                                var updatedHighestScore = new HighestScoreComponent {
                                    highestScore = newPlayerScore.highScore,
                                    playerName = newPlayerScore.playerName
                                };

                                //The reason why we don't go with:
                                //SetComponent<HighestScoreComponent>(highestScoreEntities[0],  updatedHighestScore);
                                //is because SetComponent<HighestScoreComponent>() gets codegen'd into ComponentDataFromEntity<HighestScoreComponent>()
                                //and you can't use 2 different ones or else you get an 'two containers may not be the same (aliasing)' error
                                highestScoreComponent[highestScoreEntities[0]] = updatedHighestScore;
                            }
                            // SetComponent<PlayerScoreComponent>(playerScoreEntities[j], newPlayerScore);
                            //The reason why we don't go with:
                            //SetComponent<PlayerScoreComponent>(playerScoreEntities[j],  newPlayerScore);
                            //is because SetComponent<PlayerScoreComponent>() gets codegen'd into ComponentDataFromEntity<PlayerScoreComponent>()
                            //and you can't use 2 different ones or else you get an 'two containers may not be the same (aliasing)' error
                            playerScoreComponent[playerScoreEntities[j]] = newPlayerScore;
                        }
                    }
                }
                else
                {
                    continue;
                }
            }
        }).Schedule();
    }
}
```

![Creating AdjustScoresFromBulletCollisionsSystem](/files/-MS0WokgzGyMyjEtjY0y)

* Okay once saved, let's hit play, host game, shoot around to destroy a couple asteroids, then head back to DOTS Windows to check out ClientWorld again to make sure that our PlayerScore and HighestScore are updating

![Shooting around and PlayerScore updating with PlayerName and Current/High score](/files/-MS0XTM1pveT6xrfO2O5)

* Great! They are updating
* Now let's hit "p" to self-destruct
* Checkout the PlayerScore

![Self-destructing does not cause the current score to go to 0](/files/-MS0X4_69Amiumq4CE6r)

* Uh oh, the CurrentScore didn't go to 0 even though we self-destructed as a player
* Let's update PlayerDestructionSystem to also reset the players score to 0
* Paste the code snippet below into PlayerDestructionSystem.cs:

```
using Unity.Entities;
using Unity.Jobs;
using Unity.NetCode;
using Unity.Collections;

//We are going to update LATE once all other systems are complete
//because we don't want to destroy the Entity before other systems have
//had a chance to interact with it if they need to
[UpdateInWorld(TargetWorld.Server)]
[UpdateInGroup(typeof(LateSimulationSystemGroup))]
public partial class PlayerDestructionSystem : SystemBase
{
    private EndSimulationEntityCommandBufferSystem m_EndSimEcb;    

    private EntityQuery m_PlayerScores;
    private EntityQuery m_HighestScore;

    protected override void OnCreate()
    {
        //We grab the EndSimulationEntityCommandBufferSystem to record our structural changes
        m_EndSimEcb = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();

        //We set our queries
        m_PlayerScores = GetEntityQuery(ComponentType.ReadWrite<PlayerScoreComponent>());
        m_HighestScore = GetEntityQuery(ComponentType.ReadWrite<HighestScoreComponent>());
    }
    
    protected override void OnUpdate()
    {
        //We add "AsParallelWriter" when we create our command buffer because we want
        //to run our jobs in parallel
        var commandBuffer = m_EndSimEcb.CreateCommandBuffer().AsParallelWriter();

        //We are going to need to update the NCE CommandTargetComponent so we set the argument to false (not read-only)
        var commandTargetFromEntity = GetComponentDataFromEntity<CommandTargetComponent>(false);

        JobHandle playerScoresDep;
        //We grab all the player scores because we don't know who will need to be assigned points
        var playerScoreEntities = m_PlayerScores.ToEntityArrayAsync(Allocator.TempJob, out playerScoresDep);
        //we will need to grab the PlayerScoreComponent from our player score entities to compare values
        var playerScoreComponent = GetComponentDataFromEntity<PlayerScoreComponent>();


        //We now any entities with a DestroyTag and an PlayerTag
        //We could just query for a DestroyTag, but we might want to run different processes
        //if different entities are destroyed, so we made this one specifically for Players
        //We query specifically for players because we need to clear the NCE when they are destroyed
        //In order to write over a variable that we pass through to a job we must include "WithNativeDisableParallelForRestricion"
        //It means "yes we know what we are doing, allow us to write over this variable"
        var playerDestructionJob = Entities
        .WithDisposeOnCompletion(playerScoreEntities)
        .WithReadOnly(playerScoreEntities)
        .WithNativeDisableParallelForRestriction(playerScoreComponent)
        .WithNativeDisableParallelForRestriction(commandTargetFromEntity)
        .WithAll<DestroyTag, PlayerTag>()
        .ForEach((Entity entity, int nativeThreadIndex, in PlayerEntityComponent playerEntity, in GhostOwnerComponent ghostOwnerComponent) =>
        {
            // Reset the CommandTargetComponent on the Network Connection Entity to the player
            //We are able to find the NCE the player belongs to through the PlayerEntity component
            var state = commandTargetFromEntity[playerEntity.PlayerEntity]; 
            state.targetEntity = Entity.Null;
            commandTargetFromEntity[playerEntity.PlayerEntity] = state;

            //Now we cycle through PlayerScores till we find the right onw
            for (int j = 0; j < playerScoreEntities.Length; j++)
            {
                //Grab the PlayerScore
                var  currentPlayScoreComponent = playerScoreComponent[playerScoreEntities[j]];
                //Check if the player to destroy has the same NetworkId as the current PlayerScore
                if(currentPlayScoreComponent.networkId == ghostOwnerComponent.NetworkId)
                {
                    //We create a new component with updated values
                    var newPlayerScore = new PlayerScoreComponent{
                        networkId = currentPlayScoreComponent.networkId,
                        playerName = currentPlayScoreComponent.playerName,
                        currentScore = 0,
                        highScore = currentPlayScoreComponent.highScore
                        };
                    // SetComponent<PlayerScoreComponent>(playerScoreEntities[j], newPlayerScore);
                    //The reason why we don't go with:
                    //SetComponent<PlayerScoreComponent>(playerScoreEntities[j],  newPlayerScore);
                    //is because SetComponent<PlayerScoreComponent>() gets codegen'd into ComponentDataFromEntity<PlayerScoreComponent>()
                    //and you can't use 2 different ones or else you get an 'two containers may not be the same (aliasing)' error
                    playerScoreComponent[playerScoreEntities[j]] = newPlayerScore;
                }
            }
            //Then destroy the entity
            commandBuffer.DestroyEntity(nativeThreadIndex, entity);

        }).ScheduleParallel(JobHandle.CombineDependencies(Dependency, playerScoresDep));

        //We set the system dependency
        Dependency = playerDestructionJob;
        //We then add the dependencies of these jobs to the EndSimulationEntityCOmmandBufferSystem
        //that will be playing back the structural changes recorded in this sytem
        m_EndSimEcb.AddJobHandleForProducer(Dependency);
    
    }
}
```

![Updating PlayerDestructionSystem](/files/-MS0_o-KfP4E2Mio2ydn)

* Once that's updated, hit Play, host game, shoot around again, destroy some asteroids, hit "p" to self-destruct and checkout our PlayerScoreComponent

![](/files/-MS0_e6Vl2vtnKxMmocO)

{% hint style="success" %}
&#x20;We now have our scores updating based on bullet collisions

* We created AdjustScoresFromBulletCollisionsSystem
* We updated PlayerDestructionSystem
  {% endhint %}

## Update Game UI With UI Toolkit Data Binding

Now that we have our ghosted PlayerScores and HighestScore our client can access them to update their Game UI.

The server calls the shots so the logic we use will be simple. If your UI values do not equal ghost values, change your UI values to the ghost values.

Let's update GameOverlayUpdater to including querying for the PlayerScores and HighestScore.

* Paste the code snippet below into GameOverlayUpdater.cs:

```
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using Unity.Collections;
public class GameOverlayUpdater : MonoBehaviour
{
    //This is how we will grab access to the UI elements we need to update
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;
    private Label m_GameName;
    private Label m_GameIp;
    private Label m_PlayerName;
    private Label m_CurrentScoreText;
    private Label m_HighScoreText;
    private Label m_HighestScoreText;
    //We will need ClientServerInfo to update our VisualElements with appropriate values
    public ClientServerInfo ClientServerInfo;
    private World m_ClientWorld;
    private ClientSimulationSystemGroup m_ClientWorldSimulationSystemGroup;
    //Will check for GameNameComponent
    private EntityQuery m_GameNameComponentQuery;
    private bool gameNameIsSet = false;
    //We need the PlayerScores and HighestScore as well as our NetworkId
    //We are going to set our NetworkId and then query the ghosts for the PlayerScore entity associated with us
    private EntityQuery m_NetworkConnectionEntityQuery;
    private EntityQuery m_PlayerScoresQuery;
    private EntityQuery m_HighestScoreQuery;
    private bool networkIdIsSet = false;
    private int m_NetworkId;
    private Entity ClientPlayerScoreEntity;
    public int m_CurrentScore;
    public int m_HighScore;
    public int m_HighestScore;
    public string m_HighestScoreName;
    void OnEnable()
    {
        //We set the labels that we will need to update
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_GameName = m_GameManagerUIVE.Q<Label>("game-name");
        m_GameIp = m_GameManagerUIVE.Q<Label>("game-ip");
        m_PlayerName = m_GameManagerUIVE.Q<Label>("player-name");
        //Scores will be updated in a future section
        m_CurrentScoreText = m_GameManagerUIVE.Q<Label>("current-score");
        m_HighScoreText = m_GameManagerUIVE.Q<Label>("high-score");
        m_HighestScoreText = m_GameManagerUIVE.Q<Label>("highest-score");
    }
    // Start is called before the first frame update
    void Start()
    {
        //We set the initial client data we already have as part of ClientDataComponent
        m_GameIp.text = ClientServerInfo.ConnectToServerIp;
        m_PlayerName.text = ClientServerInfo.PlayerName;
        //If it is not the client, stop running this script (unnecessary)
        if (!ClientServerInfo.IsClient)
        {
            this.enabled = false;         
        }
        //Now we search for the client world and the client simulation system group
        //so we can communicated with ECS in this MonoBehaviour
        foreach (var world in World.All)
        {
            if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
            {
                m_ClientWorld = world;
                m_ClientWorldSimulationSystemGroup = world.GetExistingSystem<ClientSimulationSystemGroup>();
                m_GameNameComponentQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<GameNameComponent>());
                //Grabbing the queries we need for updating scores
                m_NetworkConnectionEntityQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkIdComponent>());
                m_PlayerScoresQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<PlayerScoreComponent>());
                m_HighestScoreQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<HighestScoreComponent>());
            }
        }
    }
    // Update is called once per frame
    void Update()
    {
        //We do not need to continue if we do not have a GameNameComponent yet
        if(m_GameNameComponentQuery.IsEmptyIgnoreFilter)
            return;
        //If we have a GameNameComponent we need to update ClientServerInfo and then our UI
        //We only need to do this once so we have a boolean flag to prevent this from being ran more than once
        if(!gameNameIsSet)
        {
                ClientServerInfo.GameName = m_ClientWorldSimulationSystemGroup.GetSingleton<GameNameComponent>().GameName.ToString();
                m_GameName.text = ClientServerInfo.GameName;
                gameNameIsSet = true;
        }
        //Now we will handle updating scoring
        //We check if the scoring entities exist, otherwise why bother
        if(m_NetworkConnectionEntityQuery.IsEmptyIgnoreFilter || m_PlayerScoresQuery.IsEmptyIgnoreFilter || m_HighestScoreQuery.IsEmptyIgnoreFilter)
            return;
        //We set our NetworkId once
        if(!networkIdIsSet)
        {
            m_NetworkId = m_ClientWorldSimulationSystemGroup.GetSingleton<NetworkIdComponent>().Value;
            networkIdIsSet = true;
        }
        //Grab PlayerScore entities
        var playerScoresNative = m_PlayerScoresQuery.ToEntityArray(Allocator.TempJob);
        //For each entity find the entity with a matching NetworkId
        for (int j = 0; j < playerScoresNative.Length; j++)
        {
            //Grab the NetworkId of the PlayerScore entity
            var netId = m_ClientWorldSimulationSystemGroup.GetComponentDataFromEntity<PlayerScoreComponent>(true)[playerScoresNative[j]].networkId;
            //Check if it matches our NetworkId that we set
            if(netId == m_NetworkId)
            {
                //If it matches set our ClientPlayerScoreEntity
                ClientPlayerScoreEntity = playerScoresNative[j];
            }
        }
        //No need for this anymore
        playerScoresNative.Dispose();
        //Every Update() we get grab the PlayerScoreComponent from our set Entity and check it out with current values
        var playerScoreComponent = m_ClientWorldSimulationSystemGroup.GetComponentDataFromEntity<PlayerScoreComponent>(true)[ClientPlayerScoreEntity];
        //Check if current is different and update to ghost value
        if(m_CurrentScore != playerScoreComponent.currentScore)
        {
            //If it is make it match the ghost value
            m_CurrentScore = playerScoreComponent.currentScore;
            UpdateCurrentScore();
        }
        //Check if current is different and update to ghost value
        if(m_HighScore != playerScoreComponent.highScore)
        {
            //If it is make it match the ghost value
            m_HighScore = playerScoreComponent.highScore;
            UpdateHighScore();
        }            
        //We grab our HighestScoreComponent
        var highestScoreNative = m_HighestScoreQuery.ToComponentDataArray<HighestScoreComponent>(Allocator.TempJob);
        //We check if its current  value is different than ghost value
        if(highestScoreNative[0].highestScore != m_HighestScore)
        {
            //If it is make it match the ghost value
            m_HighestScore = highestScoreNative[0].highestScore;
            m_HighestScoreName = highestScoreNative[0].playerName.ToString();
            UpdateHighestScore();
        }
        highestScoreNative.Dispose();
    }
    void UpdateCurrentScore()
    {
        m_CurrentScoreText.text = m_CurrentScore.ToString();
    }
    void UpdateHighScore()
    {
        m_HighScoreText.text = m_HighScore.ToString();
    }
    void UpdateHighestScore()
    {
        m_HighestScoreText.text = m_HighestScoreName.ToString() + " - " + m_HighestScore.ToString();
    }
}
```

![Updating GameOverlayUpdater](/files/-MS0aJOiWzk9cHWQZ07Y)

* Now hit play, host game, and shoot around at the asteroids and see your score increase

{% hint style="danger" %}
Warning! This might not work as expected if there are thin-clients, since the UI-Code might find the score entity of one of the thin clients instead of the actual player.
{% endhint %}

* Hit "p" to self-destruct and start again and see the score reset to 0

![Game UI updating when player shoots asteroids](/files/-MS0bsXWSR6IhYg7mgHp)

{% hint style="success" %}
&#x20;Our game UI updates based on updated ghost values

* We updated GameOverlayUpdater
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Score-Keeping'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Send Ghosts with NetCode Using Relevancy

Code and workflows to send ghosts and clean up logs using GhostRelevancyMode

## What you'll develop on this page

![Join a LAN game and only receive ghosts within a certain radius](/files/-MSA9KZiMR9ncwEDHm4m)

We will implement a system where only the ghosts near a player are sent. We will also clean some unnecessary logs.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/GhostRelevancyMode-and-Clean-Up>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Ghost Relevancy Sphere

### Network transmission

So far we've been working with 200 Asteroids. This means our server sends updates for 200 entities, which is a lot of data streams for SnapShots.

Because data and bandwidth are limited it is important to be mindful of what updates are sent from the server to the client. You should always think: is the most important stuff getting to the client?

The \[GhostField] attributes on our ghost IComponentData is the data that gets sent over between clients and server through SnapShots. Dynamic ghosts automatically have their rotation and translation sent over (clients can also send data through RPCs and Commands).

If you remember GhostAuthoringComponent it is possible to optimize ghosts to be "static" (like our HighestScore and PlayerScore). This means the server does not send updates on Translation and Rotation (because they are static).

Our Asteroid, Player, and Bullet prefabs all are dynamic so they send updates of their Rotation and Translation.

![GhostAuthoringComponent on Asteroid prefab has a Dynamic Optimization Mode which sends Translation and Rotation data](/files/-MS0shFd4qyeI-Wn_iOh)

Think of a super large map with many ghosted objects-- do you think it's important for the client to get *all* of the snapshot data of objects that are *way* across the map? Probably not. It is not efficient for the player to receive the Translation and Rotation SnapShot updates of entities that will never ever encounter the player. That would be an inefficient use of networking.

* Try it out - go to ConvertedSubScene, then the GameSettings GameObject, and change the Number of Asteroids to 2000
* Next, change the Level Size to 100x100x100
* Then press Play, Host a game and take a look at the Asteroids

![Asteroid SnapShots not getting to client fast enough to make movement appear smooth](/files/-MS0t6k7hlZUkWFPpa5Y)

The client is having a tough time getting enough SnapShot updates to make the asteroids appear to be moving smoothly.

### Ghost Relevancy

We are going to use the concept of a Player Relevancy Sphere so that only ghosts that are within a certain radius of the player will be sent to the player.

The server will check for the relevancyRadius field in GameSettingsComponent in PlayerRelevancySphereSystem. If it exists, it will take note of the position of each client and only send ghosts within that distance.

* Let's update GameSettingComponent to have an additional field, relevancyRadius
* Paste the code snippet below into GameSettingsComponent.cs:

```
using Unity.Entities;

public struct GameSettingsComponent : IComponentData
{
    public float asteroidVelocity;
    public float playerForce;
    public float bulletVelocity;
    public int numAsteroids;
    public int levelWidth;
    public int levelHeight;
    public int levelDepth;
    public float relevancyRadius;
}
```

![Updating GameSettingsComponent](/files/-MS0vTqcItoYsDXEBLx5)

* Now we must also update SetGameSettingsSystem to pass through this new field
* Paste the code snippet below into SetGameSettingsSystem.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

public class SetGameSettingsSystem : UnityEngine.MonoBehaviour, IConvertGameObjectToEntity
{
    public float asteroidVelocity = 10f;
    public float playerForce = 50f;
    public float bulletVelocity = 500f;
    public int numAsteroids = 200;
    public int levelWidth = 2048;
    public int levelHeight = 2048;
    public int levelDepth = 2048;
    public int relevencyRadius = 0;
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        var settings = default(GameSettingsComponent);
        settings.asteroidVelocity = asteroidVelocity;
        settings.playerForce = playerForce;
        settings.bulletVelocity = bulletVelocity;
        settings.numAsteroids = numAsteroids;
        settings.levelWidth = levelWidth;
        settings.levelHeight = levelHeight;
        settings.levelDepth = levelDepth;
        settings.relevancyRadius = relevencyRadius;
        dstManager.AddComponentData(entity, settings);
    }
}
```

![UpdatingSetGameSettingsSystem](/files/-MS0vcULv5yYnCIjbaxF)

* Next, create a new System inside the Server/Systems folder and name it PlayerRelevancySphereSystem
* Paste the code snippet below into PlayerRelevancySphereSystem.cs:

```
using Unity.NetCode;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Transforms;
using Unity.Jobs;

[UpdateInGroup(typeof(ServerSimulationSystemGroup))]
[UpdateBefore(typeof(GhostSendSystem))]
public partial class PlayerRelevancySphereSystem : SystemBase
{
    //This will be a struct we use only in this system
    //The ConnectionId is the entities NetworkId
    struct ConnectionRelevancy
    {
        public int ConnectionId;
        public float3 Position;
    }
    //We grab the ghost send system to use its GhostRelevancyMode
    GhostSendSystem m_GhostSendSystem;
    //Here we keep a list of our NCEs with NetworkId and position of player
    NativeList<ConnectionRelevancy> m_Connections;
    EntityQuery m_GhostQuery;
    EntityQuery m_ConnectionQuery;
    protected override void OnCreate()
    {
        m_GhostQuery = GetEntityQuery(ComponentType.ReadOnly<GhostComponent>());
        m_ConnectionQuery = GetEntityQuery(ComponentType.ReadOnly<NetworkIdComponent>());
        RequireForUpdate(m_ConnectionQuery);
        m_Connections = new NativeList<ConnectionRelevancy>(16, Allocator.Persistent);
        m_GhostSendSystem = World.GetExistingSystem<GhostSendSystem>();
        //We need the GameSettingsComponent so we need to make sure it streamed in from the SubScene
        RequireSingletonForUpdate<GameSettingsComponent>();
    }
    protected override void OnDestroy()
    {
        m_Connections.Dispose();
    }
    protected override void OnUpdate()
    {
        //We only run this if the relevancyRadius is not 0
        var settings = GetSingleton<GameSettingsComponent>();
        if ((int) settings.relevancyRadius == 0)
        {
            m_GhostSendSystem.GhostRelevancyMode = GhostRelevancyMode.Disabled;
            return;
        }
        //This is a special NetCode system configuration
        //This is saying that any ghost we put in this list is IRRELEVANT (it means ignore these ghosts)
        m_GhostSendSystem.GhostRelevancyMode = GhostRelevancyMode.SetIsIrrelevant;

        //We create a new list of connections ever OnUpdate
        m_Connections.Clear();
        var irrelevantSet = m_GhostSendSystem.GhostRelevancySet;
        //This is our irrelevantSet that we will be using to add to our list
        var parallelIsNotRelevantSet = irrelevantSet.AsParallelWriter();

        var maxRelevantSize = m_GhostQuery.CalculateEntityCount() * m_ConnectionQuery.CalculateEntityCount();

        var clearHandle = Job.WithCode(() => {
            irrelevantSet.Clear();
            if (irrelevantSet.Capacity < maxRelevantSize)
                irrelevantSet.Capacity = maxRelevantSize;
        }).Schedule(m_GhostSendSystem.GhostRelevancySetWriteHandle);

        //Here we grab the positions and networkids of the NCEs ComandTargetCommponent's targetEntity
        var connections = m_Connections;
        var transFromEntity = GetComponentDataFromEntity<Translation>(true);
        var connectionHandle = Entities
            .WithReadOnly(transFromEntity)
            .WithNone<NetworkStreamDisconnected>()
            .WithAll<NetworkStreamInGame>()
            .ForEach((in NetworkIdComponent netId, in CommandTargetComponent target) => {
            var pos = new float3();
            //If we havent spawned a player yet we will set the position to the location of the main camera
            if (target.targetEntity == Entity.Null)
                pos = new float3(0,1,-10);
            else 
                pos = transFromEntity[target.targetEntity].Value;
            connections.Add(new ConnectionRelevancy{ConnectionId = netId.Value, Position = pos});
        }).Schedule(Dependency);

        //Here we check all ghosted entities and see which ones are relevant to the NCEs based on distance and the relevancy radius
        Dependency = Entities
            .WithReadOnly(connections)
            .ForEach((Entity entity, in GhostComponent ghost, in Translation pos) => {
            for (int i = 0; i < connections.Length; ++i)
            {
                //Here we do a check on distance, and if the entity is a PlayerScore or HighestScore entity
                //If the ghost is out of the radius (and is not PlayerScore or HighestScore) then we add it to the "ignore this ghost" list (parallelIsNotRelevantSet)
                if (math.distance(pos.Value, connections[i].Position) > settings.relevancyRadius && !(HasComponent<PlayerScoreComponent>(entity) || HasComponent<HighestScoreComponent>(entity)))
                    parallelIsNotRelevantSet.TryAdd(new RelevantGhostForConnection(connections[i].ConnectionId, ghost.ghostId), 1);
            }
        }).ScheduleParallel(JobHandle.CombineDependencies(connectionHandle, clearHandle));

        m_GhostSendSystem.GhostRelevancySetWriteHandle = Dependency;
    }
}
```

![Creating PlayerRelevancySphereSystem](/files/-MS0vg9zoayqKi_W-Tgh)

{% hint style="info" %}
You will notice that in PlayerRelevancySphereSystem we set`m_GhostSendSystem.GhostRelevancyMode = GhostRelevancyMode.SetIsIrrelevant;`

Which means that we are sending "ignore these" ghosts. \
Just as an FYI, we *could* have done "SetIsRelevant" and instead sent relevant ghosts (the inverse).
{% endhint %}

* Now let's go back to GameSettings in ConvertedSubScene and update Relevancy Radius to 40, save, and return to NavigationScene

![Updating GameSettings to have Relevancy Radius of 40](/files/-MS0wlbcH2fmGUVmocw3)

* Now, hit Play, host a game, move around, and keep an eye out on the scene view
* Self-destruct and move around

![Navigating the game and the asteroids only "appear" in proximity to the player](/files/-MS0wshPQxsGMveCxRBX)

* Only those ghosts that are in proximity to the player appear near the user
* As the player moves you can see the "sphere" of asteroids appearing and disappearing

Now with these updates, you can have bigger maps in your games and host a lot more players. The server still runs calculations on bullets and asteroids the player does not see (out of its radius), so bullets still will collide with "far-away" ghosts that the player does not see when the bullet was fired.

{% hint style="success" %}
&#x20;Our game UI updates based on updated ghost values

* We updated GameSettingsComponent
* We updated SetGameSettingsSystem
* We created PlayerRelevancySphereSystem
  {% endhint %}

## Clean-up

You might sometimes notice an error regarding GhostDistancePartitioningSystem when hitting Quit Game. There are also some errors that appear when we we quit the game while MainScene is running.

We're now going to handle quitting the application (Quit Game) more gracefully.

Part of accomplishing this is that we will cycle all worlds on our ClientServerConnectionHandler deleting queries during the OnDestroy(). We will also disable GhostDistancePartitioningSystem when we hit Quit Game.

* Paste the code snippet below into ClientServerConnectionHandler.cs:

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;
using Unity.Collections;

public class ClientServerConnectionHandler : MonoBehaviour
{
    //This is the store of server/client info
    public ClientServerInfo ClientServerInfo;

    //These are the launch objects from Navigation scene that tells what to set up
    private GameObject[] launchObjects;

    //These will gets access to the UI views 
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;

    //We will use these variables for hitting Quit Game on client or if server disconnects
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;
    private World m_ClientWorld;
    private EntityQuery m_ClientNetworkIdComponentQuery;
    private EntityQuery m_ClientDisconnectedNCEQuery;

    //We will use these variables for hitting Quit Game on server
    private World m_ServerWorld;
    private EntityQuery m_ServerNetworkIdComponentQuery;

    void OnEnable()
    {
        //This will put callback on "Quit Game" button
        //This triggers the clean up function (ClickedQuitGame)
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_GameManagerUIVE.Q("quit-game")?.RegisterCallback<ClickEvent>(ev => ClickedQuitGame());
    }

    void Awake()
    {
        launchObjects = GameObject.FindGameObjectsWithTag("LaunchObject");
        foreach(GameObject launchObject in launchObjects)
        {
            ///  
            //Checks for server launch object
            //If it exists it creates ServerDataComponent InitializeServerComponent and
            //passes through server data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ServerLaunchObjectData>() != null)
            {
                //This sets the gameobject server data  in ClientServerInfo (mono)
                ClientServerInfo.IsServer = true;
                ClientServerInfo.GameName = launchObject.GetComponent<ServerLaunchObjectData>().GameName;
                ClientServerInfo.BroadcastIpAddress = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastIpAddress;
                ClientServerInfo.BroadcastPort = launchObject.GetComponent<ServerLaunchObjectData>().BroadcastPort;

                //This sets the component server data in server world(dots)
                //ClientServerConnectionControl (server) will run in server world
                //it will pick up this component and use it to listen on the port
                foreach (var world in World.All)
                {
                    //we cycle through all the worlds, and if the world has ServerSimulationSystemGroup
                    //we move forward (because that is the server world)
                    if (world.GetExistingSystem<ServerSimulationSystemGroup>() != null)
                    {
                        var ServerDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ServerDataEntity, new ServerDataComponent
                        {
                            GameName = ClientServerInfo.GameName,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows server initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeServerComponent));

                        //For handling server disconnecting by hitting the quit button
                        m_ServerWorld = world;
                        m_ServerNetworkIdComponentQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkIdComponent>());

                    }
                }
            }

            // 
            //Checks for client launch object
            //If it exists it creates ClientDataComponent, InitializeServerComponent and
            // passes through client data to ClientServerInfo
            // 
            if(launchObject.GetComponent<ClientLaunchObjectData>() != null)
            {
                //This sets the gameobject data in ClientServerInfo (mono)
                ClientServerInfo.IsClient = true;
                ClientServerInfo.ConnectToServerIp = launchObject.GetComponent<ClientLaunchObjectData>().IPAddress;                
                ClientServerInfo.PlayerName = launchObject.GetComponent<ClientLaunchObjectData>().PlayerName;

                //This sets the component client data in server world (dots)
                //ClientServerConnectionControl (client) will run in client world
                //it will pick up this component and use it connect to IP and port
                foreach (var world in World.All)
                {
                    //We cycle through all the worlds, and if the world has ClientSimulationSystemGroup
                    //we move forward (because that is the client world)
                    if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                    {
                        var ClientDataEntity = world.EntityManager.CreateEntity();
                        world.EntityManager.AddComponentData(ClientDataEntity, new ClientDataComponent
                        {
                            PlayerName = ClientServerInfo.PlayerName,
                            ConnectToServerIp = ClientServerInfo.ConnectToServerIp,
                            GamePort = ClientServerInfo.GamePort
                        });
                        //Create component that allows client initialization to run
                        world.EntityManager.CreateEntity(typeof(InitializeClientComponent));

                        //We will now set the variables we need to clean up during QuitGame()
                        m_ClientWorld = world;
                        m_ClientSimulationSystemGroup = world.GetExistingSystem<ClientSimulationSystemGroup>();
                        m_ClientNetworkIdComponentQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkIdComponent>());
                        //This variable is used to check if the server disconnected
                        m_ClientDisconnectedNCEQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadWrite<NetworkStreamDisconnected>());

                    }
                }
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //The client checks if the NCE has a NetworkStreamDisconnected component
        //If it does we act like they quit the game manually
        if(m_ClientDisconnectedNCEQuery.IsEmptyIgnoreFilter)
            return;
        else
            ClickedQuitGame();
    }

   //This function will navigate us to NavigationScene and connected with the clients/server about leaving
    void ClickedQuitGame()
    {
        //As a client if we were able to create an NCE we must add a request disconnect
        if (!m_ClientNetworkIdComponentQuery.IsEmptyIgnoreFilter)
        {
            var clientNCE = m_ClientSimulationSystemGroup.GetSingletonEntity<NetworkIdComponent>();
            m_ClientWorld.EntityManager.AddComponentData(clientNCE, new NetworkStreamRequestDisconnect());

        }

        //As a server if we were able to create an NCE we must add a request disconnect to all NCEs
        //We must to see if this was a host build
        if (m_ServerWorld != null)
        {
            //First we grab the array of NCEs
            var nceArray = m_ServerNetworkIdComponentQuery.ToEntityArray(Allocator.TempJob);
            for (int i = 0; i < nceArray.Length; i++)
            {
                //Then we add our NetworkStreamDisconnect component to tell the clients we are leaving
                m_ServerWorld.EntityManager.AddComponentData(nceArray[i], new NetworkStreamRequestDisconnect());
            }
            //Then we dispose of our array
            nceArray.Dispose();
        }

#if UNITY_EDITOR
        if(Application.isPlaying)
#endif
            SceneManager.LoadSceneAsync("NavigationScene");
#if UNITY_EDITOR
        else
            Debug.Log("Loading: " + "NavigationScene");
#endif
        if (ClientServerInfo.IsServer)
                    m_ServerWorld.GetExistingSystem<GhostDistancePartitioningSystem>().Enabled = false;
    }

    //When the OnDestroy method is called (because of our transition to NavigationScene) we
    //must delete all our entities and our created worlds to go back to a blank state
    //This way we can move back and forth between scenes and "start from scratch" each time
    void OnDestroy()
    {
        for (var i = 0; i < launchObjects.Length; i++)
        {
            Destroy(launchObjects[i]);
        }
        foreach (var world in World.All)
        {
            var entityManager = world.EntityManager;
            var uq = entityManager.UniversalQuery;
            world.EntityManager.DestroyEntity(uq);
        }

        World.DisposeAllWorlds();

        //We return to our initial world that we started with, defaultWorld
        var bootstrap = new NetCodeBootstrap();
        bootstrap.Initialize("defaultWorld"); 

    }
}
```

![Updating ClientServerConnectionHandler](/files/-MS4uZMoMvfDt1BTy-8j)

* Now let's clean-up some more by removing some unhelpful logs from certain scripts
* In SendServerGameLoadedRpc remove "Server acted on confirmed game load"
* In GameUIManager.cs (the custom Visual Element) remove "Clicked quit game"

![Cleaning up logs](/files/-MS4vL3yGM61Zi9nn1w2)

Now to wrap-up this entire section, we are left with these networking outputs:

1. started listening for UDP broadcast (NavigationScene)
2. server listening on port (MainScene server launch)
3. client trying to connect to an IP and port (MainScene client launch)
4. "Error receiving data from UDP client:" (When NavigationScene calls .Stop() on UdpConnection)\
   \- This is the UDP client receive thread being stopped\
   \- We can hide errors entirely for production

* We showed how to use GhostRelevancy but for people that are just opening up this tutorial and don't know what is going on they might be confused why it is so "empty" intially so let's change back our GameSettings
  * level width, height, depth = 40
  * Num Asteroids = 200
  * Relevancy Radius = 0

* Finally, we will add an assembly definition
  * This is[ needed to use UI Toolkit](https://forum.unity.com/threads/ui-toolkit-migration-guide.1138621/#post-7405538) if you haven't gone through this tutorial step-by-step
  * Add the following file to /Assets:

{% file src="/files/I3MUepKoJqnrhiJwzGQF" %}

{% hint style="success" %}
&#x20;We updated our project so that it can handle Quit Games a bit more gracefully

We also removed unnecessary logs

* We updated ClientServerConnectionHandler
* We removed log from SendServerGameLoadedRpc
* We removed log from GameUIManager "Quit game"
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'GhostRelevancyMode-and-Clean-Up'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Intro to AR Foundation

Code and workflows to integrate AR Foundation into your desktop build

## What you'll develop in the AR Foundation section

![Shooting down AR player from desktop](/files/-MSQkWsGeth5AFZOJPkL)

![Getting shot down by desktop player](/files/-MSQk_ChKcD34qcuPQvM)

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/UI-Updates>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

### Functionalities included

* Setting up AR Foundation + ARKit plug-in
* Adding AR Foundation GameObjects to game
  * AR Session Origin
  * AR Session
* Dynamically checking if deployed to AR platform and disabling AR functionality if not
  * Creating AR-specific systems that only run when AR enabled
* Grabbing Pose Driver value and providing it to ECS to move player
  * Updating input response systems for updated movement controls
* Pulling ECS data spawn position and updating Pose Driver to move location of AR Camera to behind player
  * Use ARSessionOrigin.MakeContentAppearAt() to update origin of AR session based on game play
* Dynamically updating UI for AR instructions when deployed to AR platform
* Updating PanelSettings to be responsive to both desktop and mobile platforms

{% hint style="info" %}
MacOS development platform is required to deploy to iOS

Unity cannot directly deploy an iOS app. Instead, Unity compiles code to then be *further* compiled by Xcode (Apple's integrated development environment for macOS, iOS, etc).
{% endhint %}

## A little bit about Augmented Reality (AR)

### How AR technology works

Rather than publishing yet another explanation of AR on the internet ourselves (there are probably enough of those), we think it's better to just direct you to what we think is arguably the best explanation of how AR works in commodity hardware.

[Matt Miesnieks](https://twitter.com/mattmiesnieks) wrote a post on Medium during his time as the Founder of 6d.ai (company acquired by Niantic) to describe Apple's then-new release of ARKit. If you are interested in building AR applications and want to learn more about the technology, please read the full blog post here: <https://medium.com/6d-ai/why-is-arkit-better-than-the-alternatives-af8871889d6a>

**What technology is ARKit built on?**

![Image from Matt's blog post](https://miro.medium.com/max/2048/1*kRZDY5t6kAiNSYa1HcfhPw.jpeg)

"Technically ARKit is a Visual Inertial Odometry (VIO) system, with some simple 2D plane detection. VIO means that the software tracks your position in space (your 6dof pose) in real-time i.e. your pose is recalculated in-between every frame refresh on your display, about 30 or more times a second. These calculations are done twice, in parallel. Your pose is tracked via the Visual (camera) system, by matching a point in the real world to a pixel on the camera sensor each frame. Your pose is also tracked by the Inertial system (your accelerometer & gyroscope — together referred to as the Inertial Measurement Unit or IMU). The output of both of those systems are then combined via a Kalman Filter which determines which of the two systems is providing the best estimate of your “real” position (referred to as Ground Truth) and publishes that pose update via the ARKit SDK. Just like your odometer in your car tracks the distance the car has traveled, the VIO system tracks the distance that your iPhone has traveled in 6D space. 6D means 3D of xyz motion (translation), plus 3D of pitch/yaw/roll (rotation).

The big advantage that VIO brings is that IMU readings are made about 1000 times a second and are based on acceleration (user motion). Dead Reckoning is used to measure device movement in between IMU readings. Dead Reckoning is pretty much a guess (!) just like if I asked you to take a step and guess how many inches that step was, you’d be using dead reckoning to estimate the distance. I’ll cover later how that guess is made highly accurate. Errors in the inertial system accumulate over time, so the more time between IMU frames or the longer the Inertial system goes without getting a “reset” from the Visual System the more the tracking will drift away from Ground Truth.

Visual / Optical measurements are made at the camera frame rate, so usually 30fps, and are based on distance (changes of the scene in between frames). Optical systems usually accumulate errors over distance (and time to a lesser extent), so the further you travel, the larger the error."

-From 6d.ai's ["Why is ARKit better than the alternatives?"](https://medium.com/6d-ai/why-is-arkit-better-than-the-alternatives-af8871889d6a)

The core problem that must be solved for in creating AR experiences is for the device to know exactly where it is in [6 degrees of freedom](https://en.wikipedia.org/wiki/Six_degrees_of_freedom) (translation + rotation). The device uses both visual measurements (camera), and inertial measurements (accelerometer/gyroscope) to figure out where it is in three-dimensional space.

The translation + rotation (6 degrees of freedom) of  a device is also referred to as its "pose" in AR.

If you know where the device is (its pose), rendering a virtual object to appear in the physical space becomes easier. The same way a Unity camera moving to the right in a 2D game causes the Unity rendering engine to render objects in a different perspective, the same happens with using AR in the real physical world.

### AR Platforms

Now that we've learned a little more about how AR works (a highly-accurate pose is determined by device's sensor data), let's take a look at the platforms where you can build AR applications.

Currently, the leading players in the AR space are:

* Apple's iOS (ARKit)
* Google's Android (ARCore)
* Microsoft's HoloLens (Mixed Reality Toolkit)
* Magic Leap (Lumin)

There are some very interesting libraries to build web-based AR experiences [like AR.js](https://ar-js-org.github.io/AR.js-Docs/), which can run on a phone browser, but these web libraries are limited.

ARKit, ARCore, Mixed Reality Toolkit, and Lumin each provide special APIs to their specific device hardware to help developers create AR experiences.

A lot of these APIs are similar in that they provide the developer with similar AR data, like providing the "pose" of the device. Each hardware manufacturer has their own version of the "pose" API.

Wouldn't it be nice if there was one single way to communicate with all of the leading AR platforms without having to build 4 different implementations...? Introducing: Unity AR Foundation.

## How Unity handles AR

### Unity Mixed Reality ("XR") Tech Stack

![From "Unity XR Platform updates"](/files/-MSAKNSFzLbTm4YzRz9V)

### [**Unity XR plugin framework**](https://blogs.unity3d.com/2020/01/24/unity-xr-platform-updates/)

We have been working to improve our multi-platform offering, enabling direct integrations through a unified plugin framework. The resulting tech stack consists of an API that exposes common functionalities across our supported platforms in a frictionless way for creators while enabling XR hardware and software providers to develop their own Unity plugins. This architecture offers the following benefits:

* **Multi-platform developer tools** such as AR Foundation and the XR Interaction Toolkit&#x20;
* **Faster partner updates** from supported plugins via the Unity Package Manager&#x20;
* **More platforms** have access to an interface to leverage Unity’s XR rendering optimizations and developer tools

-From [Unity's  XR platform updates](https://blogs.unity3d.com/2020/01/24/unity-xr-platform-updates/)

### AR Foundation

Unity's AR Foundation is an API that sits on top of all the major hardware AR SDKs mentioned earlier.

When "pose" is requested from the AR Foundation layer during runtime, AR Foundation automatically translates that request to whatever appropriate implementation.

Unity does not implement any of these AR functionalities itself; it's just a translation layer. AR Foundation calls a platform-specific "plug-in" to get the necessary data from the hardware. So adding the "AR Foundation" package into our Unity package is not enough; we must also include additional specific packages for the AR platforms we will be targeting. In our case, for this section, that additional package will be Apple's ARKit package.

It is important to note that not *all* plug-ins are made equal. Not all AR Foundation functionalities are available across all plug-ins. For example, both ARKit and ARCore now provide access to a depth API, but the HoloLens does not provide this data.

| Functionality              | ARCore | ARKit | Magic Leap | HoloLens |
| -------------------------- | ------ | ----- | ---------- | -------- |
| Device tracking            | ✓      | ✓     | ✓          | ✓        |
| Plane tracking             | ✓      | ✓     | ✓          |          |
| Point clouds               | ✓      | ✓     |            |          |
| Anchors                    | ✓      | ✓     | ✓          | ✓        |
| Light estimation           | ✓      | ✓     |            |          |
| Environment probes         | ✓      | ✓     |            |          |
| Face tracking              | ✓      | ✓     |            |          |
| 2D Image tracking          | ✓      | ✓     | ✓          |          |
| 3D Object tracking         |        | ✓     |            |          |
| Meshing                    |        | ✓     | ✓          | ✓        |
| 2D & 3D body tracking      |        | ✓     |            |          |
| Collaborative participants |        | ✓     |            |          |
| Human segmentation         |        | ✓     |            |          |
| Raycast                    | ✓      | ✓     | ✓          |          |
| Pass-through video         | ✓      | ✓     |            |          |
| Session management         | ✓      | ✓     | ✓          | ✓        |
| Occlusion                  | ✓      | ✓     |            |          |

## Our Approach

In MainScene, we will run a check if "we are an AR system." If we are an AR system, we will create an IsARPlayerComponent singleton.

We will then use RequireSingletonForUpdate\<IsARPlayerComponent> for our AR-specific systems.

We will create a new InputSystem for AR called ARInputSystem that takes in screen taps and translates them to "shoot" commands. We will also update our PlayerCommand to take in AR pose. So our ARInputSystem will be sending "shoot" data through screen taps and updated position through grabbing the "pose" that ARKit's provides.

AR Foundation is written using MonoBehaviours, so we will create and update an ARPlayerPoseComponent in an Update(). The MonoBehaviour will use the EntityManager to update our ARPlayerPoseComponent and our ARInputSystem will pull this data to add it to our PlayerCommand.

## Unity resources for AR Foundation

Unity documentation for AR Foundation 4.2.3: <https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/> **Refer to this for more information.**

Unity documentation for ARKit XR Plugin 4.1.3: <https://docs.unity3d.com/Packages/com.unity.xr.arkit@4.2/manual/index.html> **Refer to this for more information.**

**To be best prepared for the code-alongs**

* [ ] Read through 6d.ai's [blogpost on ARKit](https://medium.com/6d-ai/why-is-arkit-better-than-the-alternatives-af8871889d6a)

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Set Up AR Foundation and ARKit

Code and workflows to implement AR Foundation and ARKit in your project to create an AR build

## What you'll develop on this page

![Launching the project on an AR enabled iPhone](/files/-MSFoIqbBiw0P_oAzYSx)

We will update our build to trigger an AR session if the platform is AR-enabled.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Setting-Up-AR-Foundation>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Setting up packages and subsystems

AR Foundation requires a bit more love than simply adding packages. We also need to specify "subsystems."

* Let's start by adding our packages to manifest.json
* Paste these two lines into your manifest.json file in the Project folder

```
"com.unity.xr.arfoundation": "4.2.3",
"com.unity.xr.arkit": "4.2.3",
```

![Adding packages to manifest.json](/files/-MSFYIg7CmgljczxTl8I)

* Next we need to enable our Plug-in
* In Unity, navigate to "Project Settings" (Edit > Project Settings) then click XR Plug-in Management and&#x20;
* Click on the iOS tab and check the box next to "ARKit"

![Adding ARKit as a plug-in in XR Plug-in Management](/files/-MSFYMlovyXc1e-Peauq)

* Next we need to provide our app end-users a reason for why we're asking to use their camera (because we need it for ARKit) when we ask for permission during start-up of the app
  * Navigate to "Build Settings" (under "File"), then "Player Settings", and when "Player" is selected on the left panel, click the iOS tab
    * Scroll down to "Camera Usage Description" under "Other Settings" and enter the following text into the text field: "Camera will be used for AR capabilities"
    * Scroll down to "Requires ARKit support" and set it to true

![Adding Camera Usage Description and enabling "Requires ARKit support"](/files/-MSFZ0ehgwFNAkJv3Z4L)

{% hint style="success" %}
&#x20;We've enabled AR development on iOS devices in Unity

* We updated our manifest.json
* We enabled the ARKit subsystem in XR Plug-in Management
* We updated our Player Settings
  {% endhint %}

## Updating MainScene to integrate with AR platforms

### AR Session and AR Session Origin

Now we are going to set up AR Session and AR Session Origin in MainScene. AR Session and AR Session Origin are GameObjects that are core to building Unity AR Foundation experiences.

> #### Unity's explanation of ARSession <a href="#arsession" id="arsession"></a>
>
> An AR scene should include an `ARSession` component. The AR Session controls the lifecycle of an AR experience by enabling or disabling AR on the target platform. The `ARSession` can be on any `GameObject`.
>
> <img src="https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/images/ar-session.png" alt="ARSession component" data-size="original">
>
> When you disable the `ARSession`, the system no longer tracks features in its environment, but if you enable it at a later time, the system attempts to recover and maintain previously-detected features.
>
> If you enable the **Attempt Update** option, the device tries to install AR software if possible. Support for this feature is platform-dependent.
>
> **NOTE**
>
> An AR session is a global construct. An `ARSession` component manages this global session, so multiple `ARSession` components will all try to manage the same global session.
>
> **Checking for device support**
>
> Some platforms might support a limited subset of devices. On these platforms, your application needs to be able to detect support for AR Foundation so it can provide an alternative experience when AR is not supported.
>
> The `ARSession` component has a static coroutine that you can use to determine whether AR is supported at runtime:
>
> ```csharp
> public class MyComponent {
>     [SerializeField] ARSession m_Session;
>
>     IEnumerator Start() {
>         if ((ARSession.state == ARSessionState.None) ||
>             (ARSession.state == ARSessionState.CheckingAvailability))
>         {
>             yield return ARSession.CheckAvailability();
>         }
>
>         if (ARSession.state == ARSessionState.Unsupported)
>         {
>             // Start some fallback experience for unsupported devices
>         }
>         else
>         {
>             // Start the AR session
>             m_Session.enabled = true;
>         }
>     }
> }
> ```
>
> **Session state**
>
> To determine the current state of the session (for example, whether the device is supported, if AR software is being installed, and whether the session is working), use `ARSession.state`. You can also subscribe to an event when the session state changes: `ARSession.stateChanged`.

| `ARSessionState`       | **Description**                                                                                                                                       |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `None`                 | The AR System has not been initialized and availability is unknown.                                                                                   |
| `Unsupported`          | The current device doesn't support AR.                                                                                                                |
| `CheckingAvailability` | The system is checking the availability of AR on the current device.                                                                                  |
| `NeedsInstall`         | The current device supports AR, but AR support requires additional software to be installed.                                                          |
| `Installing`           | AR software is being installed.                                                                                                                       |
| `Ready`                | AR is supported and ready.                                                                                                                            |
| `SessionInitialized`   | An AR session is initializing (that is, starting up). This usually means AR is working, but hasn't gathered enough information about the environment. |
| `SessionTracking`      | An AR session is running and is tracking (that is, the device is able to determine its position and orientation in the world).                        |

> #### AR Session Origin <a href="#ar-session-origin" id="ar-session-origin"></a>
>
> <img src="https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/images/ar-session-origin.png" alt="AR session origin" data-size="original">
>
> The purpose of the `ARSessionOrigin` is to transform trackable features, such as planar surfaces and feature points, into their final position, orientation, and scale in the Unity scene. Because AR devices provide their data in "session space", which is an unscaled space relative to the beginning of the AR session, the `ARSessionOrigin` performs the appropriate transformation into Unity space.
>
> This concept is similar to the difference between "model" or "local" space and world space when working with other Assets in Unity. For instance, if you import a house asset from a DCC tool, the door's position is relative to the modeler's origin. This is commonly called "model space" or "local space". When Unity instantiates it, it also has a world space that's relative to Unity's origin.
>
> Likewise, trackables that an AR device produces, such as planes, are provided in "session space", relative to the device's coordinate system. When instantiated in Unity as `GameObject`s, they also have a world space. In order to instantiate them in the correct place, AR Foundation needs to know where the session origin should be in the Unity scene.
>
> `ARSessionOrigin` also allows you to scale virtual content and apply an offset to the AR Camera. If you're scaling or offsetting the `ARSessionOrigin`, then its AR Camera should be a child of the `ARSessionOrigin`. Because the AR Camera is session-driven, this setup allows the AR Camera and detected trackables to move together.
>
> \
> From [AR Foundation's About AR Foundation documentation](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/index.html)

* Back in Unity, navigate to MainScene in your Scenes folder and open up MainScene
  * Right-click on the Hierarchy, select "XR" then AR Session
  * Right-click on the Hierarchy, select "XR" then AR Session Origin

![Creating AR Session and AR Session Origin](/files/-MSFa0OOBBxLDuDuTGPl)

* Now let's create the script that will check for device support
  * If there is no AR support on a particular device attempting to use your app, then the AR Session component and the AR Session Origin GameObject will be disabled
  * Select the "AR Session" GameObject in Hierarchy
  * When AR Session is selected, go to the Inspector and click "Add Component" and create a new script called "ARPlatformInitializer" and click "Create and add"
  * Once created and added, drag the file into the Scripts and Prefabs/Mixed folder
  * Paste the code snippet below into ARPlatformInitializer.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using Unity.Entities;
using Unity.NetCode;

public class ARPlatformInitializer : MonoBehaviour
{
    [SerializeField] GameObject m_Session;
    [SerializeField] GameObject m_SessionOrigin;

    IEnumerator Start() {
        if ((ARSession.state == ARSessionState.None) ||
            (ARSession.state == ARSessionState.CheckingAvailability))
        {
            yield return ARSession.CheckAvailability();
        }

        if (ARSession.state == ARSessionState.Unsupported)
        {
            //If we AR is unsupported we disable both GameObjects
            m_SessionOrigin.SetActive(false);
            m_Session.SetActive(false);
        }
        else
        {
            //If AR is supported we create our IsARPlayerComponent singleton in ClientWorld
            foreach (var world in World.All)
            {
                if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                {
                    world.EntityManager.CreateEntity(typeof(IsARPlayerComponent));
                }
            }

        }
    }
}
```

![Creating ARPlatformInitializer ](/files/-MSF_vy5U7guIlgX1xdi)

* Now let's create a new component (the component that is referenced in the script above, which is why you might have an error right now in Unity-- the component it references does not exist yet!)
* Create a C# script named "IsARPlayerComponent" in the Client/Components folder
* Paste the code snippet below into IsARPlayerComponent.cs:

```
using Unity.Entities;
using UnityEngine;

public struct IsARPlayerComponent : IComponentData
{
}
```

![Create IsARPlayerComponet](/files/-MSFaY9kZNCl3rbfcnnl)

* Now select the "AR Session" GameObject in Hierarchy again and drag the ARSession and ARSessionOrigin GameObjects from the Hierarchy into the appropriate fields in the ARPlatformInitializer component in Inspector&#x20;

![Setting the fields of ARPlatformInitializer](/files/-MSFasBr1-DymvmojeiU)

* Let's go to NavigationScene, hit play, and host a game to make sure that are GameObjects are indeed disabled (by expanding MainScene in Hierarchy while in playmode)

  ![AR GameObjects are inactive in MainScene](/files/-MSFbQvF9s1zlq3Fxr1x)

{% hint style="success" %}
&#x20;We have set up our two core AR GameObjects

* We added AR GameObjects to MainScene
  * AR Session
  * AR Session Origin
* We created ARPlatformInitializer
* We created IsARPlayerComponent
  {% endhint %}

## Configuring the AR Camera background using a Scriptable Render Pipeline

This is a necessary step when using AR Foundation with the Universal Render Pipeline.

We are going to be following the instructions here: <https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/ar-camera-background-with-scriptable-render-pipeline.html>

* In Assets/ navigate to UniversalRenderPipelineAsset\_Renderer and in the Inspector Add Renderer Feature "AR Background Renderer Feature"

![Adding AR Background rendering to our URP Pipeline Asset](/files/BU6yhSmlt0wJDBkedEjw)

{% hint style="success" %}
We will now be able to render the AR Background

* We updated our URP Pipeline Asset by adding a Renderer Feature for AR Backgrounds
  {% endhint %}

## iOS build configuration

We are going to create an iOS build configuration in Assets/BuildSettings.

* Navigate to BuildSettings, right-click, and select "Create", then, "Build", then, "Empty Build Configuration" and name it "iOS-Build"

![Creating iOS-Build](/files/-MSFc7_d7Ml9JdDlZs5G)

* Expand "Shared Configurations" at the top then click "+ Add Configuration"
* Drag "BaseBuildConfiguration" into the field and then hit "Apply"

![Adding BaseBuildConfiguration as a Shared Configuration](/files/-MSFcAhOB95nl8eHX5aw)

* Next on "Classic Scripting Settings" component&#x20;
  * choose "IL2CPP" as Scripting Backend
  * choose "Debug" as IL2CPP Compiler Configuration
  * under add "Classic Build Profile" component, choose "iOS" from the Platform dropdown, hit "Apply", and then  at the top of Inspector hit "Build and Run"
    * Sometimes you need to hit "Build and Run" twice as it fails the first time (sometimes even 3 times...)
      * "Editor's active Build Target needed to be switched to iOS..."

![Setting IL2CPP as Scripting Backend](/files/-MSFctu1WbrkuF2M9yLX)

* Next, in Xcode, make sure you set up your signing capabilities
  * Click on the "Unity-iPhone" project at on the left
  * Click on "Signing & Capabilities"
  * Make sure you are able to sign the application
* Hit "Play" in XCode to deploy the application&#x20;
  * you can either connect an iOS device to run your application or choose a simulated device as the destination

![Setting up Signing & Capabilities in Xcode and hitting Play to deploy the application to a connected device](/files/-MSFie0eWxt4Kd2m1xWI)

* Host a game

![Launching Asteroids project (now AR-enabled) on an iPhone](/files/-MSFo6Udm1IkHq0OA5Hb)

The new iPhones eat up some top and side margins in the UI. We will update this in the final "UI Updates" section.

{% hint style="success" %}
&#x20;We created an AR session on iOS devices

* We created our iOS build configuration
* Compiled our code to Xcode
* Built our app in Xcode
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Setting-Up-AR-Foundation'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Spawn a Player using AR Foundation

Code and workflows for using AR Foundation to spawn an AR Player

## What you'll develop on this page

![spawn an AR Player that is able to interact with desktop players by shooting and navigating](/files/-MSLkKNL4eD7bsH3jB0b)

Your project will be able to make use of AR Player input configurations when deployed on ARKit enabled platforms.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-to-AR-Player>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Updating controls

### Sampling AR pose

Almost all AR APIs are able to provide the device's "pose" (translation + rotation). AR Foundation provides this data through its "Pose Driver."

> #### AR Pose Driver <a href="#ar-pose-driver" id="ar-pose-driver"></a>
>
> The `AR Pose Driver` drives the local position and orientation of the parent GameObject according to the device's tracking information. The most common use case for this would be attaching the `ARPoseDriver` to the AR Camera to drive the camera's position and orientation in an AR scene.
>
> <img src="https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/images/ar-pose-driver.png" alt="AR Pose Driver" data-size="original">
>
> \
> From [AR Foundation's AR Pose Driver documentation](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/index.html)

In order to spawn an AR Player in our game, we need to grab AR pose data from AR Foundation and provide it to our ECS system. AR Foundation runs on MonoBehaviours, so we're going to need to use a pattern of dropping data "into" ECS (using the Entity Manager), and then pulling it back "out" when we need it. Currently, ECS does not provide a way to "push" the information out (i.e. by calling a MonoBehaviour method within a system).

The device's movement will control our AR player's movement. So if the device moves to the left, the player moves to the left. If the device moves to the right, the player moves to the right. On the client, we will sample the pose and use NetCode to send it to the server as ICommandData.&#x20;

* Let's create ARPoseComponent in the Client/Components folder
* Paste the code snippet below into ARPoseComponent.cs:

```
using Unity.Entities;
using Unity.Collections;
using Unity.Transforms;

public struct ARPoseComponent : IComponentData
{
    public Translation translation;
    public Rotation rotation;
}
```

![Creating ARPoseComponent](/files/-MSLFxJBpORRnkp3H_hd)

This is going to be the component that will update every time we sample pose data.

Now we need to actually create the MonoBehaviour that will grab the pose data, and then set ARPoseComponent. From the Unity AR Foundation documentation above, we know that the AR Pose Driver script updates the translation and rotation of the parent GameObject. We are going to add AR Pose Sampler to the same GameObject and pull its transformation and rotation.

* In MainScene, select the AR Camera that is nested in AR Session Origin, click "Add Component" in Inspector and create a new script called ARPoseSampler, and move the script to the Client folder

![Creating ARPoseSampler](/files/-MSLG0VvHAa4vmKSSLff)

* Paste the code snippet below into ARPoseSampler.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine.XR.ARFoundation;
using Unity.Mathematics;

public class ARPoseSampler : MonoBehaviour
{
    //We will be using ClientSimulationSystemGroup to update our ARPoseComponent
    private ClientSimulationSystemGroup m_ClientSimGroup;
    
    void Start()
    {
        //We grab ClientSimulationSystemGroup to update ARPoseComponent in our Update loop
        foreach (var world in World.All)
        {
            if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
            {
                //We create the ARPoseComponent that we will update with new data
                world.EntityManager.CreateEntity(typeof(ARPoseComponent));
                //We grab the ClientSimulationSystemGroup for our Update loop
                m_ClientSimGroup = world.GetExistingSystem<ClientSimulationSystemGroup>();
            }
        }        
    }

    // Update is called once per frame
    void Update()
    {
        //We create a new Translation and Rotation from the transform of the GameObject
        //The GameObject Translation and Rotation is updated by the pose driver
        var arTranslation = new Translation {Value = transform.position};
        var arRotation = new Rotation {Value = transform.rotation};
        //Now we update our ARPoseComponent with the updated Pose Driver data
        var arPose = new ARPoseComponent {
           translation = arTranslation,
           rotation = arRotation 
        };
        m_ClientSimGroup.SetSingleton<ARPoseComponent>(arPose);
    }
}
```

![Updating ARPoseSampler](/files/-MSLG6CEerWTIy8C_Y7v)

### Updating PlayerCommand

We are going to use PlayerCommand to send the pose data, so we'll need to update the script in order to do so.

We are also going to need to add a boolean to PlayerCommand to signify that the command was sent from an AR player, now that the server will need to differentiate between AR and desktop players.

* Paste the code snippet below into PlayerCommand.cs:

```
using Unity.Networking.Transport;
using Unity.NetCode;
using Unity.Burst;
using Unity.Entities;
using Unity. Transforms;
using Unity.Mathematics;


[GhostComponent(PrefabType = GhostPrefabType.AllPredicted)]
public struct PlayerCommand : ICommandData
{
    public uint Tick {get; set;}
    public byte right;
    public byte left;
    public byte thrust;
    public byte reverseThrust;
    public byte selfDestruct;
    public byte shoot;
    public float mouseX;
    public float mouseY;
    public byte isAR;
    public float3 arTranslation;
    public quaternion arRotation;
}

```

![Updating PlayerCommand](/files/-MSLGBA5yU66qq_HIV7C)

Now PlayerCommand is able to grab the pose and send the pose to the server.

### Create ARInputSystem

Rather than adding `if(ar)` statements into InputSystem we are going to create a new input system in Client/Systems just for AR players, named "ARInputSystem."

* Create ARInputSystem in the Client/Systems folder
* Paste the code snippet below into ARInputSystem.cs:

```
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.NetCode;
using Unity.Jobs;
using Unity.Collections;
using UnityEngine.XR;
using Unity.Physics;

[UpdateInGroup(typeof(GhostInputSystemGroup))]
public partial class ARInputSystem : SystemBase
{
    //We will need a command buffer for structural changes
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;
    //We will grab the ClientSimulationSystemGroup because we require its tick in the ICommandData
    private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;

    protected override void OnCreate()
    {
        //We set our variables
        m_ClientSimulationSystemGroup = World.GetOrCreateSystem<ClientSimulationSystemGroup>();
        m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
        //We will only run this system if the player is in game and if the palyer is an AR player
        RequireSingletonForUpdate<NetworkStreamInGame>();
        RequireSingletonForUpdate<IsARPlayerComponent>();
    }

    protected override void OnUpdate()
    {
        //The only inputs is for shooting or for self destruction
        //Movement will be through the ARPoseComponent
        byte selfDestruct, shoot;
        selfDestruct = shoot = 0;

        //More than 2 touches will register as self-destruct
        if (Input.touchCount > 2)
        {
            selfDestruct = 1;
        }
        //A single touch will register as shoot
        if (Input.touchCount == 1)
        {
            shoot = 1;
        }

        //We grab the AR pose to send to the server for movement
        var arPoseDriver = GetSingleton<ARPoseComponent>();
        //We must declare our local variables before the .ForEach()
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer();
        var inputFromEntity = GetBufferFromEntity<PlayerCommand>();
        var inputTargetTick = m_ClientSimulationSystemGroup.ServerTick;

        TryGetSingletonEntity<PlayerCommand>(out var targetEntity);
        Job.WithCode(() => {
        if (targetEntity == Entity.Null)
        {
            if (shoot != 0)
            {
                var req = commandBuffer.CreateEntity();
                commandBuffer.AddComponent<PlayerSpawnRequestRpc>(req);
                commandBuffer.AddComponent(req, new SendRpcCommandRequestComponent());
            }
        }
        else
        {
            var input = inputFromEntity[targetEntity];
            input.AddCommandData(new PlayerCommand{Tick = inputTargetTick,
            selfDestruct = selfDestruct, shoot = shoot,
            isAR = 1,
            arTranslation = arPoseDriver.translation.Value,
            arRotation = arPoseDriver.rotation.Value});

        }
        }).Schedule();
        
        //We need to add the jobs dependency to the command buffer
        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

![](/files/-MSL_M5terMhofVWDcnR)

### Update response system

Next, we need to update one of our response systems. We will only need to update the InputResponseMovementSystem (and not InputResponseSpawnSystem) because the spawning of bullets does not need to be altered. If the PlayerCommand has shoot = 1, then the bullet will spawn.

* Paste the code snippet below into InputResponseMovementSystem.cs:

```
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.NetCode;
using Unity.Networking.Transport.Utilities;
using Unity.Collections;
using Unity.Physics;
using Unity.Jobs;
using UnityEngine;

//InputResponseMovementSystem runs on both the Client and Server
//It is predicted on the client but "decided" on the server
[UpdateInWorld(TargetWorld.ClientAndServer)] 
public partial class InputResponseMovementSystem : SystemBase
{
    //This is a special NetCode group that provides a "prediction tick" and a fixed "DeltaTime"
    private GhostPredictionSystemGroup m_PredictionGroup;
    

    protected override void OnCreate()
    {
        // m_BeginSimEcb = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();

        //We will grab this system so we can use its "prediction tick" and "DeltaTime"
        m_PredictionGroup = World.GetOrCreateSystem<GhostPredictionSystemGroup>();
        
    }

    protected override void OnUpdate()
    {
        //No need for a CommandBuffer because we are not making any structural changes to any entities
        //We are setting values on components that already exist
        // var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();

        //These are special NetCode values needed to work the prediction system
        var currentTick = m_PredictionGroup.PredictingTick;
        var deltaTime = m_PredictionGroup.Time.DeltaTime;

        //We must declare our local variables before the .ForEach()
        var playerForce = GetSingleton<GameSettingsComponent>().playerForce;

        //We will grab the buffer of player commands from the player entity
        var inputFromEntity = GetBufferFromEntity<PlayerCommand>(true);

        //We are looking for player entities that have PlayerCommands in their buffer
        Entities
        .WithReadOnly(inputFromEntity)
        .WithAll<PlayerTag, PlayerCommand>()
        .ForEach((Entity entity, ref Translation translation, ref Rotation rotation, ref PhysicsVelocity velocity,
                in GhostOwnerComponent ghostOwner, in PredictedGhostComponent prediction) =>
        {
            //Here we check if we SHOULD do the prediction based on the tick, if we shouldn't, we return
            if (!GhostPredictionSystemGroup.ShouldPredict(currentTick, prediction))
                return;

            //We grab the buffer of commands from the player entity
            var input = inputFromEntity[entity];

            //We then grab the Command from the current tick (which is the PredictingTick)
            //if we cannot get it at the current tick we make sure shoot is 0
            //This is where we will store the current tick data
            PlayerCommand inputData;
            if (!input.GetDataAtTick(currentTick, out inputData))
                inputData.shoot = 0;

            if (inputData.right == 1)
            {   //thrust to the right of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(1,0,0)).xyz * playerForce * deltaTime;                
            }
            if (inputData.left == 1)
            {   //thrust to the left of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(-1,0,0)).xyz * playerForce * deltaTime;
            }
            if (inputData.thrust == 1)
            {   //thrust forward of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(0,0,1)).xyz * playerForce * deltaTime;
            }
            if (inputData.reverseThrust == 1)
            {   //thrust backwards of where the player is facing
                velocity.Linear += math.mul(rotation.Value, new float3(0,0,-1)).xyz * playerForce * deltaTime;
            }

            
            if (inputData.mouseX != 0 || inputData.mouseY != 0)
            {   //move the mouse
                //here we have "hardwired" the look speed, we could have included this in the GameSettingsComponent to make it configurable
                float lookSpeedH = 2f;
                float lookSpeedV = 2f;
                Quaternion currentQuaternion = rotation.Value; 
                float yaw = currentQuaternion.eulerAngles.y;
                float pitch = currentQuaternion.eulerAngles.x;

                //MOVING WITH MOUSE
                yaw += lookSpeedH * inputData.mouseX;
                pitch -= lookSpeedV * inputData.mouseY;
                Quaternion newQuaternion = Quaternion.identity;
                newQuaternion.eulerAngles = new Vector3(pitch,yaw, 0);
                rotation.Value = newQuaternion;
            }
            //If the PlayerCommand is from an AR player we will update movement in a special way
            if (inputData.isAR == 1)
            {
                //The player is going to be (0,-2,10) relative to the AR pose
                //This will make the player appear a bit lower and in front of the camera, making it easier to control
                translation.Value = (inputData.arTranslation) - (math.mul(rotation.Value, new float3(0,2,0)).xyz) + (math.mul(rotation.Value, new float3(0,0,10)).xyz);
                //The player will face the same direction as the camera
                rotation.Value = (inputData.arRotation);
            }

        }).ScheduleParallel();

        //No need to .AddJobHandleForProducer() because we did not need a CommandBuffer to make structural changes
    }   
}
```

![Updating InputResponseMovementSystem](/files/-MSLI9v7lUTsYugfQKim)

In InputResponseMovementSystem, we hardcoded the offset of our camera to our player as (0, 2, -10). We *instead* could've included this offset as part of our GameSettingsComponent, but we decided to leave it hardcoded for the sake of simplicity.

### Update spawning classification for AR players

Now we need to update our PlayerGhostSpawnClassificationSystem so it does not add the camera to an AR Player (remember that in PlayerGhostSpawnClassificationSystem we add a Camera to the player after spawning).&#x20;

We are going to check for the IsARPlayerComponent singleton and if it exists we will not add the Camera to the player.

* Paste the code snippet below into PlayerGhostSpawnClassificationSystem.cs:

```
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.NetCode;
using UnityEngine;
using Unity.Transforms;
using Unity.Mathematics;

//We are updating only in the client world because only the client must specify exactly which player entity it "owns"
[UpdateInWorld(TargetWorld.Client)]
//We will be updating after NetCode's GhostSpawnClassificationSystem because we want
//to ensure that the PredictedGhostComponent (which it adds) is available on the player entity to identify it
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
[UpdateAfter(typeof(GhostSpawnClassificationSystem))]
public partial class PlayerGhostSpawnClassificationSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //We will store the Camera prefab here which we will attach when we identify our player entity
    private Entity m_CameraPrefab;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetExistingSystem<BeginSimulationEntityCommandBufferSystem>();

        //We need to make sure we have NCE before we start the update loop (otherwise it's unnecessary)
        RequireSingletonForUpdate<NetworkIdComponent>();
        RequireSingletonForUpdate<CameraAuthoringComponent>();
    }

    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_CameraPrefab == Entity.Null)
        {
            //We grab our camera and set our variable
            m_CameraPrefab = GetSingleton<CameraAuthoringComponent>().Prefab;
            return;
        }
        
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();
        
        //We must declare our local variables before using them
        var camera = m_CameraPrefab;
        //The "playerEntity" is the NCE
        var networkIdComponent = GetSingleton<NetworkIdComponent>();
        //The false is to signify that the data will NOT be read-only
        var commandTargetFromEntity = GetComponentDataFromEntity<CommandTargetComponent>(false);
        //Check if this is an AR player
        IsARPlayerComponent arComponent;
        var isAR = TryGetSingleton<IsARPlayerComponent>(out arComponent);

        //We will look for Player prefabs that we have not added a "PlayerClassifiedTag" to (which means we have checked the player if it is "ours")
        Entities
        .WithAll<PlayerTag>()
        .WithNone<PlayerClassifiedTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in GhostOwnerComponent ghostOwnerComponent) =>
        {
            // If this is true this means this Player is mine (because the GhostOwnerComponent value is equal to the NetworkId)
            // Remember the GhostOwnerComponent value is set by the server and is ghosted to the client
            if (ghostOwnerComponent.NetworkId == networkIdComponent.Value)
            {
                if(!isAR)
                {
                    //This creates our camera
                    var cameraEntity = commandBuffer.Instantiate(entityInQueryIndex, camera);
                    //This is how you "attach" a prefab entity to another
                    commandBuffer.AddComponent(entityInQueryIndex, cameraEntity, new Parent { Value = entity });
                    commandBuffer.AddComponent(entityInQueryIndex, cameraEntity, new LocalToParent() );
                }
            }
            // This means we have classified this Player prefab
            commandBuffer.AddComponent(entityInQueryIndex, entity, new PlayerClassifiedTag() );

        }).ScheduleParallel();

        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}

```

![Updating PlayerGhostSpawnClassificationSystem](/files/-MSLHe8RY4oGPhCVrT0R)

* Let's hit play, host a game, and spawn a player

![Hitting play and our camera not appearing](/files/-MSLIhFKwZq4qZy2PVpN)

If you remember back to our "DOTS Entity Component System ECS" section, we added the following to our "Scripting Define Symbols": `HYBRID_ENTITIES_CAMERA_CONVERSION`. Now that we are building for iOS, we also need to add this to our iOS Player Settings. This way when we hit "play" in the Unity editor we are still able to render our camera (instead of constantly switching back and forth between Desktop and iOS build).

* Go to "Player Settings..."  (File > Build Settings > Player Settings button in the bottom left) and add `HYBRID_ENTITIES_CAMERA_CONVERSION` to the "Scripting Define Symbols" field when Player is selected, after "`UNITY_XR_ARKIT_LOADER_ENABLED;`"&#x20;
  * Hit apply
* Save!

![Updating "Player Settings..." for our iOS build with HYBRID\_ENTITIES\_CAMERA\_CONVERSION](/files/-MSLJeiTTJYan-fYfJgr)

* Ok, now you need to restart your computer (yup, we're being serious; in our testing, this was a necessary step in order for the camera to work in the editor again)
* Go to the BuildSettings folder, choose your development platform, and hit "Build and Run"  (this will cause the Editor to switch)
  * The top of the Editor will change from "iOS" to "PC, Mac & Linux"
* Hit play, host a game, and spawn a player

![After switching back to development platform player camera works again](/files/-MSLOYh_y1a_2dXqSzky)

* Now let's try something different. Unclick play, go back to BuildSettings folder, select iOS-Build and hit Build and Run (this will cause editor to switch)

![Selecting our iOS-Build and hitting Build and Run to switch back](/files/-MSLOfBn-dvjDxIT1c25)

* Once the Editor resets to "iOS" hit play, host a game, and spawn a player

![Spawning a player now generates the camera again](/files/-MSLQ4-aH4SWM1pnUFgI)

* Now click "Build and Run" again to build to Xcode

![Hitting Build and Run after the Editor reset to iOS](/files/-MSLR0G1vbdAs6kweI4k)

* In the deployed app, host a game
* Tap on the screen to spawn and shoot, use 3 fingers to self-destruct, and move the device to move the player

![Updated AR Player is able to spawn, navigate, and fire](/files/-MSLVVGcLLbo2XCcPFnu)

{% hint style="success" %}
&#x20;We are now able to control our AR player

* We created ARPoseSampler
* We updated PlayerCommand
* We updated InputResponseMovementSystem
* We updated PlayerGhostSpawnClassificationSystem
  {% endhint %}

## Updating spawning

Currently, InputMovementResponseSystem places the player ahead of the AR pose, regardless of where our server spawns our AR player. This isn't great because it means as soon as a player is destroyed (via self-destructing or via another player shooting at them), it will regenerate right where they were, which is not very fun.

We need to update our flow so that when our AR Player is spawned we can tell our AR Pose Driver to move the camera to behind spawn location. We need to communicate to Pose Driver and say "you are now at translation + rotation" position.

A good place to do this is PlayerGhostSpawnClassificationSystem. When we receive our player ghost on the client, we will create a new singleton called "SpawnPositionForARComponent" which will be the location our server spawned our player.

Within ARPoseSampler we will add logic to check for this component, and if it exists (which means there was a new spawn), we will update the pose to be behind the player and then delete the singleton.

* Create SpawnPositionForARComponent in the Client/Components folder
* Paste the code snippet below into SpawnPositionForARComponent.cs:

```
using Unity.Entities;
using UnityEngine;
using Unity.Mathematics;

public struct SpawnPositionForARComponent : IComponentData
{
    public float3 spawnTranslation;
    public quaternion spawnRotation;
}
```

![](/files/-MSLWzB2l4eZI6lLU7ms)

* Now we need to update PlayerGhostSpawnClassificationSystem in order to create a singleton with the spawn position
* Paste the code snippet below into PlayerGhostSpawnClassificationSystem.cs:

```
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.NetCode;
using UnityEngine;
using Unity.Transforms;
using Unity.Mathematics;

//We are updating only in the client world because only the client must specify exactly which player entity it "owns"
[UpdateInWorld(TargetWorld.Client)]
//We will be updating after NetCode's GhostSpawnClassificationSystem because we want
//to ensure that the PredictedGhostComponent (which it adds) is available on the player entity to identify it
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
[UpdateAfter(typeof(GhostSpawnClassificationSystem))]
public partial class PlayerGhostSpawnClassificationSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem m_BeginSimEcb;

    //We will store the Camera prefab here which we will attach when we identify our player entity
    private Entity m_CameraPrefab;

    protected override void OnCreate()
    {
        m_BeginSimEcb = World.GetExistingSystem<BeginSimulationEntityCommandBufferSystem>();

        //We need to make sure we have NCE before we start the update loop (otherwise it's unnecessary)
        RequireSingletonForUpdate<NetworkIdComponent>();
        RequireSingletonForUpdate<CameraAuthoringComponent>();
    }

    protected override void OnUpdate()
    {
        //Here we set the prefab we will use
        if (m_CameraPrefab == Entity.Null)
        {
            //We grab our camera and set our variable
            m_CameraPrefab = GetSingleton<CameraAuthoringComponent>().Prefab;
            return;
        }
        
        var commandBuffer = m_BeginSimEcb.CreateCommandBuffer().AsParallelWriter();
        
        //We must declare our local variables before using them
        var camera = m_CameraPrefab;
        //The "playerEntity" is the NCE
        var networkIdComponent = GetSingleton<NetworkIdComponent>();
        //The false is to signify that the data will NOT be read-only
        var commandTargetFromEntity = GetComponentDataFromEntity<CommandTargetComponent>(false);
        //Check if this is an AR player
        IsARPlayerComponent arComponent;
        var isAR = TryGetSingleton<IsARPlayerComponent>(out arComponent);

        //We will look for Player prefabs that we have not added a "PlayerClassifiedTag" to (which means we have checked the player if it is "ours")
        Entities
        .WithAll<PlayerTag>()
        .WithNone<PlayerClassifiedTag>()
        .ForEach((Entity entity, int entityInQueryIndex, in GhostOwnerComponent ghostOwnerComponent, in Translation translation, in Rotation rotation) =>
        {
            // If this is true this means this Player is mine (because the GhostOwnerComponent value is equal to the NetworkId)
            // Remember the GhostOwnerComponent value is set by the server and is ghosted to the client
            if (ghostOwnerComponent.NetworkId == networkIdComponent.Value)
            {
                if(!isAR)
                {
                    //This creates our camera
                    var cameraEntity = commandBuffer.Instantiate(entityInQueryIndex, camera);
                    //This is how you "attach" a prefab entity to another
                    commandBuffer.AddComponent(entityInQueryIndex, cameraEntity, new Parent { Value = entity });
                    commandBuffer.AddComponent(entityInQueryIndex, cameraEntity, new LocalToParent() );
                }
                //If we are an AR player we will create SpawnPositionForARoComponent
                if (isAR)
                {
                    var spawnLocation = commandBuffer.CreateEntity(entityInQueryIndex);
                    commandBuffer.AddComponent(entityInQueryIndex, spawnLocation, new SpawnPositionForARComponent {
                        spawnTranslation = translation.Value,
                        spawnRotation = rotation.Value
                    });
                }
            }
            // This means we have classified this Player prefab
            commandBuffer.AddComponent(entityInQueryIndex, entity, new PlayerClassifiedTag() );

        }).ScheduleParallel();

        m_BeginSimEcb.AddJobHandleForProducer(Dependency);
    }
}
```

![Updating PlayerGhostSpawnClassificationSystem to provide spawn location](/files/-MSLXh4JSfvqp9vNve0l)

Now we will update ARPoseSampler to check for a SpawnPositionForARComponent, and if it exists we update the pose to be behind the player spawn. We will use ARSessionOrigin's MakeContentAppearAt method, explained in Unity's AR Foundation documentation below:

> **MakeContentAppearAt(Transform, Quaternion)**
>
> Makes `content` appear to have orientation `rotation` relative to the `Camera`.
>
> **Declaration**
>
> ```csharp
> public void MakeContentAppearAt(Transform content, Quaternion rotation)
> ```
>
> **Parameters**

| Type                                                                                        | Name     | Description                                                                |
| ------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------- |
| [Transform](https://docs.unity3d.com/2020.3/Documentation/ScriptReference/Transform.html)   | content  | The `Transform` of the content you wish to affect.                         |
| [Quaternion](https://docs.unity3d.com/2020.3/Documentation/ScriptReference/Quaternion.html) | rotation | The rotation the content should appear to be in, relative to the `Camera`. |

> **Remarks**
>
> This method does not actually change the `Transform` of content; instead, it updates the `ARSessionOrigin`'s `Transform` so that the content appears to be in the requested orientation.
>
> \
> From [AR Foundation's MakeContentAppearAt documentation](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/api/UnityEngine.XR.ARFoundation.ARSessionOrigin.html#UnityEngine_XR_ARFoundation_ARSessionOrigin_trackablesParent)

Because we don't want to actually move content, but instead want to move our AR Pose, we are going to provide the inverse of our translation and rotation to the MakeContentAppearAt method.

Also, we are going to need to save our updates to AR Session Origin. This is because we need to "undo" them before we add another update. That is because MakeContentAppearAt is additive, it does not reset on each call. So if we did not "undo" this, we would be pushing our pose further and further away.

* Paste the code snippet below into ARPoseSampler.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.NetCode;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine.XR.ARFoundation;
using Unity.Mathematics;

public class ARPoseSampler : MonoBehaviour
{
    //We will be using ClientSimulationSystemGroup to update our ARPoseComponent
    private ClientSimulationSystemGroup m_ClientSimGroup;
    //We will be using Client World when destroying our SpawnPositionForARComponent
    private World m_ClientWorld;

    //This is the query we will use for SpawnPositionForARComponent
    private EntityQuery m_SpawnPositionQuery;
    //This is the AR Session Origin from the hierarchy that we will use to move the camera
    public ARSessionOrigin m_ARSessionOrigin;
    //We will save our updates to translation and rotation so we can "undo" them before our next update
    //We need to "undo" our updates because of how AR Session Origin MakeContentAppearAt() works
    private float3 m_LastTranslation = new float3(0,0,0); //We set the initial value to 0
    private quaternion m_LastRotation = new quaternion(0,0,0,1);  //We set the initial value to the identity
    
    void Start()
    {
        //We grab ClientSimulationSystemGroup to update ARPoseComponent in our Update loop
        foreach (var world in World.All)
        {
            if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
            {
                //Set our world
                m_ClientWorld = world;
                //We create the ARPoseComponent that we will update with new data
                world.EntityManager.CreateEntity(typeof(ARPoseComponent));
                //We grab the ClientSimulationSystemGroup for our Update loop
                m_ClientSimGroup = world.GetExistingSystem<ClientSimulationSystemGroup>();
                //Now we set our query for SpawnPositionForARComponent
                m_SpawnPositionQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadWrite<SpawnPositionForARComponent>());
            }
        }        
    }

    // Update is called once per frame
    void Update()
    {
        //We create a new Translation and Rotation from the transform of the GameObject
        //The GameObject Translation and Rotation is updated by the pose driver
        var arTranslation = new Translation {Value = transform.position};
        var arRotation = new Rotation {Value = transform.rotation};
        //Now we update our ARPoseComponent with the updated Pose Driver data
        var arPose = new ARPoseComponent {
           translation = arTranslation,
           rotation = arRotation 
        };
        m_ClientSimGroup.SetSingleton<ARPoseComponent>(arPose);

        //If the player was spawned, we will move the AR camera to behind the spawn location
        if(!m_SpawnPositionQuery.IsEmptyIgnoreFilter)
        {
            //We grab the component from the Singleton
            var spawnPosition = m_ClientSimGroup.GetSingleton<SpawnPositionForARComponent>();
            // Debug.Log("spawn position is: " + spawnPosition.spawnTranslation.ToString());     
            
            //We set the new pose to behind the player (0, 2, -10) (this is the same value the player is put in front of the pose in InputResponseMovementSystem)
            var newPoseTranslation = (spawnPosition.spawnTranslation) + (math.mul(spawnPosition.spawnRotation, new float3(0,2,0)).xyz) - (math.mul(spawnPosition.spawnRotation, new float3(0,0,10)).xyz);
            //The rotation will be the same
            var newPoseRotation = (spawnPosition.spawnRotation);
            
            // Debug.Log("calculated camera position is: " + newPoseTranslation.ToString());

            //MakeContentAppearAt requires a transform even though it is never used so we create a dummy transform
            Transform dummyTransform = new GameObject().transform;
            //First we will undo our last MakeContentAppearAt to go back to "normal"
            m_ARSessionOrigin.MakeContentAppearAt(dummyTransform, -1f*m_LastTranslation, Quaternion.Inverse(m_LastRotation));
            
            //Now we will update our LastTranslation and LastRotations to the values we are about to use
            //Because of how MakeContentAppearAt works we must do the inverse to move our camera where we want it
            m_LastTranslation = -1f * newPoseTranslation;
            m_LastRotation = Quaternion.Inverse(newPoseRotation);
            //Now that we have set the variables we will use them to adjust the AR pose
            m_ARSessionOrigin.MakeContentAppearAt(dummyTransform, m_LastTranslation, m_LastRotation);

            // Debug.Log("transform after MakeContentAppearAt: " + transform.position.ToString());
            //Now we delete the entity so this only runs during an initial spawn
            m_ClientWorld.EntityManager.DestroyEntity(m_ClientSimGroup.GetSingletonEntity<SpawnPositionForARComponent>());
        }
    }
}
```

![Updating ARPoseSampler to ingest SpawnPositionForARComponent](/files/-MSLaRgOE6O9wPrVP7Gd)

* Make sure MainScene is open, and then drag AR Session Origin from the Hierarchy into the appropriate field in the ARPoseSampler component within AR Camera (which is nested in AR Session Origin in Hierarchy)

![Updating ARPoseSamplers public field with AR Session Orign](/files/-MSLaXgvTIONLX-THIN0)

* Let's build and run for iOS and check it out

{% hint style="success" %}
&#x20;We are now able to re-spawn as an AR player

* We created SpawnPositionForARComponent
* We updated PlayerGhostSpawnClassificationSystem
* We updated ARPoseSampler
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'Updating-to-AR-Player'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}


# Update UI using AR Foundation

Code and workflows to optimize and update the UI in our AR enabled project

## What you'll develop on this page

![Shooting down AR player](/files/-MSQkJtqhXrWgeh-yqBq)

![Getting shot down by desktop player](/files/-MSQkNjGPOv_IYOSBQrS)

We will update the UI to both fit better and to dynamically update the instructions shown to AR players.

Github branch link: <https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/UI-Updates>

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## Updating UI

### Updating the instructions

We are going to update the instructions on the bottom-left of the screen in ARPlatformInitializer.

We are going to grab each of the instructions and update it with new text once we know that the project is running on an AR platform.

* Paste the code snippet below into ARPlatformInitializer.cs:

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.UIElements;

public class ARPlatformInitializer : MonoBehaviour
{
    [SerializeField] GameObject m_Session;
    [SerializeField] GameObject m_SessionOrigin;

    //This is how we will grab access to the UI elements we need to update
    public UIDocument m_GameUIDocument;
    private VisualElement m_GameManagerUIVE;
    private VisualElement m_BottomLeft;
    private Label m_1stInstruction;
    private Label m_2ndInstruction;
    private Label m_3rdInstruction;
    private Label m_4thInstruction;

    void OnEnable()
    {
        //We set the labels that we will need to update
        m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
        m_BottomLeft = m_GameManagerUIVE.Q<VisualElement>("bottom-left");
        m_4thInstruction = m_GameManagerUIVE.Q<Label>("instructions-4");
        m_3rdInstruction = m_GameManagerUIVE.Q<Label>("instructions-3");
        m_2ndInstruction = m_GameManagerUIVE.Q<Label>("instructions-2");
        m_1stInstruction = m_GameManagerUIVE.Q<Label>("instructions-1");
    }

    IEnumerator Start() {
        if ((ARSession.state == ARSessionState.None) ||
            (ARSession.state == ARSessionState.CheckingAvailability))
        {
            yield return ARSession.CheckAvailability();
        }

        if (ARSession.state == ARSessionState.Unsupported)
        {
            //If we AR is unsupported we disable both GameObjects
            m_SessionOrigin.SetActive(false);
            m_Session.SetActive(false);
        }
        else
        {
            //If AR is supported we create our IsARPlayerComponent singleton in ClientWorld
            foreach (var world in World.All)
            {
                if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
                {
                    world.EntityManager.CreateEntity(typeof(IsARPlayerComponent));
                }
            }

            //Due to a UI Toolkit bug we cannot currently update existing labels without getting wacky behavior
            // https://forum.unity.com/threads/updating-labels-causes-a-gap-on-first-ui-view.1049081/
            //So we will remove all labels and attach new ones to our bottom left container
            m_BottomLeft.Remove(m_4thInstruction);
            m_BottomLeft.Remove(m_3rdInstruction);
            m_BottomLeft.Remove(m_2ndInstruction);
            m_BottomLeft.Remove(m_1stInstruction);

            //Now that our container is empty we make 3 new labels for our 3 new instructions
            Label instruction1 = new Label();
            Label instruction2 = new Label();
            Label instruction3 = new Label();

            //Now we add our instruction-text class to our labels so they have the same styling as before
            instruction1.AddToClassList("instruction-text");
            instruction2.AddToClassList("instruction-text");
            instruction3.AddToClassList("instruction-text");

            //Now we update the instruction text in the labels
            instruction3.text = "Tap with 1 finger to spawn and shoot";
            instruction2.text = "Move device to move player";
            instruction1.text = "Tap with 3 fingers to self-destruct";

            //Because we flex grow upwards we start with the bottom instruction (instruction 1) and then add the rest
            m_BottomLeft.Add(instruction1);
            m_BottomLeft.Add(instruction2);
            m_BottomLeft.Add(instruction3);
        }
    }
}
```

![Updating ARPlatformInitializer](/files/-MSQTmPA3jw31J0EU0Fp)

* In MainScene drag the GameUI GameObject from Hierarchy into the Game UI Document field within the AR Platform Initializer component in Inspector when AR Session is selected in Hierarchy

![Moving GameUI into ARPlatformInitializer compoinent](/files/-MSQTnwrXZQtQsMae4eT)

* Go to the BuildSettings folder, select iOS-Build, then click "Build and Run" in Inspector&#x20;
* Then launch the app

![Hitting "Build and run" for the iOS-Build](/files/-MSQTunGa6ysVmt2w6-e)

![Updating instructions dynamically for AR players](/files/-MSQY_aLZ3DkvBnAAJyC)

{% hint style="success" %}
&#x20;We now have dynamic Game UI based on platform type

* We updated ARPlatformInitializer
  {% endhint %}

### Updating the styling

We are going to update the styling to update the padding in the footer and header. Currently, the curve and notch in newer iPhones cover up quite a bit at the top and bottom of the screen. Although adjusting the styling to account for newer iPhones is not required (well, none of this is), we are explaining this just to make your project a little easier to navigate. We will also update our logo to use an SVG.

{% file src="/files/-MSQPNDFxs2U8MYPg9rK" %}
Moetsi Logo SVG
{% endfile %}

* Add the SVG file to the UI folder
  * With your SVG selected go to the Inspector and select
    * "UI Toolkit Vector Image" in the drop-down list for Generated Asset Type
    * Target Resolution 2160p
    * Click "Apply"

![Adding Moetsi Logo SVG to UI/ as a UI Toolkit Vector Image](/files/-MSQVFgSoO6f33RqyYm7)

* Paste the code snippet below into TitleScreenUI USS:

```
.screen {
    flex-grow: 1;
    font-size: 20px;
    align-items: stretch;
    background-color: rgb(255, 255, 255);
}
.quit-button {
    margin-right: 10px;
    margin-left: 0;
    width: 120px;
    height: 68px;
    font-size: 24px;
    color: rgb(0, 0, 0);
    background-color: rgba(0, 0, 0, 0);
    border-left-color: rgb(0, 0, 0);
    border-right-color: rgb(0, 0, 0);
    border-top-color: rgb(0, 0, 0);
    border-bottom-color: rgb(0, 0, 0);
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
}
.quit-button:hover {
    background-color: rgba(0, 0, 0, 0);
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}
.quit-button:active {
    background-color: rgb(0, 0, 0);
    color: rgb(255, 255, 255);
}
.main-menu-button {
    width: 219px;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
}
.header {
    position: absolute;
    flex-direction: row;
    justify-content: space-between;
    flex-grow: 1;
    top: 0;
    width: 100%;
    align-items: flex-start;
    padding-top: 85px;
}
.main-content {
    position: absolute;
    top: 108px;
    left: auto;
    right: auto;
    bottom: auto;
    max-width: 550px;
    width: 100%;
    height: auto;
    align-items: center;
    padding-left: 20px;
    padding-right: 20px;
    -unity-font: url('/Assets/UI/Fonts/HV.ttf');
    color: rgb(0, 0, 0);
}
.title {
    font-size: 64px;
    margin-top: 100px;
    -unity-font: url('/Assets/UI/Fonts/Cervo.otf');
    color: rgb(0, 0, 0);
}
.section-title-container {
    width: 100%;
    margin-top: 100px;
}
.section-title {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 36px;
    color: rgb(0, 0, 0);
}
.blue-button {
    width: 100%;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 0;
    margin-right: 0;
    margin-top: 21px;
    margin-bottom: 0;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    height: 68px;
    border-left-color: rgb(150, 191, 208);
    border-right-color: rgb(150, 191, 208);
    border-top-color: rgb(150, 191, 208);
    border-bottom-color: rgb(150, 191, 208);
    background-color: rgba(0, 0, 0, 0);
    font-size: 24px;
    color: rgb(150, 191, 208);
    flex-wrap: wrap;
    white-space: normal;
}
.blue-button:hover {
    border-left-width: 7px;
    border-right-width: 7px;
    border-top-width: 7px;
    border-bottom-width: 7px;
    background-color: rgba(0, 0, 0, 0);
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
}
.blue-button:active {
    background-color: rgb(150, 191, 208);
    color: rgb(255, 255, 255);
}
.green-button {
    left: auto;
    right: auto;
    margin-left: 0;
    margin-right: 0;
    margin-top: 36px;
    margin-bottom: 0;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    width: 100%;
    height: 120px;
    color: rgb(160, 194, 114);
    font-size: 36px;
    background-color: rgba(0, 0, 0, 0);
    border-left-color: rgb(160, 194, 114);
    border-right-color: rgb(160, 194, 114);
    border-top-color: rgb(160, 194, 114);
    border-bottom-color: rgb(160, 194, 114);
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    flex-wrap: wrap;
    white-space: normal;
}
.green-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 7px;
    border-right-width: 7px;
    border-top-width: 7px;
    border-bottom-width: 7px;
    background-color: rgba(0, 0, 0, 0);
}
.green-button:active {
    background-color: rgb(160, 194, 114);
    color: rgb(255, 255, 255);
}
.data-section {
    width: 100%;
    background-color: rgb(255, 255, 255);
}
.data-section-input {
    flex-direction: column-reverse;
    height: 49px;
    margin-left: 0;
    margin-right: 0;
    margin-top: 36px;
    margin-bottom: 0;
    font-size: 36px;
    color: rgb(160, 194, 114);
    border-bottom-width: 4px;
    border-bottom-color: rgb(160, 194, 114);
    border-top-color: rgb(160, 194, 114);
    border-left-color: rgb(160, 194, 114);
    border-right-color: rgb(160, 194, 114);
    width: auto;
    background-color: rgba(0, 0, 0, 0);
}
.data-section-label {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    color: rgb(160, 194, 114);
}
.unity-base-field {
}
.screen-scroll-container {
    flex-grow: 1;
    background-color: rgb(255, 255, 255);
}
.quit-game-button {
    flex-direction: column-reverse;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    background-color: rgba(0, 0, 0, 0);
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    width: 219px;
    height: 68px;
    border-left-color: rgb(255, 255, 255);
    border-right-color: rgb(255, 255, 255);
    border-top-color: rgb(255, 255, 255);
    border-bottom-color: rgb(255, 255, 255);
    font-size: 24px;
    color: rgb(255, 255, 255);
    white-space: normal;
}
.quit-game-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}
.quit-game-button:active {
    background-color: rgb(255, 255, 255);
    color: rgb(0, 0, 0);
}
.logo {
    flex-grow: 1;
    max-width: 300px;
    height: 68px;
    margin-left: 10px;
    background-image: url('/Assets/UI/Moetsi Logo SVG.svg');
    -unity-background-scale-mode: scale-to-fit;
    padding-top: 10px;
}
.local-games-list-container {
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 200px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    border-left-color: rgb(0, 0, 0);
    border-right-color: rgb(0, 0, 0);
    border-top-color: rgb(0, 0, 0);
    border-bottom-color: rgb(0, 0, 0);
}
.local-games-list {
    width: 100%;
    height: 100%;
}
.or {
    color: rgb(0, 0, 0);
    margin-top: 20px;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 36px;
}
.HostGameScreen {
    align-items: center;
}
.JoinGameScreen {
    align-items: center;
}
.ManualConnectScreen {
    align-items: center;
}
.row {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    height: 74px;
}
.game-name-data {
    margin-left: 17px;
}
.list-item-game-name {
    margin-left: 0;
    font-size: 24px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    color: rgb(150, 191, 208);
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
}
.list-item-game-name-label {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 10px;
}
.list-item-button {
    width: 141px;
    height: 46px;
    margin-left: 0;
    margin-right: 12px;
    margin-top: 0;
    margin-bottom: 0;
    border-left-color: rgba(0, 0, 0, 0);
    border-right-color: rgba(0, 0, 0, 0);
    border-top-color: rgba(0, 0, 0, 0);
    border-bottom-color: rgba(0, 0, 0, 0);
}

```

![Updating TitleScreenUI USS](/files/-MSQV3f2BEp9ZduuH95n)

If you want to use a different SVG (not the Moetsi logo SVG), you will need to update the name of the SVG in the .logo class.

* Paste the code snippet below into GameUI USS:

```
.quit-game-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}
.quit-game-button:active {
    background-color: rgb(255, 255, 255);
    color: rgb(0, 0, 0);
}
.quit-game-button {
    flex-direction: column-reverse;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    background-color: rgba(0, 0, 0, 0);
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    height: 68px;
    border-left-color: rgb(255, 255, 255);
    border-right-color: rgb(255, 255, 255);
    border-top-color: rgb(255, 255, 255);
    border-bottom-color: rgb(255, 255, 255);
    font-size: 24px;
    color: rgb(255, 255, 255);
    white-space: normal;
    max-width: 140px;
    width: 100%;
}
.game-ui-screen {
    background-color: rgba(0, 0, 0, 0);
    justify-content: space-between;
    color: rgb(255, 255, 255);
}
.game-ui-header {
    height: auto;
    flex-direction: row;
    align-items: flex-start;
}
.top-right-container {
    margin-right: 10px;
}
.top-right-values {
    color: rgb(255, 255, 255);
    -unity-text-align: upper-right;
    font-size: 18px;
}
.top-right-labels {
    -unity-text-align: upper-right;
    color: rgb(255, 255, 255);
    font-size: 10px;
}
.spacers {
    height: 3px;
}
.footer {
    bottom: 0;
    flex-direction: row;
    justify-content: space-between;
    position: absolute;
    flex-grow: 1;
    width: 100%;
    height: auto;
    padding-bottom: 45px;
}
.bottom-left {
    flex-direction: column-reverse;
    margin-left: 10px;
}
.instruction-text {
    color: rgb(255, 255, 255);
    white-space: normal;
    font-size: 18px;
}
```

![Updating GameUI USS](/files/-MSQWhVTPjfJBeBTBiUf)

Let's also update our PanelSettings (in the UI folder) to better handle mobile and desktop.

* In Inspector, make the following updates to PanelSettings:
  * Scale Mode = Scale With Screen Size
  * Screen Match Mode = Match Width Or Height
  * Reference Resolution
    * X = 1000
    * Y = 1200
  * Screen Match Mode Parameters
    * Height = 1

Our UI dynamically updates when we change our Scale Mode to  "scale with screen size." We choose "height" as the main driver because we have a more vertical UI in our app. If it is wider, it should not affect the scale of our UI, but if it is taller or shorter we would want our UI to scale.

![Updating PanelSettings](/files/-MSQYP1TFAH135hL17yM)

* Now build for iOS (clicking "Build and Run" in Inspector when iOS-Build is selected from the BuildSettings folder) and take a look at our new UI updates

![](/files/-MSQc_m6NLtVbjFh6cBv)

&#x20;

![Shooting down AR player](/files/-MSQjbWMxhRrCmzNdwQ-)

![Getting shot down by the desktop player](/files/-MSQkDI2D8_bruuyl_mk)

{% hint style="success" %}
We now have a more desktop/mobile-friendly UI

* We imported an SVG
* We updated TitleScreen USS
  * And updated the logo class to use the imported SVG name if necessary
* We updated GameUI USS
* We updated PanelSettings
  {% endhint %}

**Github branch link:**&#x200C;

`git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/`\
`git checkout 'UI-Updates'`‌

{% hint style="info" %}
[Join our Discord for more info](https://discord.com/invite/88j758eUvs)
{% endhint %}

## That's all folks!

Hopefully this gitbook was helpful to other XR developers. We plan on keeping this gitbook updates as packages and Editors are updated.

Please reach out [on Discord](https://discord.com/invite/88j758eUvs) if you have any questions or if you would like some additional sections to cover other topics.


