Switching Character Control in Unity: Part II, Implementation

Nick Francisci
4 min readJan 15, 2022

--

In part I, we defined a strategy in which a transmitter connects to a receiver via a registry, and then forwards commands from the PlayerInput to the receiver. Here, in part II, we will write the code for the transmitter, receiver, and registry. The controllers, which are hardwired to the receiver and provide the final control of the character movement, are not our concern in this article. However, you’re welcome to reference the code for the turret’s rotation controller or for the tank’s drive controller. Of course, the code for the entire demo is available on GitHub.

Let’s get started.

An animated gif. The window is split. On the left, a camera looks at a tank as it first moves towards the camera. The tank then stops and the turret begins to rotate left and right. The animation finishes with the turret elevating towards the camera. On the right, the same scene is shown from the perspective of a camera following the tank. After the tank finishes moving, the camera switches to one looking just over the turret and rotates with the turret as it first trains, then elevates.
Our goal.

The Registry

The registry provides a place for transmitters to find receivers. Brace yourself; there’s quite a bit of code in the registry but it’s also most of what we need to make the whole system work. I’ve broken up the code into sections for easier presentation, but you’re welcome to refer to the full file.

To kick off, we want transmitters to easily find our registry from anywhere so we make the registry a singleton object. When this singleton awakes for the first time, it will make a static (read: global) reference to itself. If a new copy of the script is created for any reason, it will destroy itself. The benefit of using a singleton is that all other scripts can simply call Registry.<method> without needing to either find or be given a reference to the registry object.

Now we need the ability to keep track of transmitters and receivers internally, for which we use arrays. We also need some private methods to assign or remove new transmitters or receivers from these arrays.

Within these methods, we will invoke a callback method on the transmitter (which we will write later) to let that transmitter take action when it is connected or disconnected from a receiver.

Finally, the registry provides one publicly accessible method, GetNewReceiver, that connects a transmitter to the next available receiver.

Congratulations, we’ve finished the registry! This is what is looks like in the inspector:

The unity inspector for the registry gameobject. The registry object contains the registry script. The script has a receivers array with two elements. The first element is a receiver script on the tank gameobject and the second is a receiver script on the turret gameobject.
The registry inspector config

Note: this implementation assumes that the game designer will assign all the receivers into the receivers array in the inspector and that the number of receivers will not change at runtime. However — with some extra work — it’s also possible to add receivers to the registry dynamically by running a method in the receivers’ Awake method. This change requires adaptation within the registry so the NextFreeRecvIdx method can handle null elements in the receivers array.

The Transmitter

To begin the transmitter code we need transmitters to get new receivers (which is now easy!). For the purposes of this demo we presume we want to automatically connect the player’s transmitter to the first available receiver immediately after we enter play-mode. Once connected to a new receiver, we also want to switch to that receiver’s camera.

To set our reference to the new receiver and activate the new camera, we make use of the Connect callback method. The registry will call Connect once it finds a suitable receiver.

For the other important bit, we need to pass the actions from the PlayerInput component through to the receiver. We use Unity’s SendCommand method to pass these actions to the corresponding receiver method because it affords us the luxury of passing arbitrary arguments without worrying about their types.

There is one special command: the command to switch roles. When we receive a switch-role command from the PlayerInput, we don’t want to pass that command to the receiver. Instead, we intercept the command and ask the registry for a new receiver to switch to.

That’s everything for the transmitter. The only thing left is to make use of the commands we passed through.

The Receiver

The receiver is dead simple. It holds two references. The first is for a camera, which is activated when the transmitter switches to this receiver. The second reference is for an array of listeners that act on commands sent to this receiver. Prior to runtime, we use the inspector to add all scripts that need to respond to the receiver into to the listeners array.

The inspector panel of unity, showing the turret object. The object contains two scripts. The first, named ‘Rotate’, controls the movement of the turret horizontally. It has a horizontal speed parameter set to 1 and a vertical speed parameter set to 0. The second script on the object is named ‘Receiver’. It contains a ‘Receiver Camera’ parameter with a turret camera object assigned. It also contains an array of listeners which include the turret object itself and a barrel object.
The receiver setup on our tank turret. The listeners are the current object (the turret) and the barrel. The turret rotates laterally in response to horizontal movement input. The barrel elevates in response to vertical movement input.

During runtime, all the receiver needs to do is to pass commands from the transmitter onto its listeners. The listener scripts are free to act on the commands as though they were coming directly from the PlayerInput component.

And you’re all done! Celebrate with this funky bird.

Closing Note

If you’re particularly interested in learning how to extend this system to multiplayer games and allow multiple players to cycle through characters, let me know in response to this article and I’ll write that one next.

--

--

Nick Francisci

Engineer motivated by helping folks access the fundamentals of life: wholesome food, education, and healthcare. I also make video games. Web: nickfrancisci.com