Switching Character Control in Unity: Part II, Implementation
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.
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:
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 theNextFreeRecvIdx
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.
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.