PlayerObjects null on server

I’m starting to learn Unity’s Netcode for online multiplayer games.

I have a simple 2D scene where players can connect and control their own square. That works fine and everything gets synched between them.

I want to spawn monsters that get controlled by the server. Now, I get stuck commanding the monsters to walk to the nearest player.

My idea:

  • Make sure only the server controls the monster (in OnNetworkSpawn)
  • Let the server search for the nearest player (in RecalculateNearestPlayerServerRpc)
  • Move the monster toward the nearest player (in Update)

My problem is that while trying to get all PlayerObjects the respective variables in the ConnectedClientsListof the NetworkManager is null:

NetworkManager.Singleton.ConnectedClientsList[0]
{Unity.Netcode.NetworkClient}
   ClientId: 0
   OwnedObjects: Count = 0
   PlayerObject: null
NetworkManager.Singleton.ConnectedClientsList[1]
{Unity.Netcode.NetworkClient}
   ClientId: 1
   OwnedObjects: Count = 0
   PlayerObject: null

Here is the code for my MonsterController:

public class MonsterController : NetworkBehaviour
{
   private Rigidbody2D rb;
   private float speed = 10.0f;

   void Awake()
   {
       rb = GetComponent<Rigidbody2D>();
   }

   public override void OnNetworkSpawn()
   {
       if (!IsServer) enabled = false;
   }

   void Update()
   {
       RecalculateNearestPlayerServerRpc();
       var dir = nearestPlayer.transform.position - transform.position;
       rb.velocity = dir * speed;
   }


   [ServerRpc]
   private void RecalculateNearestPlayerServerRpc()
   {
       float nearestPlayerDistance = float.MaxValue;
       foreach (NetworkClient client in NetworkManager.Singleton.ConnectedClientsList)
       {
           float sqrDistance = (client.PlayerObject.transform.position-transform.position).sqrMagnitude;

           if (sqrDistance < nearestPlayerDistance)
           {
               nearestPlayerDistance = sqrDistance;
               nearestPlayer = client.PlayerObject;
           }
       }
   }
}

I spawn and give ownership like this:

public class GameManager : NetworkBehaviour {
   [SerializeField] private PlayerController _playerPrefab;

   public override void OnNetworkSpawn() {
       SpawnPlayerServerRpc(NetworkManager.Singleton.LocalClientId);
   }  

   [ServerRpc(RequireOwnership = false)]
   private void SpawnPlayerServerRpc(ulong playerId) {
       var spawn = Instantiate(_playerPrefab);
       spawn.NetworkObject.SpawnWithOwnership(playerId);
   }

   public override void OnDestroy() {
       base.OnDestroy();
       MatchmakingService.LeaveLobby();
       if(NetworkManager.Singleton != null )NetworkManager.Singleton.Shutdown();
   }
}

What am I doing wrong?

There’s a player prefab section in NetworkManager, if you leave that empty, no player gets a “player object” associated with it since in this case you are expected to manually spawn player objects. That is what you do but I can’t tell you whether you’re expected to assign this reference yourself as well. You could try assigning it, and if that is not allowed, you’d have to keep your own list of current players around (Dictionary with clientId as key, player object as value).

1 Like

Thank you for the hint.

What I was doing is delaying the player spawn from the NetworkManager. I used the player prefab but called the wrong method while spawning.

Instead of:

  [ServerRpc(RequireOwnership = false)]
   private void SpawnPlayerServerRpc(ulong playerId) {
       var spawn = Instantiate(_playerPrefab);
       spawn.NetworkObject.SpawnWithOwnership(playerId);
   }

I spawn with SpawnAsPlayerObject:

   [ServerRpc(RequireOwnership = false)]
    private void SpawnPlayerServerRpc(ulong playerId) {
        var spawn = Instantiate(_playerPrefab);
        spawn.NetworkObject.SpawnAsPlayerObject(playerId);
    }