Creating a Cross-Platform Multi-Player Game in Unity — Part 2
In the second part of this tutorial, you’ll write the code to have users connect with each other and then get a race started! By Todd Kerpelman.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Creating a Cross-Platform Multi-Player Game in Unity — Part 2
55 mins
- An Introduction to Matchmaking
- Invites, or Auto-Match?
- Adding Auto Matching
- Running on a Second Device
- Running on Android
- Running on iOS
- Adding a Simple Waiting Room UI
- Adhering to the Delegate Pattern
- Starting the Multiplayer Game
- Assigning Player Colors
- Making the Cars Move
- Sending Message Data
- Reliable or Unreliable?
- Receiving Message Data
- Where to Go From Here?
Receiving Message Data
In MultiplayerController.cs, replace the code of OnRealTimeMessageReceived
with the following:
// We'll be doing more with this later...
byte messageVersion = (byte)data[0];
// Let's figure out what type of message this is.
char messageType = (char)data[1];
if (messageType == 'U' && data.Length == _updateMessageLength) {
float posX = System.BitConverter.ToSingle(data, 2);
float posY = System.BitConverter.ToSingle(data, 6);
float velX = System.BitConverter.ToSingle(data, 10);
float velY = System.BitConverter.ToSingle(data, 14);
float rotZ = System.BitConverter.ToSingle(data, 18);
Debug.Log ("Player " + senderId + " is at (" + posX + ", " + posY + ") traveling (" + velX + ", " + velY + ") rotation " + rotZ);
// We'd better tell our GameController about this.
}
You're basically doing the opposite of what SendMyUpdate
does; instead of putting data into a ByteArray
, you're extracting it into useful values that you can use in your game. System.BitConverter.ToSingle()
takes a ByteArray
and an offset, and converts those bytes into a native data type.
Now that you have the information you need, you can do something useful with it. Add the following interface definition to MPInterfaces.cs:
public interface MPUpdateListener {
void UpdateReceived(string participantId, float posX, float posY, float velX, float velY, float rotZ);
}
Next, add the following public variable to the top of your MultiplayerController
class:
public MPUpdateListener updateListener;
Then, add the following to the end of the if
block in OnRealTimeMessageReceived
, right after the We'd better tell our GameController about this
comment:
if (updateListener != null) {
updateListener.UpdateReceived(senderId, posX, posY, velX, velY, rotZ);
}
Go back to GameController.cs
and declare that it satisfies the MPUpdateListener
interface as follows:
public class GameController : MonoBehaviour, MPUpdateListener {
Then add the following method somewhere inside that same class:
public void UpdateReceived(string senderId, float posX, float posY, float velX, float velY, float rotZ) {
if (_multiplayerReady) {
OpponentCarController opponent = _opponentScripts[senderId];
if (opponent != null) {
opponent.SetCarInformation (posX, posY, velX, velY, rotZ);
}
}
}
Here you're checking that _multiplayerReady
is true
, as there's always a chance you could receive a message before you're done your setup; if you don't protect against this you could end up with some fun and exciting null pointer exceptions.
If everything checks out, you then call SetCarInformation()
on the OpponentCarController
corresponding to the participantID
of the sender.
Now open OpponentCarController.cs
and add the following method:
public void SetCarInformation(float posX, float posY, float velX, float velY, float rotZ) {
transform.position = new Vector3 (posX, posY, 0);
transform.rotation = Quaternion.Euler (0, 0, rotZ);
// We're going to do nothing with velocity.... for now
}
There's nothing too surprising in SetCarInformation
; you simply set this car's position and rotation to the values reported by its game client.
Finally, go back to GameController
and add the following line to the top of SetupMultiplayerGame()
:
MultiplayerController.Instance.updateListener = this;
In the code above, GameController
tells MultiplayerController
that "I am the class you should talk to when you get an update from another player in a multiplayer game." So MultiplayerController
will call UpdateReceived()
in your GameController
, which will then call SetCarInformation()
on the appropriate OpponentCarController
object.
Build and run your app on both devices; you should now be able to move your car on one screen and see its movement reflected on the other screen. Move both cars on both devices at the same time, and hey — you've got yourself a racing game! :]
If everything hasn't gone well, I'd suggest you try the following two things:
- Make sure you're running the latest version of the game on both devices. Sometimes I'll update my game on only one device and forget that I need to load it on the other as well.
- Kill the app on both devices and try again. Sadly, the "try it a second time and maybe it will magically work this time" solution seems to work for multiplayer games more times than I'd care to admit.
Where to Go From Here?
You can download the completed project over here.
Your multiplayer racing game is off to a really good start, but there still are a few problems that you'll need to address:
- A biggie: the game doesn't end! You'll need a way to declare a winner and leave the room.
- You're making network calls more frequently than you need to. It'd be nice if your game was a little more considerate of your user's battery and data plan.
- Depending on your network connection, the cars' movements might look a little jittery. You need a way to smooth them out and make the animation look fluid.
Luckily, these are all relatively easy issues to fix — and you'll address them all in Part 3 of this tutorial series. As always, if you have any questions or comments, please feel free to join in the discussion below!