Happy new year everyone! Time for a quick dev update for last week (meant to post this earlier, but ah well).
Networking abstraction and Photon PUN
About a year ago I implemented a networking abstraction layer to separate use of the deprecated Unity RakNet based API from the rest of the code.
Last week I made some improvements to its interface to finish it off, and then I added a new implementation under the hood using Photon PUN 2. I went with PUN because it’s easy to get up and running, it’s well supported, and its API is very similar to the old Unity API. Thankfully the PUN implementation was relatively easy to do, partly due to the API similarity, and partly because of the abstraction layer which meant I didn’t have to touch the rest of the code.
I’ve no idea yet if I’ll stick with PUN in the long run, but for now it at least means I can get rid of all references to the deprecated Unity networking API, while not breaking my prototype multiplayer implementation. So now I’m finally free to move on past Unity 2017.4 and upgrade to a newer version, which I’ll hopefully be doing soon!
Builder tool UI feedback
After the recent video I put out about the builder tool UI changes I was considering, I’ve had loads of amazing and really useful feedback, so thank you for that! I think the consensus is to leave things as they are and focus on the rest of the game, so that’s what I’ll be doing, at least for now. I’ll keep the prototype code around somewhere in case I need to come back to it in the future.
Saved game serialization
As for this week, I’m back to looking at Json.NET saved game serialization, a topic I covered a few months ago. I’m trying to improve on the performance and GC alloc issues I had discussed in that blog post. If this doesn’t work out then Json isn’t going to be viable for this purpose, and I’ll have to think of another way to solve the inflexibility of the current binary stream based system.
Sorry for the lack of updates for the past couple of months, I was away visiting family and things for some of that time, but I have also made a lot of progress on the game. So let’s dive in!
Networking layer
Most of my time lately has been spent on networking code. As I discussed in the previous post, the Unity networking API situation at the moment is unfortunate to put it mildly. To mitigate against this, I’ve now built a new networking abstraction layer. This wraps and hides away all direct access to the deprecated Unity RakNet based API that I’m currently using, and should facilitate easier migration to a different networking API in the future.
This was a big task as it touched code throughout the game. There are four major aspects to the networking API that needed to be eliminated from the game code: Network static API, network messages, NetworkView, and NetworkPlayer. In more detail:-
Network static API, e.g. Network.isServer, Network.SetSendingEnabled(),
etc. These are now only used inside the networking layer (or where
absolutely necessary, wrapped behind a new API for use in game code).
Network messages, e.g. OnServerInitialized(), OnPlayerConnected(), etc. These are now only used inside the networking layer, and where necessary propagated out to the game code via events.
NetworkView, used for remote procedure calls (RPCs) and state serialization:-
RPCs. The new layer now allows game code to register methods,
then remotely call, and receive them. Internally this currently is
still implemented using a NetworkView and RPCs.
State serialization via OnSerializeNetworkView() and BitStream.
I’ve now implemented a system within the networking layer to
synchronize state across the network. Internally this is still
implemented using a NetworkView, OnSerializeNetworkView(), and BitStream, but none of this is exposed to the game code.
NetworkPlayer, used to uniquely identify a connected player. Now
the new networking layer assigns its own ID for each player, and keeps a
reference to each player’s NetworkPlayer for use internally only
After of all of these changes, the Unity networking API is no longer directly referenced anywhere in game code. The next step will be to actually change the networking layer’s internal implementation from using the Unity API to something else. This will likely still be a painful task, but should be far more manageable now than it would have been before.
Construction modification optimisations
In the previous blog post I talked about the optimisations I’d done to the construction modification code (this code deals with attaching and detaching parts). The performance of this code is particularly important in the context of the damage system which can require a large number of parts to be detached at once.
I have now implemented a GameObject pooling system, which eliminates the last major remaining performance cost, that being the instantiation and destruction of construction and RigidbodyGameObjects. In the example I showed in the last post (shown again below), the cost of breaking the attachments is now down to less than 10ms (prior to doing any optimisations, it was around 60ms!)
There are still more optimisations I can do. For example, the instantiation and destruction of the attachments between parts are contributing some performance cost (and GC allocs). Plus, associated with the attachments are the physics ConfigurableJoints between Rigidbodies, which also have to be created and destroyed. Maybe I could pool these too, something to look at in the future for sure.
But for right now I’m happy enough with the performance that I can get back to working on the damage system.
Damage system
On which subject, I’ll be returning my focus to this next. I still need to decide how to propagate collision forces to cause plausible breakage of parts. Also, I’m leaning away from the idea of damage accumulation and attachment “health”, towards more of a probabilistic method, but I’m not entirely sure yet.