Bootcamp Multiplayer Demo

Smooth Movement

Movement updates are sent with a 100 millisecond interval and messages are sent unreliable. Some messages may not be received, but they will not interfere or delay other messages that are far more important such as shooting or weapon changes.


/// From file: PlayerLocal.cs
    
    public void Update () 
    {
      try
      {
        if (this.engine != null)
        {
                this.ReadKeyboardInput();
                this.nameText.text = this.engine.LocalPlayer.name;
                this.Move();
                this.Anim();
        }
       }
       catch (Exception e)
       {
           Debug.Log(e);
       }
    }

    private void Move()
    {
      if (Time.time > this.nextMoveTime)
      {     
	  /// MoveOp is executed only if curent time is greater than next move time. This operation triggers an unreliable event and is subject to prediction and correction.
	   this.MoveOp(Constants.EV_MOVE, false);            
	}
    }
	
     public void MoveOp(byte operationCode, bool reliable)
     {
	/// Position and Target rotation are converted to a float array in preparation for broadcast to the server
                   
  	float[] Position = GetPosition(this.transform.position);
	float[] Rotation = GetRotation(this.transform.rotation);
	float[] TargetPosition = GetPosition(this.SoldierTarget.position);
        	
	/// position and rotation info is stored in a hashtable to be broadcast.
      Hashtable evInfo = new Hashtable();
      evInfo.Add(Constants.STATUS_PLAYER_POS, (float[])Position);
      evInfo.Add(Constants.STATUS_PLAYER_ROT, (float[])Rotation);
      evInfo.Add(Constants.STATUS_TARGET_POS, (float[])TargetPosition);
	
      engine.peer.OpRaiseEvent(operationCode, evInfo, reliable);
              
      /// up to 10 times per second
      this.nextMoveTime = Time.time + 0.1f;
     }

Messages are received in Game.cs in the EventAction function. Depending on the evenCode received (in this case Constants.EV_MOVE) it is sent to the remote player that corresponds to the playerid that broadcast the event. This is determined by the LiteEventKey.ActorNr. With that info, the player is extracted from the players array in the room and used to set the ā€œpā€ variable for later execution... in this particular case using the SetPosition function.


/// From file: Game.cs
    
public void EventAction(byte eventCode, Hashtable photonEvent)
{
   if (eventCode != Constants.EV_MOVE)
   {
      this.DebugReturn("EventAction() " + eventCode);
   }
  
   int actorNr = (int)photonEvent[(byte)LiteEventKey.ActorNr];
            
   /// get the player that raised this event
   Player p;
   this.Players.TryGetValue(actorNr, out p);

   switch (eventCode)
   {
      case Constants.EV_MOVE:
      {          	              
       p.playerRemote.SetPosition((Hashtable)photonEvent[(byte)LiteEventKey.Data]);				break;
       break;
	}
   }

Setting the transform position and rotation directly would cause the remote players to jump from position to position. To make movement appear smooth we update the position with Vector3.Lerp and the rotation with Quaternion.Slerp in usePhoton.cs in every frame.


/// From file: usePhoton.cs
    
void UpdatePositions()
{
   if (GameInstance == null || GameInstance.Players == null)
   {
      return;
   }

   foreach (Player player in GameInstance.Players.Values)
   {
      Transform t;

      if (!player.PlayerIsLocal)
      {
         /// was dead
         if (player.playerTransform == null)
         {
            AddRemotePlayer(player);
         }

         t = player.playerTransform;

         if(!player.playerRemote.Dead)
         {
            /// set this Transform position
            t.transform.position =
            Vector3.Lerp(t.transform.position, player.playerRemote.pos, Time.deltaTime * 7.0f);

            /// set this Transform rotation
            t.transform.localRotation =
            Quaternion.Slerp(t.transform.localRotation, player.playerRemote.rot, Time.deltaTime * 7.0f);
         }
         
       /// get the corresponding TextMesh (used to display the name per player)
       Hashtable DataActor = new Hashtable();
       DataActor.Add("actornr", player.playerID);
       DataActor.Add("actorname", player.playerRemote.Name);
       t.FindChild("Pelvis/EnemiesRef").SendMessage("SetActorNr", DataActor);
    }            
  }
}