COMBO:SERVER+CLIENT

We said, in a previous lesson, that a "normal" computer can manage 5 to 10 players on the network. But... how Blizzard has 6 million registered players in World of Warcraft?

First, they have "only" around 1 million guys playing at the same instant. Blizzard has a "farm" of servers: thousands of computers receiving the players. And they have a software that, when a player gives the Login, if there are no more capacity on the computers that are "on air", a new one is started, having a new copy of the game. These computers are professional models (having Xeon chip etc..). Each one supports around 50 players.

But, returning to our case: how a hobbyist (target of our coure ) will create a multiplayer game?

Unity suggests an architecture that we call "Combo server+client". You create the game beeing an executable application that includes Server and Client together.

So: it's like having a basketball court and invite friends to a game. You put the Server "on air" (you can leave it "on" all day, of course) and tells to yours friends (or make public) the "IP address". They, having a copy of the "Combo", connect and enter in the game.

We will present here how to create this "Combo". The code combines what we've learned in previous lessons. But let's go step by step.

Open a new "scene" and put the "classic" empty gameobject "Administration" having a NetworkView.

Inside it, put the script:

var IPServer = "127.0.0.1";
var  IPServ = "";
 
function OnGUI (){
	 
 if (Network.peerType == NetworkPeerType.Disconnected){
	IPServer = GUI.TextField(new Rect(120,10,100,20),IPServer);
	if (GUI.Button (new Rect(10,10,100,30),"Conect")){
	 Network.Connect(IPServer, 25000);
	}
	if (GUI.Button (new Rect(10,50,100,30),"Start Server")){
	  Network.InitializeServer(50, 25000,false);
	  
	 for (var go : GameObject in FindObjectsOfType(GameObject)){
	  go.SendMessage("OnLoaded", SendMessageOptions.DontRequireReceiver);	
	 }
	}
 }
 
 else{  
  if(Network.isServer){
	IPServ = Network.player.ipAddress;
	GUI.Label(new Rect(140,20,250,40),"IP to be used: "+IPServ );
  }
    
  if (GUI.Button (new Rect(10,10,100,30),"Exit")){
   if(Network.isServer){
    networkView.RPC("ExitCL", RPCMode.Others);
   }
   Network.Disconnect();
   Application.Quit();
  }
 }
 
}
 
function OnConnectedToServer() {
	 
	for (var go : GameObject in FindObjectsOfType(GameObject))
	 go.SendMessage("OnLoaded", SendMessageOptions.DontRequireReceiver);		
}
 

@RPC
function ExitCL(){
  Application.Quit();
}

What do we have new here?

1 - In the series of lines:

 for (var go : GameObject in FindObjectsOfType(GameObject)){
	  go.SendMessage("OnLoaded", SendMessageOptions.DontRequireReceiver);
 }
we have created what is called: a "proclamation". We are creating an "event" called "OnLoaded". We can have many functions:
function OnLoaded(){
// lines of code
}
that will be "called" on any script of the application - we will soon see example.

2 - You can use the conditional

if (Network.isServer)
to run something that only occurs in the Server.

3 - The command

Application.Quit ();
does the obvious... Note that we use it here in two times. When the customer leaves, he just falls. But, when the server falls, through an RPC, all customers will fall.

OK, now we have a "Combo". And we will go ahead:

Like we did in the first lesson, drag the "courtyard" to the views and give to it a "Reset". It will go to the position (0,0,0).

Add adequate lighting.

IMPORTANT: You need to add a NetworkView to the prefab "Human".

Let's add to the "Administration", the script that creates and controls the avatar, the: "AvatarControl.js"

var velocity = 5.0;
var velJump = 8.0;
var gravity = 20.0;
private var deltaDirection : Vector3;
var Player : GameObject;
var contr  : CharacterController;
var playerClone: String;
var avatar0: Transform;
  
function OnLoaded(){ 
  
 Network.Instantiate(avatar0, transform.position, transform.rotation, 0);
 playerClone =  "Human(Clone)" ;  
   
 Player = GameObject.Find(playerClone);
 contr =  Player.GetComponent(CharacterController);
}

function Update(){
  if(!Player) return;
 if (contr.isGrounded){
   Player.transform.eulerAngles.y += Input.GetAxis("Horizontal");  
   deltaDirection = Vector3(0, 0,Input.GetAxis("Vertical"));
   deltaDirection = Player.transform.TransformDirection(deltaDirection);
   deltaDirection *= velocity;
 }

  deltaDirection.y -= gravity * Time.deltaTime;
  contr.Move(deltaDirection * Time.deltaTime); 
}

function OnPlayerDisconnected (player : NetworkPlayer){
 Network.RemoveRPCs(player, 0);
 Network.DestroyPlayerObjects(player);
}

What is diffrent (blue) from the script of the first lesson?

1 - We are replacing the function "Start" by "OnLoaded". It will run, when the Server (or Client) starts.

2 - We are replacing Instantiate by Network.Instantiate (it has a new parameter - not important here).

3 - We haven't animation control here. We will learn how to do it in the multiplayer environment soon.

4 - We have something to do (remove and destroy) when the player disconnects and the event "OnPlayerDisconnected" is fired.

You must drag the prefab "Human" on the variable "Avatar0", in the "Inspector".

We need to add to the Main Camera, the same "camera-follow" script:

var target : Transform;
var distance = 5.0;
var height = 1.0;
var heightDamping = 2.0;
var rotationDamping = 3.0;
 
var ac: AvatarControl;

function LateUpdate () {
  if(!ac.Player) return;
 target = ac.Player.transform;
	
 if(!target) return;
	
 wantedRotationAngle = target.eulerAngles.y;
 wantedHeight = target.position.y + height;
 currentRotationAngle = transform.eulerAngles.y;
 currentHeight = transform.position.y;

 currentRotationAngle = Mathf.LerpAngle (currentRotationAngle, wantedRotationAngle,rotationDamping * Time.deltaTime);

 currentHeight = Mathf.Lerp (currentHeight, wantedHeight, heightDamping * Time.deltaTime);
 currentRotation = Quaternion.Euler (0, currentRotationAngle, 0);
	
 transform.position = target.position;
 transform.position -= currentRotation * Vector3.forward * distance;
 transform.position.y = currentHeight;
 transform.LookAt (target);
}

You need to drag "Administration" on the variable "ac", in the Inspector.

Now, we can create an executable (not for web) of this exercise, and, for this, be sure to tick "Run In Background" and set "Display Resolution Dialog" to "Disabled" like we said before.

Download the zipped demo:

RIGHTCLICKING HERE

You can create multiple copies of the demo on your machine. Or you can have a Server on your machine and clients on other computers (using the IP address recomended). Note that the avatars do not have animation and are clones.




PREVIOUS LESSON NEXT LESSON
TOC HOME PAGE