5. Client-side representation

The client-side logic we want to implement for this feature is:

  • Visualise active health packs hovering just above the ground.
  • Do not visualise inactive health packs.

Step 1. In your Unity Editor, locate Assets/Fps/Prefabs/HealthPickup.prefab.

Step 2. Select this prefab and press Ctrl+D/Cmd + D to duplicate it.

Step 3. Move this duplicated prefab to Assets/Fps/Prefabs/Entities/UnityClient.

Step 4. Rename the duplicated prefab to HealthPickupClient (the process of duplication will have appended an unnecessary 1 to the file name).

Step 5. To map this prefab for the Client worker, select Assets/Fps/Config/ClientPrefabMapping

Step 6. Select New Entity Type, and then select SimpleEntityResolver to get a new element in the mapping list.

Step 7. Expand the new element and change Entity Type to HealthPickup, and set Prefab to the newly created HealthPickupClient.

Step 8. Select the prefab HealthPickupClient again to open it in the editor.

Step 9. Still in your Unity Editor, add a new script component to the root of your HealthPickupClient prefab by selecting Add Component > New Script in the Inspector window.

Step 10. Name this script HealthPickupClientVisibility and open the script in your code editor.

This script will contain the logic to toggle the visibility of the health pack when the health pack becomes active/inactive.

Step 11. Replace the contents of HealthPickupClientVisibility with the following snippet:

using Fps.Config;
using Improbable.Gdk.Subscriptions;
using Pickups;
using UnityEngine;

namespace Fps
    public class HealthPickupClientVisibility : MonoBehaviour
        [Require] private HealthPickupReader healthPickupReader;

        private MeshRenderer cubeMeshRenderer;

        private void OnEnable()
            cubeMeshRenderer = GetComponentInChildren<MeshRenderer>();
            healthPickupReader.OnUpdate += OnHealthPickupComponentUpdated;

        private void UpdateVisibility()
            cubeMeshRenderer.enabled = healthPickupReader.Data.IsActive;

        private void OnHealthPickupComponentUpdated(Pickups.HealthPickup.Update update)

Step 12. In the FpsEntityTemplate class, add HealthPickup to the list of components in the Player entity's clientRangeInterest query results filter.

var clientRangeInterest = InterestQuery.Query(Constraint.RelativeCylinder(clientRadius)).FilterResults(new[]
    Position.ComponentId, Metadata.ComponentId, OwningWorker.ComponentId,
    ServerMovement.ComponentId, ClientRotation.ComponentId, HealthComponent.ComponentId,
    GunComponent.ComponentId, GunStateComponent.ComponentId, ShootingComponent.ComponentId,

The HealthPickupClientVisibility script is mostly standard C# code that you could find in any game built with Unity Engine. There are a few parts which are specific to the SpatialOS GDK though, let's break those down:

  • [WorkerType(WorkerUtils.UnityClient)]

This WorkerType annotation marks this MonoBehaviours to only be enabled for a specific worker-type. In this case, this MonoBehaviour will only be enabled on UnityClient client-workers, ensuring that it will never run on your server-workers.

While we also separate our prefabs by worker types, its good practice to annotate MonoBehaviours that are worker specific with WorkerType annotations.

It makes it explicit to the reader where the MonoBehaviour should run and serves as a safety check against accidentally putting this behaviour on a prefab meant for a different worker type.

  • [Require] private HealthPickupReader healthPickupReader;

This is a Reader object, which allows you to interact with your SpatialOS components easily at runtime. In particular, this is a HealthPickupReader, which allows you to access the value of the HealthPickup component of the underlying linked entity. For more information about Readers, see the Reader API.

The [Require] annotation on the HealthPickupReader is very important. This tells the GDK to inject this object when its requirements are fulfilled.

A Reader's requirements is that the underlying SpatialOS component is checked out on your worker-instance, regardless of authority. You have to add the HealthComponent to the clientRangeInterest query in Step 12 to ensure client-workers check out this component.

A Monobehaviour will only be enabled if all required objects have their requirements satisfied.

  • healthPickupReader.OnUpdate += OnHealthPickupComponentUpdated;

Here, we bind a method to an event on the Reader. This means that whenever the HealthPickup component is updated, we will trigger a callback on OnHealthPickupComponentUpdated. This allow you to react to changes in components.

  • cubeMeshRenderer.enabled = healthPickupReader.Data.IsActive;

Here, we access the current data of the HealthPickup component of the underlying linked entity. We toggle the MeshRenderer on the GameObject - this ensures that the visibility of the GameObject is always kept in sync with the is_active property on the HealthPickup component.

Entities are not always represented by GameObjects. Exactly how entities are represented on each of your workers is up to you.

The GDK also offers an ECS workflow which represents them as a grouping of Unity ECS entity and components.

If you are more familiar with the traditional Unity GameObject style of development then the GDK provides a MonoBehaviour workflow for you.

You are not limited to these options either, and can configure your worker to create something very custom when it encounters a particular entity type.

Your choice of prefab name can be anything, but must match the string you used for the entity's Metadata component when you wrote the entity template function for this entity. This is implementation detail from the AdvancedEntityPipeline that the FPS Starter Project uses to create GameObjects.

The GDK automatically clears event handlers when a script is disabled, therefore you do not need to manually remove the OnHealthPickupComponentUpdated callback.

Updated 7 months ago

5. Client-side representation

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.