In the previous lesson, you filled the game world with enemy pirate ships. In this lesson you’ll:

learn how to get write access to a component

learn about limiting which code runs on which workers

write to a component by updating the steering component properties on the PirateShip

1. Write a MonoBehaviour to move PirateShips

As you saw at the end of the last lesson, the world is scattered with pirate ships, but they don’t do anything.
In this step, you’ll write a script that uses the existing ShipControls component to move the ships around.

1.1. How to move ships around

You saw in lesson 1 that an entity’s position
is stored in its Position component. The entity’s template also included a ShipControls component.

Properties are part of a component, and they store any data of an entity that you want to share between
workers. ShipControls has two properties that will be useful here: targetSteering and targetSpeed.

How the PlayerShip moves

PlayerShip prefabs include a script (PlayerInputController.cs) that writes player keyboard input to
the ShipControls properties whenever a player presses particular keys.

Another script (ShipMovement.cs) watches for updates to ShipControls, and updates
the Position accordingly. That’s what moves the ship.

Why don’t we just write to the Position component directly?

Two reasons.
One: in a real game, some players may modify the executable game so they can cheat.
You wouldn’t want to let players with modified games decide on their own position.
Using ShipMovement.cs protects your game from unwanted position modifications.
Two: this way lets you re-use ShipMovement.cs to make it easy to steer
a PirateShip around.

How the PirateShip will move

There’s nothing changing those values from 0 at the moment, which is why the pirate ships don’t move.

Like the PlayerShip prefab, the PirateShip prefab includes ShipMovement.cs, so it’s ready to move as soon as it
receives updates to the ShipControls properties. So you need to write a MonoBehaviour that makes steering decisions
and updates the ShipControls properties with them.

In this lesson you’re going to use components which have already been created for you.
If you’re keen to know how to define new components - you’ll learn all about it in the next lesson.

Which worker?

NPCs should be completely controlled on the server side. So you’ll lock down this script so it can only be
run by a UnityWorker.

1.2. Create a MonoBehaviour to steer the ship

In the Unity Editor’s project panel, navigate to Assets/Gamelogic/Pirates/Behaviours/
and create a C# script called SteerRandomly.

Just inside the SteerRandomly class, add the line [Require] private ShipControls.Writer ShipControlsWriter;.

This does two things:

It imports a ShipControls.Writer.

This is an object you can use to interact with ShipControls. It has methods for checking the current values of
properties, sending updates to properties, and a lot more. You’ll use one of those methods in the next step.

It uses the [Require] annotation.

This annotation, also provided by the SpatialOS Unity SDK, is another way of controlling when this script is run.

Workers can have read access or write access to a component. Only one worker at a time, regardless of whether
it’s a UnityWorker or UnityClient, can have write access to a specific component on a specific entity.

The [Require] annotation means that this script (SteerRandomly.cs) is only enabled if the worker has
write access to the component specified (ShipControls): ie, if the worker is allowed to have a Writer.

[WorkerType(WorkerPlatform.UnityWorker)]
public class SteerRandomly : MonoBehaviour
{
/*
* This MonoBehaviour will only be enabled for the single UnityWorker
* which has write access to this entity's ShipControls component.
*/
[Require] private ShipControls.Writer ShipControlsWriter;
}

Add a method to the steerRandomly class that sends a random update to the ShipControls.

For all components and all properties, there’s a method that looks like this: <component name>.Update().Set<property name>().
7. Finally, something needs to invoke RandomizeSteering().

You can use the Unity function InvokeRepeating()
to do this. You should use InvokeRepeating in OnEnable(), and cancel it in OnDisable(), in order to
prevent unexpected behaviour - for more information see MonoBehaviour lifecycle:

This builds your worker code for local development, and is equivalent to running
spatial worker build UnityWorker UnityClient --target=development, as you
did in the first lesson. It’s just quicker to run this, as you don’t have to close Unity.