Syncing Xevious enemies with MLAPI
My experiments with MLAPI for implementing multiplayer in Xevious continues with testing the syncing of enemies.
The basic synchronisation
The core of sync’ing enemies with MLAPI is to spawn them.
The important point is that Spawn can only be done on the Server. When the spawn occurs the object is replicated to the clients. However, in order for the spawn to work it must have a Network Object component.
With just these basic components in place the Toroid Enemy ship can be spawned on the server and replicated to the client. However, having the Toroid created in the scene is not a lot of use, we need to move it.
Transformation Synchronisation
MLAPI has automatic support for transformation synchronisation via the Network Transform component. Although I have shown in Syncing Positions that sometimes it might be better to be less, “network chatty” I did use this automatic support for my Toroid Enemy.
However, for the Toroid to sync correctly I had to change the Min Meters to a smaller value. This means that MLAPI will only send sync information if the delta distance is greater than this 0.0015 meters — i.e. almost always for an enemy that is moving.
With the Network Object, Spawn and Network Transform in place then a basic demo could be run.
Pooling Objects
Whilst the initial demo is okay, it is generating and destroying objects on the fly. We know this is not a great idea, fortunately MLAPI does provide an object pooling mechanism to avoid angering the Garbage Collecting Gods.
A couple of important points to note;
- The handler only fires on the Client. I admit this confused for me for a while.
- The RegisterSpawnHandler does NOT have any concept of type. Therefore when you RegisterSpawnHandler is invoked you have no idea what prefab it has been invoked for. Therefore writing a single delegate to handle all the calls for any pooled object type is not possible. MLAPI wants you to have a pool per object, rather than a pool that can manage multiple objects. Therefore in the above code I am using closures to create a delegate per pooled object type, the prefab name is captured locally before the above snippet.
The result looks exactly the same as before…which is a good thing.
Animations and Fly-ins
There are two obvious flaws in the previous demos. If you compare the Toroids in Player 2 with Player 1, you can see that they are not spinning, i.e. they are not animating. MLAPI contains Network Animator which is a sync for the animation controller, although it does not have the same granularity of control as the Photon PUN version.
This also means that only the Server can control the animator state.
The second problem is that as Toroids are created, or dehydrated from the pool, the Network Transform sync makes them “fly-in” from their old position rather than having their position set and just appearing at their new starting point. To solve this I added a little extra code when returning the items to the server pools.
gameObject.SetActive(false);
gameObject.transform.position += Vector3.up * 100f;
Crude, but effective :)
Where next?
The next challenges will be implementing:
- the enemy bullets — should these still be just sync’ed tranforms & animations?
- basic ground targets. — ground targets must be a child of the scrolling background and must move using local rather than world position.