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
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
The client is having a tough time getting enough SnapShot updates to make the asteroids appear to be moving smoothly.
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:
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;
Now we must also update SetGameSettingsSystem to pass through this new field
Paste the code snippet below into SetGameSettingsSystem.cs:
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;
Next, create a new System inside the Server/Systems folder and name it PlayerRelevancySphereSystem
Paste the code snippet below into PlayerRelevancySphereSystem.cs:
You will notice that in PlayerRelevancySphereSystem we setm_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).
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
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
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.
Our game UI updates based on updated ghost values
We updated GameSettingsComponent
We updated SetGameSettingsSystem
We created PlayerRelevancySphereSystem
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:
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