You’re still here ? As princess Leia would say, you’re braver than I thought. Today that bravery will be tested as we move on to a rather scary topic : network programming.
Specifically, we’ll be looking at UDP networking for Unity. If you think UDP is some kind of political party or that this “networking thing” must have something to do with sharing your private life for the benefit of advertisers, I’d suggest you don’t read further before taking a look at my articles on network protocols and programming.
In fact, if you have doubts about something network-related, you should definitely read those articles. Come on, it’s free !
Unity networking facilities
Unity exists to simplify the work of video game developers. That means it features tools to help program the networking aspect of a game. Things like detecting game servers, connecting to a game session on such a server and exchanging player data.
These tools are definitely not restricted to video game design. However, they can be a bit limited because of that intended purpose.
Thankfully Unity relies on the .NET framework, which has its own networking tools that are more generic. In most cases you’ll find them easier to bend to your will.
In practice, you can get the simplest network code by using a combination of Unity and .NET facilities. I’m now going to demonstrate that statement with a very useful example.
Simple networking application
Let’s do something basic yet essential : we’ll write a Unity application that can identify all the machines on your local network that are running this same application. I’m assuming you have at least one PC (how else would you do the programming ?) and one Android phone, both connected to the same network.
This application needs to do very little :
- Connect to your network.
- Advertise its presence to the rest of the network
- Listen for other devices running the app as they advertise their own presence
- Display identification for all the devices that have been discovered
As you’re about to see, this can be done with very little code, however that’s still a bit too much code for me to write here and have you copy-paste. So I’ll let you download the C# script for this application, right here.
Creating the project
Now start the Unity editor and create a new 3D project. Let’s call it “Network_App_1” for the sake of poetry. Select the Main Camera and change its background form Sky Box to Solid Color. Make it a white background because we’re going to print things and it’s best if we can read it.
Decompress, drag and drop the “UDP_Discovery.cs” script into the Unity editor’s assets explorer. From there, drag and drop it onto the Main Camera, thus ensuring it’ll be executed when the application runs. Now double-click the script to open Visual Studio and let’s see what we have. I went to the effort of commenting everything, I hope you appreciate it. Commentless code is an abomination in the eyes of the
Disclaimer : This script is not something you might want to use in a finished product. Its purpose is to demonstrate concepts and give you leads to create your own scripts. I know I’m asking you to do some work, but hey, if you’re just looking for a dumb recipe to follow, this ain’t the site for you. I expect you’ll learn better if you think more. You can thank me later.
Connecting to a network
Unity applications run on an OS, whether it’s Windows or Android or anything else. That means you don’t need to write code to establish a network connection. Instead you need to determine what your OS has connected to, and this tends to require OS-specific code.
Luckily, Unity takes care of that tedium for us and provides a more abstract API compatible with all operating systems. Part of that API is the Network object. Just like the Immediate Mode GUI object GUI, the Network object is maintained by the Unity engine at run time.
You can use it to get your current IP address, as a string, with just one simple statement :
string my_IP_address = Network.player.ipAddress;
Your should have connected your machine to a network before you start the application. If you haven’t, this statement will return the invalid IP address “0.0.0.0”, and that’s easy to test for if you’re feeling paranoid.
From your own IP address you can derive your network’s broadcast address. Broadcasting is like shouting : send a packet to that address and every host will receive it, which is exactly what we need for this application.
Most LAN are class C networks. This means the first three bytes of your IP address identify your network, and the last byte identifies the host. There’s no host 0, that’s the network’s address. And there’s no host 255 : that’s the broadcast address.
You can easily build your local broadcast address by parsing your IP address :
byte Bytes = IPAddress.Parse(My_IP_Address).GetAddressBytes(); My_Broadcast_Address = Bytes + "." + Bytes + "." + Bytes + "." + 255;
Here you go : you’re connected to your network.
Our app needs to shout around “hey guys, my name is…” once in a while, so other hosts can detect it. The network equivalent of shouting is broadcasting, which is why we computed our local broadcast address.
But you can’t just shout continuously. It’d wear down everyone’s nerves and, of course, it’d eat up all your network bandwidth. So let’s arbitrarily decide that shouting once every 100 milliseconds is a good compromise. Now we need some kind of sequencing mechanism., and .NET has a good one : threads.
To put it simply, a thread is a method that executes in parallel to the rest of your program. From its point of view, the thread method will execute exactly as if you had called it normally.
Our broadcasting thread is the aptly-named Broadcaster() method and is started in the equally aptly-named Start() method. As you can see it’s a very simple loop with a socket call to broadcast a message, followed by a 100 ms sleep. I’ve written it as if it were the only thing the application executes : such is the magic of multithreading. Let the OS handle the tedium of sequencing tasks, we have better things to do.
Start() is also where we create the broadcast’s message. For that I used the Unity SystemInfo object, which, as you’d expect, provides all sorts of information pertaining to your system. Its deviceModel property may not be consistent on all operating systems, though : on Android phones you’ll get your phone’s model name but on an PC you’ll get your processor model and RAM capacity. Still, for today’s application we can work with that.
So… now our application is telling the network it’s here, ten times per second. Let’s make sure it doesn’t go unheard.
Listening for other devices
We’ll be running our application on two or more machines on the same network (not much point otherwise). That means there will be broadcast packets coming in from other machines. And of course we can’t predict when they’ll come. So our application needs to constantly listen for them in order to learn where’s its crew at, yo.
We can use a second thread to take care of that. The thread’s method will be Receiver() and, once again, it will be started from the Start() method. Let’s look at its logic :
First, I use my socket’s Available property to see if some packets came in. Its value is in bytes and it can represent multiple packets, which is why I use a “while” loop instead of an “if” statement.
Then I need to get each packet, using the socket’s Receive() method. It will give me an incoming packet’s payload and the IP address from whence it came. The IPEndPoint object has two roles : it filters which hosts and port I want to get packets from and, once I got a packet, it holds its source IP address.
That’s why I initialize it just before calling UDP_Socket.Receive(), and that’s also why it is passed as a reference (C# pointer, basically).
Receive() returns the payload as an array of bytes (the native format of network payloads) which you must convert into something C# can use, here it’s a string object. This is Application Layer stuff : in your own projects you may need to convert payloads to (and from) different data types.
Remember, this application broadcasts a string which identifies the machine on which it runs. Therefore it’ll receive a bunch of similar string, each associated to an IP address. And because the broadcasts repeat every 100 ms, it’ll receive a lot of duplicates.
C# makes it child’s play to identify those duplicates and store only “original” packets. You could have missed it given how simple it is. In the Receive() thread, after we’ve turned a packet’s payload and source IP address into a nice string, we do this :
if (!Display_Buffer.Contains(text)) Display_Buffer += "\n" + text;
It’s even faster than typing “if the string text is not already present in the buffer, append it to the buffer as a new line”. Which is exactly what it does. If you’re used to programming such things in C or C++, I’ll give you a moment to recover from shock.
Note that Unity’s GUI facilities will respect “\n”, meaning we can just print Display_Buffer and each host will appear on its own line, saving us a loop.
Odds and ends
You may have noticed there’s a one millisecond sleep in the Receive() thread’s loop. Why is it there ? Do we really need it ? Jeez you’re nosy ! OK, I’ll explain…
Operating systems can’t guess how fast you want your program to run, so they assume you want it to run as fast as possible. Here we have a thread that’s basically an infinite loop and should never block. Unless we tell it to sleep most of the time, it’ll eat every processor cycle it can find. In practice, this means your processor will end-up running at its maximum clock speed as long as the application runs. That will drain your battery quick-fast if you’re running on a mobile device.
The choice of a millisecond sleep is arbitrary but sensible.
Do remember : a sleep is not a delay loop even though the effect may appear identical. A delay loop would not prevent your CPU from going full-throttle. A sleep tells the OS to suspend the thread’s execution until some point in the future. Instead of running a delay loop, the CPU can execute other tasks until it’s time to resume the thread.
In the OnGUI() method, notice I’ve coded a button to clear the buffer which stores the packet received by the application. That’s because this application is too simplistic to detect when a host drops off the network. Manually clearing the buffer will let the Receive() thread refill it with packets coming only from the hosts that are still online when you hit that button.
Now let’s run this thing !
You already know how to compile and run a Unity application, both on PC and Android. Do it. Now. If you have several PC’s you can build it as an executable file and distribute it (along with its “_data” folder) with a USB stick. It will also run in the Unity editor’s “player”. Once you have several machines running it, you should see something like this on each one :
Oops, did I leave an ESP8266 online when I took that screenshot ? Forget you saw that, wink wink.
Yeah, why not ?
Our script has no Update() method (the one Unity runs for every frame it renders). That’s because not every Unity script needs to be about graphics. That being said, since most computers can render on the order of 100 frames per second, or one every 10 ms, I could have used Update() instead of the Broadcaster() or the Receiver() thread. Try it for fun. Here’s a hint : it will make the script even simpler if you do it correctly.
Speaking of the Receiver() thread, try and use the Windows’ task manager to see what happens if you remove that millisecond sleep. Now, go the other way and see what happens when you make that sleep longer.
This post may be two thousand words long, but that’s nothing considering you now have everything you need to write network applications with Unity : you can connect to other devices and you can send and receive data. And the crucial thing here is that you don’t need to resort to dirty hacks like hard-coding IP addresses or WiFi network credentials. I keep telling you, and I hope it’ll get through your thick skulls someday : Unity is perfect for general purpose application programming. And it’s free. What more do you want ?
I haven’t decided yet what the next article is going to be about. Maybe we’ll start looking at 3D graphics. After all, that is one of the things most people know Unity for.
Until then, take care, and keep programming.