Everyone I know (for “everyone” = “programmers”, anyway) seems to be trying out Unity and I’d heard much praise about how easy it is for rapid prototyping. Enough to make me take a look.
My very first thought, going through one of the tutorials, was “this feels like cheating”. One of the basic tutorials is a vertical scrolling shooter, and within an hour or two you have 3D models, sounds, particles and some basic enemy waves. Creating it mainly involves dragging a few things around in the interface, and writing a few lines of code.
However, my next thought: this is so far removed from normal programming, how do I actually, you know, “make a game”? This is the bit I need to get my head around next, learning the proper ways to translate my normal C++ skills into the Unity paradigm.
Starting out with networking
My current game-making interest is for multiplayer LAN games (if you can’t find the perfect game, make your own!), so my first project was investigating the networking support and making a basic lobby. It didn’t take long to run into the first problem with the built-in networking solution, but more about that later.
There are two methods in Unity of networking your game. The first is to mark variables on objects as being synchronised, so that they’re automatically kept up to date across clients (e.g. position and rotation of characters). The second is to use Remote Procedure Calls (RPCs) which allow one instance of the game to call a function on another instance (to signal events, e.g. player has fired a weapon).
My preferred setup uses an authoritative server (the clients send inputs, and the server processes them and sends back the results) but the server is also a client so can play the game. This is as opposed to a dedicated server where you have a separate server process that all player clients connect to. Requiring a dedicated server for LAN play just seems messy and requires building and running two executables, which is a pain. Conceptually we have a separate client and server running in the same process, and all communication is through the network, even if it’s a single player game or your client is the server. The major advantage of this is that all clients run the exact same code and if the game functions correctly in single player then you’re confident it will work in multiplayer, which makes testing a lot easier.
Building a lobby
So the first thing you need is a lobby that allows you to create a server or join an existing one. Luckily Unity lets you do this in about three lines of code. The server then listens to connect and disconnect requests and assigns a player number to each connected player. Finally there’s a basic chat box so clients can all write in the chat window.
When a client connects to the server there’s a bit of back and forth with RPCs. The server sends an allow or deny response (in case the game is full or has been started), and on receiving an ‘allow’ response, the client sends back the player name. Once the server receives the player name that player is inserted into the lobby. To keep the code simple, the player creating the server goes through the same process as any other client – simply call the OnConnectedToServer event (client-side), and the OnPlayerConnected event (server-side) and all the message passing and player initialisation will happen exactly as if it was a remote client.
An RPC bug
This is where the problem comes in. An RPC has a parameter that determines who it is sent to: All, Server or a specific client. If your process is the server, and you send an RPC to Server, you would expect it to just call the local function. Instead it does… nothing! Which is really helpful, and means the client-running-on-the-server case doesn’t work.
This is a known bug and has been around for years. Reading forum discussions (such as this one) provides a good insight into the Unity community, where there are a bunch of people who will argue vociferously that this is in fact correct behaviour, and that all your code should look like:
if (Network.isServer) networkView.RPC("MyRPCFunction"); else MyRPCFunction();
I would respectfully guess that these people are unlikely to be professional programmers. Luckily it’s not too hard to fix. That same thread has a method of using reflection to write an RPC wrapper that does the correct thing: call the RPC as normal if you’re not the server, or look up the function by name and call it directly if you are.
There is a similar issue with instantiating objects. Depending on if you’re in a networked game or not, you need to use two different Instantiate methods. Adding a wrapper for this as well simplifies the code elsewhere. I guess that the ‘standard’ networking case these days that Unity was built for is dedicated servers, and I’m just old fashioned trying to build an old-style LAN game. But it can be done.
Here is my NetworkManager and Lobby code so far, which might be a useful starting point if you’re building anything similar:
I’m not at all confident that this is the best way to go about coding in Unity, but it’s pretty tidy and works. You can create a named game, search for and join/disconnect/rejoin games registered on their test server or connect by IP, set your player name and chat in a box. The NetworkManager is a singleton class and the Lobby should be added to an object in the scene, and you should be good to go.
The only slight complication above using the standard RPCs is calling functions on a different script on the same GameObject. This “just works” using the standard calls, but when using the wrapper you need to pass in the target script. I guess the wrapper could search all the scripts, but you should know what script it’s in anyway so it’s probably not a problem:
NetworkManager.Instance.RPC(GetComponent<MyScriptName>(), "MyFunction", RPCMode.Server);
With all that out the way it’s on to the next step – start making an actual game.