1. Create the player model

To see how your camera control scheme is working, you will need a visual representation for the Player entity.

Unity represents SpatialOS entities by instantiating a prefab of the correct type into the scene.
The EntityTemplate for Player entities in the Starter Project codebase specifies that it will use the EntityPrefab
named “Player”. You can find this prefab in the Assets/EntityPrefabs folder.

You can build your Player prefab however you want, but the simplest way is to:

Drag the existing Player prefab into the scene.

Right-click it in the scene hierarchy.

Add some default Unity 3D Objects as children of the prefab root.

This simple example prefab includes some spheres for eyes to allow you to verify which direction the Player
GameObject is facing.

Apply the changes to the prefab.

Remove the Player GameObject from the scene. Keeping it in the scene will cause errors, because the player needs to
be instantiated by SpatialOS.

2. Working with player cameras

If you placed a camera on the Player EntityPrefab itself, you’d run into problems: when two players are visualized on
the same client, each of them would be instantiated with a camera, which would not work as expected.

Instead, for best practice, you should:

place a camera in your UnityClient scene so it is a local resource on the client-side

reference the scene camera from any script that controls the camera, and move the camera to the correct position relative
to the Player GameObject

use SpatialOS [Require] attribute in your camera controls scripts to ensure the camera
is only used by the Player’s own character GameObject

The most commonly used camera perspectives are first-person and third-person
Depending on your game’s camera control scheme you may wish to add one or both of the scripts to your Player EntityPrefab.

The Starter Project already contains a camera in the scene called MainCamera, and the first-person and third-person camera
control scripts below will refer to that camera.

If you choose to use your own custom camera control script, be aware of the SpatialOS-specific additions:

requiring a writer for the ClientConnection component

acquiring a reference to the scene camera in OnEnable()

Third-person camera

Third-person cameras float above the Player character’s head and look down, so you can see both the player’s model and some
of the environment in front of them. They typically also allow the user to rotate the view to observe the environment but
not affect the orientation of the Player avatar itself.

The third-person camera script you are creating in this section must:

run only on the client-side

run only on the Player GameObject being controlled by the current client

obtain a reference to the camera in the client’s local scene

make the camera move with the Player GameObject by parenting the transforms

set the camera an initial distance from the Player gameobject and view direction

Using the [Require] attribute and a Writer object ensures a script is only enabled on workers which have write access over
the specified component. The ACL for the Player entity template specifies that only the client that owns this Player
entity can have write access to the ClientConnection component. This means that for any given Player entity, its camera
control scripts will only run on the corresponding client.

Later, you will also add some mouse controls to rotate the camera.

Add the following ThirdPersonCamera.cs script to the Player prefab, located in Assets/EntityPrefabs:

using Assets.Gamelogic.Core;
using Improbable.Core;
using Improbable.Unity.Visualizer;
using UnityEngine;
namespace Assets.Gamelogic.Player
{
public class ThirdPersonCamera : MonoBehaviour
{
[Require]
private ClientAuthorityCheck.Writer ClientAuthorityCheckWriter;
private Transform camera;
private UnityEngine.Quaternion cameraRotation;
private float cameraDistance;
private void OnEnable()
{
// Grab the camera from the Unity scene
camera = Camera.main.transform;
// Set the camera as a child of the Player to easily ensure the camera follows the Player
camera.parent = transform;
// Set the camera rotation and zoom distance to some initial values
cameraRotation = SimulationSettings.InitialThirdPersonCameraRotation;
cameraDistance = SimulationSettings.InitialThirdPersonCameraDistance;
}
private void LateUpdate()
{
SetCameraTransform();
}
// Update the position and orientation of the camera to match the cameraRotation and cameraDistance
private void SetCameraTransform()
{
// Set the position of the camera based on the desired rotation towards and distance from the Player model
camera.localPosition = cameraRotation * Vector3.back * cameraDistance;
// Set the camera to look towards the Player model
camera.LookAt(transform.position);
}
}
}

This script makes use of some constants specified in a file called SimulationSettings.cs, but you can add them to some
other location if you’d prefer:

For a classic third-person camera you should enable the user to adjust the camera pitch, yaw and distance to the avatar
using their mouse.

In the following script the camera pitch and yaw are adjusted based on the movement of the mouse within the viewport while
the user is holding down the right mouse button. The camera’s distance to the player model is adjusted by scrolling of
the user’s mouse wheel.

Add the following methods to the ThirdPersonCamera.cs script you already created:

Hold right-click on your mouse and rotate the camera around the player character.

From the positions of the player and the cube (which is present in the Starter Project’s client scene), you can see that the
camera is rotating, rather than the player character themselves.

First-person camera

The position of the first-person camera remains fixed relative to the character model, with the pitch and yaw once again
changing based on mouse movement.

The first-person camera script must:

run only on the client-side

run only on the Player GameObject being controlled by the current client

obtain a reference to the camera in the UnityClient scene

make the camera move with the Player GameObject by parenting the transforms

position the camera roughly at the player’s head

rotate the camera based on mouse position on each update call

As in the third-person example above, make the first-person camera control script run only on the controlling player’s
client by using SpatialOS syntax to require a ClientConnection writer.

Add the following FirstPersonCamera.cs script to the Player EntityPrefab:

using Assets.Gamelogic.Core;
using Improbable.Core;
using Improbable.Unity.Visualizer;
using UnityEngine;
using Quaternion = UnityEngine.Quaternion;
namespace Assets.Gamelogic.Player
{
public class FirstPersonCamera : MonoBehaviour
{
[Require]
private ClientAuthorityCheck.Writer ClientAuthorityCheckWriter;
private Transform camera;
private float pitch;
private void OnEnable()
{
// Grab the camera from the Unity scene
camera = Camera.main.transform;
// Set the camera as a child of the Player to easily ensure the camera follows the Player
camera.parent = transform;
// Position the camera at the same location as the Player
camera.localPosition = SimulationSettings.FirstPersonCameraOffset;
Cursor.lockState = CursorLockMode.Locked;
}
private void Update ()
{
SetCameraRotation();
}
// Update the pitch of camera object and the yaw of the Player object
private void SetCameraRotation()
{
// Take the new yaw (horizontal look) based on the position on the lateral position of the mouse in the viewport
var yaw = (transform.rotation.eulerAngles.y + Input.GetAxis("Mouse X") * SimulationSettings.FirstPersonCameraSensitivity) % 360f;
// Take the new pitch (vertical look) based on the position on the vertical position of the mouse in the viewport
pitch = Mathf.Clamp(pitch - Input.GetAxis("Mouse Y") * SimulationSettings.FirstPersonCameraSensitivity, -SimulationSettings.FirstPersonCameraMaxPitch, -SimulationSettings.FirstPersonCameraMinPitch);
// Update the pitch value of the camera
camera.localRotation = Quaternion.Euler(new Vector3(pitch, 0, 0));
}
}
}

Once again, some constants are referred to as being stored where in SimulationSettings.cs, but you can add them to some
other location if you’d prefer:

3. Rotating the Player entity

The current implementation in both first-person and third-person is a “free-look” mode which rotates the camera but does
not affect the direction the player model is facing.

For many games, only the rotation of the player around the up-vector (the y-axis in Unity’s coordinate system) matters to how
the player entity will be visualized. To update the model’s rotation based on the camera’s rotation, the following line is needed:

Add this line to the bottom of the SetCameraRotation() function, inside FirstPersonCamera.cs
This behaviour is standard and expected for first-person camera schemes, but this line of code could also be used in a
third-person script for an “over the shoulder” camera scheme.

Some games allow you to use “free-look” mode while a key is being held.
By wrapping the above line of code in an user input check you can switch between free-look and rotation locked to the camera:

4. Synchronising rotation

So far these camera rotations and player model rotations are entirely client-side, seen only by the player themselves.
To allow other clients (and the server) to rotate their own local models of the Player entity, the rotation information
needs to be synchronised across SpatialOS.

To synchronise this information across SpatialOS you must:

add a PlayerRotation component to the project’s schema

include the component in the Player entity template

determine which worker type should be given write access to the component

modify scripts to read from and write to the component

Create the PlayerRotation component

As previously stated, most games only require the entity’s rotation around the up-vector (the y-axis in Unity’s coordinate
system) to be visualized for other players. This means you can represent the entity’s rotation using a single float value.

Create a new schema file called PlayerRotation.schema with the following contents:

If you want to use 3D rotation, you can create a component which uses a
quaternion instead of a float.

Modifying the Player entity template

You need to make two changes to the Player entity template:

Include the new component.

Set the ACL for the new component.

To provide a highly responsive experience, player rotation should be made client-side authoritative by giving the user’s
client write access to the new component. This is especially necessary for first-person camera schemes, though
third-person camera setups may be more forgiving.

The Starter Project defines the Player entity template in Assets/EntityTemplateFactory.cs.

In the CreatePlayerTemplate(...) function, the Player template specifies the components of a Player entity.
Add the PlayerRotation component you just created alongside these, granting write access of the component to the player’s client:

Writing to the PlayerRotation component

In your camera controls script (e.g. FirstPersonCamera.cs) you can now write to the bearing property of the entity’s
PlayerRotation component by using a Writer class:

Add the following import statement:

using Improbable.Player;

Add a component writer requirement to the top of the FirstPersonCamera class:

[Require]
private PlayerRotation.Writer PlayerRotationWriter;

To send the local model orientation from the authoritative client to all other workers, we use the writer’s Send function.
Add this line to the SetCameraRotation() function immediately after the model’s transform is set using the yaw value:

Use the inspector to see the bearing value update as the client’s Player looks around.

Reading from the PlayerRotation component

Lastly, you want to visualize this rotation on all other clients. To do this you’ll use a script similar to TransformReceiver.cs
in the Starter Project. TransformReceiver reads the entity’s position property from the WorldTransform component and
updates the position of the entity’s local GameObject representation accordingly.

TransformReceiver only requires a Reader, as it is intended to be run on non-authoritative workers, which will not be
sending any information back to SpatialOS.

Callbacks are attached to the ComponentUpdated event which ensures every time the component’s properties are changed the
receiver script updates the local representation om the worker it is running.

Create a new script called PlayerRotationReceiver and add the following definition to read from PlayerRotation and
update the GameObject’s rotation around the up-vector accordingly:

```

using Improbable.Player;
using Improbable.Unity.Visualizer;
using Improbable.Worker;
using UnityEngine;

PlayerRotation needs to run on all client and server-side workers, except for the client controlling that Player
entity. That client, which has write access to the PlayerRotation component, doesn’t need to read from the component as
it is the one sending the updates!

Protection against running on the authoritative worker is given by the conditional
PlayerRotationReader.Authority == Authority.NotAuthoritative in OnComponentUpdated(...).

Without this check, a player’s rotation would be set by two different scripts; the camera script that updates
transform.rotation based on mouse-movement, and the value synchronised across SpatialOS. Nothing feels more responsive
than code running client-side, so the Authority check ensures a responsive feel.

Conclusion

You’ve now implemented a client-side camera control script which sends the player’s view direction to all other workers,
both clients and server-side, which will continually update their local representations of that entity.

The following are some ideas of where you might like to go next with this aspect of your game:

Synchronise vertical look-direction

In the above examples the camera’s yaw (horizontal look direction; rotation around up-vector) is used to rotate the
player’s model, which is then synchronised across all workers.

To synchronise the vertical look direction (i.e if a player is looking at the sky, or aiming down at the ground)
across all workers, you will need to add an additional property to your component and set it using the rotation of the player around the right-vector (the x-axis in Unity’s coordinate system).

Switching between first- and third-person cameras

Use a keypress to toggle between first and third-person camera schemes.

Independent look direction

Use separate properties for ‘look-direction’ and ‘facing-direction’ to allow players to be visualized running in one direction while looking to the side.

Add an aim reticule

Whether your game involves firing guns or planting flowers, drawing a visual indicator in the centre of the screen
will dramatically improve aiming precision.