» Object Movement

» Overview

The Object Movement example shows how to utilize User Variables as a means of making simple 3D object movement without server-side Extension usage.

User Variables are one of the three server variable objects available in SmartFoxServer to store data to be shared among the clients of an application or game (the other two are Room Variables and Buddy Variables, which are out of the scope of this tutorial). As the word says, a server variable is stored on the server and broadcasted to the other clients when a change occurs. In particular, a User Variable is broadcasted to all the other users in the same Room where the owner of that variable is located. This means that a change in a User Variable will be reflected on all other clients within the same Room so that, for example, they can update the scene accordingly.

In this example all the users are represented by a basic shape that they move around in a simple environment (which represents the server Room): so we need to collect the "avatar" position and rotation updates from all the clients to keep the scene in sync. This can be easily done with User Variables: when we create or update the variables representing those informations, all the other clients in the Room are notified and they can put the avatars of their owners in the right place.
In addition to synchronizing avatar positions, each user can also select what model their avatar should use as well as the color. These are also set using User Variables.

You should notice that using User Variables is not the most efficient and scalable way to send object positions over and over again, in particular in realtime games. A better but more complex approach involves sending updates using the UDP protocol to a server-side Extension, but this will be the topic of a more advanced example.

>> DOWNLOAD the source files <<

» Installation

» Running the example

In order to run the application follow these steps:

  1. make sure your SmartFoxServer 2X installation contains the BasicExamples Zone definition;
  2. start SmartFoxServer 2X;
  3. make sure the client runs on the same machine as the server (the IP address can otherwise be configured as explained in this document via the source files);
  4. open the /deploy/ObjectMovement.html file in a browser.

» Source code setup

The complete project is contained in a Unity package. To access and build it please follow the following steps:

  1. create a empty new Unity project;
  2. in the Unity project tab, right click and select Import Package -> Custom Package...;
  3. browse to the package for this example in the /source folder;
  4. open the _Scenes/Connection scene.

All relevant C# code is in the Scripts folder and the SmartFoxServer 2X client API DLL is contained in the Plugins folder.

» Code Highlights

Similar to the other examples, we have a script to control the connection to the server and to log in as a user. When login successfully occurs, the user will automatically be moved to the game world.

The following code uses the client side RoomManager to check if the room called Game Room already exists. If not then we are the first player to join, and we will create it instead of simply joining.

void OnLogin() {
	// We either create the Game Room or join it if it exists already
	if (smartFox.RoomManager.ContainsRoom("Game Room")) {
		smartFox.Send(new JoinRoomRequest("Game Room"));
		
	} else {
		RoomSettings settings = new RoomSettings("Game Room");
		settings.MaxUsers = 40;
	 
		smartFox.Send(new CreateRoomRequest(settings, true));
	}
}

Once the user joins the Room named Game Room, the code then loads the Game scene.

NOTE
When switching scene remember to unsubscribe from the event listener. Else the code which exists in the old scene will still be getting callbacks. This is easily done with the smartFox.RemoveAllEventListeners(); method.

Each client keeps track of the local avatar model as well as all the remote avatars in the Room. The remote players are tracked using the following Dictionary for looking up their avatar using the SFSUser object as a key.

private Dictionary<SFSUser, GameObject> remotePlayers = new Dictionary<SFSUser, GameObject>();

Each avatar GameObject is represented by a mesh (cube, sphere and cylinder) as well as a material in different colors. These are randomly set for each client on entering the game.

int numModel = UnityEngine.Random.Range(0, playerModels.Length);
int numMaterial = UnityEngine.Random.Range(0, playerMaterials.Length);
SpawnLocalPlayer(numModel, numMaterial);

When remote players join, the local client needs to spawn an avatar for them in the local simulation. This is tracked when a given client recieves an update of User Variables from a new client. The following code shows, how the User Variables for position, rotation, model and material are read and used for spawning.

public void OnUserVariableUpdate(BaseEvent evt) {

	...
	
	if (!remotePlayers.ContainsKey(user)) {
		// New client just started transmitting - lets create remote player
		Vector3 pos = new Vector3(0, 1, 0);
		if (user.ContainsVariable("x") && user.ContainsVariable("y") && user.ContainsVariable("z")) {
			pos.x = (float)user.GetVariable("x").GetDoubleValue();
			pos.y = (float)user.GetVariable("y").GetDoubleValue();
			pos.z = (float)user.GetVariable("z").GetDoubleValue();
		}
		
		float rotAngle = 0;
		if (user.ContainsVariable("rot")) {
			rotAngle = (float)user.GetVariable("rot").GetDoubleValue();
		}
		
		int numModel = 0;
		if (user.ContainsVariable("model")) {
			numModel = user.GetVariable("model").GetIntValue();
		}
		
		int numMaterial = 0;
		if (user.ContainsVariable("mat")) {
			numMaterial = user.GetVariable("mat").GetIntValue();
		}
		
		SpawnRemotePlayer(user, numModel, numMaterial, pos, Quaternion.Euler(0, rotAngle, 0));
		
		...
	}
}

Transmitting the updated User Variables is done automatically for position and rotation for the local player as part of the FixedUpdate() Unity callback. Not to spam remote clients sending updates even if the local user did not move or rotate his avatar, the PlayerController class uses a dirty flag to track input keypresses. When the flag is set to dirty, then position and rotation are transmitted.

void FixedUpdate() {

	....

	// If we spawned a local player, send position if movement is dirty
	if (localPlayer != null && localPlayerController != null && localPlayerController.MovementDirty) {
		List<UserVariable> userVariables = new List<UserVariable>();
		userVariables.Add(new SFSUserVariable("x", (double)localPlayer.transform.position.x));
		userVariables.Add(new SFSUserVariable("y", (double)localPlayer.transform.position.y));
		userVariables.Add(new SFSUserVariable("z", (double)localPlayer.transform.position.z));
		userVariables.Add(new SFSUserVariable("rot", (double)localPlayer.transform.rotation.eulerAngles.y));
		smartFox.Send(new SetUserVariablesRequest(userVariables));
		localPlayerController.MovementDirty = false;
	}

Material and model changes are triggered via the GUI code. The following code shows how the material is being set locally and the selection sent to the other players via User Variables.

public void ChangePlayerMaterial(int numMaterial) {
	localPlayer.GetComponentInChildren<Renderer>().material = playerMaterials[numMaterial];

	List<UserVariable> userVariables = new List<UserVariable>();
	userVariables.Add(new SFSUserVariable("mat", numMaterial));
	smartFox.Send(new SetUserVariablesRequest(userVariables));
}

When a remote user disconnects, then this is tracked via the OnUserExitRoom callback. The code simply cleans up and removed the remote avatar from the scene.

public void OnUserExitRoom(BaseEvent evt) {
	// Someone left - lets make certain they are removed if they didnt nicely send a remove command
	SFSUser user = (SFSUser)evt.Params["user"];		
	RemoveRemotePlayer(user);
}

private void RemoveRemotePlayer(SFSUser user) {
	if (user == smartFox.MySelf) return;
	
	if (remotePlayers.ContainsKey(user)) {
		Destroy(remotePlayers[user]);
		remotePlayers.Remove(user);
	}
}	

NOTE
You should read the comments to methods and properties in the example source code for additional informations.

» More resources

You can learn more about the described feature by consulting the following resources: