When I load the arena scene, only the player prefab of the host player will spawn, while the client player prefab totally does not spawn at all. Below is the relevant code of the object in charge of spawning the player characters.
public class PlayerLoader : NetworkBehaviour
{
// public TextMeshProUGUI chara;
[SerializeField] GameObject[] goats;
private bool gameStart = false;
[ServerRpc(RequireOwnership = false)]
public void SpawnPlayerServerRpc(ulong clientId, int prefabId, ServerRpcParams serverRpcParams = default)
{
//if (!IsServer) GetComponent<NetworkObject>().SpawnWithOwnership(NetworkManager.Singleton.LocalClientId);
GameObject newPlayer;
newPlayer = Instantiate(goats[prefabId]);
Debug.Log("Spawned");
NetworkObject netObj = newPlayer.GetComponent<NetworkObject>();
netObj.SpawnWithOwnership(clientId, true);
}
public override void OnNetworkSpawn()
{
}
// Start is called before the first frame update
void Start()
{
GetComponent<NetworkObject>().SpawnWithOwnership(OwnerClientId);
int id = PlayerPrefs.GetInt("ButtonParameter", -1);
Debug.Log(id);
SpawnPlayerServerRpc(OwnerClientId, id);
PlayerPrefs.DeleteKey("ButtonParameter");
}
}
I noticed that the debug logs for both Start() and SpawnPlayerServerRpc() did not even show up for the non-host/client player, which means the game completely ignored these instructions! I’m not really sure how to approach this problem aside from that. Any help would be greatly appreciated!
I made modifications with the assumption that PlayerLoader was on an in-scene placed NetworkObject.
If that is the case, then this adjustment should work:
/// <summary>
/// I am assuming this component sits on an an in-scene placed NetworkObject.
/// </summary>
public class PlayerLoader : NetworkBehaviour
{
// public TextMeshProUGUI chara;
[SerializeField] GameObject[] goats;
private bool gameStart = false;
[ServerRpc(RequireOwnership = false)]
public void SpawnPlayerServerRpc(int prefabId, ServerRpcParams serverRpcParams = default)
{
var newPlayer = Instantiate(goats[prefabId]);
var netObj = newPlayer.GetComponent<NetworkObject>();
// Use the ServerRpcParams.Receive to know who the sender is of an Rpc.
//netObj.SpawnWithOwnership(clientId, true);
// As long as this component is on an in-scene placed NetworkObject, then
// each player as they connect should invoke the OnNetworkSpawn which will invoke
// this SpawnPlayerServerRpc. Use the client id of the client who sent the Rpc as the owner id.
netObj.SpawnWithOwnership(serverRpcParams.Receive.SenderClientId, true);
Debug.Log($"[Client-{serverRpcParams.Receive.SenderClientId}] Spawned as prefab index-{prefabId} ({newPlayer.name}) on server the side.");
}
public override void OnNetworkSpawn()
{
int id = PlayerPrefs.GetInt("ButtonParameter", -1);
Debug.Log($"[Client-{NetworkManager.LocalClientId}] Chose to spawn as the {goats[id].name} (index {id}).");
SpawnPlayerServerRpc(id);
PlayerPrefs.DeleteKey("ButtonParameter");
}
}
If PlayerLoader is not on an in-scene placed NetworkObject, then let me know and describe how the associated NetworkObject is spawned.
If it is an in-scene placed NetworkObject, then what is happening is the Start method is being invoked prior to OnNetworkSpawn. RPCs cannot be invoked prior to spawning the associated NetworkObject.
Let me know if this resolves your issue?
I got it to work, thank you! Initially it didn’t work but I realized in another script I used SceneManager to move from the menu scene to the arena scene where the player prefab loader was located instead of NetworkManager.Singleton.SceneManager and I also put that before NetworkManager.Singleton.StartHost()/StartClient() when it should have been put after. Combined with your code I finally solved the issue.
1 Like
Awesome!
Happy netcoding!
1 Like