Need help with VR Multiplayer

Options
I took a course on Unity muliplayer and created a Battle Royal game that works great. Using that as a base I constructed a simple scene that after connecting to a server, a player can create a game and start it in a lobby. If I start the game without any other players, the scene loads and the player is instantiated and can walk around and get no errors. My player has a simple capsule body and 2 low poly hands. When I start the game with another player (my other Oculus), in either headset, I can look around, see the other player where they spawned, but our players are frozen.

I am sooooooo close to creating a multiplayer VR game and just need a little help understanding what I did wrong and how everything should be structured. There are absolutely NO tutorials on creating a multiplayer VR game with PUN. It would surely help a lot of people. Thanks.

The error is:
NullReferenceException: Object reference not set to an instance of an object
GameManager.SpawnPlayer () (at Assets/Scripts/GameManager.cs:51)
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <567df3e0919241ba98db88bec4c6696f>:0)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <567df3e0919241ba98db88bec4c6696f>:0)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <567df3e0919241ba98db88bec4c6696f>:0)
Photon.Pun.PhotonNetwork.ExecuteRpc (ExitGames.Client.Photon.Hashtable rpcData, Photon.Realtime.Player sender) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:473)
Photon.Pun.PhotonNetwork.OnEvent (ExitGames.Client.Photon.EventData photonEvent) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:2063)
Photon.Realtime.LoadBalancingClient.OnEvent (ExitGames.Client.Photon.EventData photonEvent) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2826)
ExitGames.Client.Photon.PeerBase.DeserializeMessageAndCallback (ExitGames.Client.Photon.StreamBuffer stream) (at C:/Dev/photon-sdk-dotnet/PhotonDotnet/PeerBase.cs:656)
ExitGames.Client.Photon.EnetPeer.DispatchIncomingCommands () (at C:/Dev/photon-sdk-dotnet/PhotonDotnet/EnetPeer.cs:549)
ExitGames.Client.Photon.PhotonPeer.DispatchIncomingCommands () (at C:/Dev/photon-sdk-dotnet/PhotonDotnet/PhotonPeer.cs:1598)
Photon.Pun.PhotonHandler.Dispatch () (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:205)
Photon.Pun.PhotonHandler.FixedUpdate () (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:139)

The offending code is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using System.Linq;

public class GameManager : MonoBehaviourPun
{
    public float postGameTime;

    [Header("Players")]
    public string playerPrefabLocation;
    public PlayerController[] players;
    public Transform[] spawnPoints;
    public int alivePlayers;

    private int playersInGame;

    // instance
    public static GameManager instance;

    void Awake()
    {
        instance = this;
    }

    void Start()
    {
        players = new PlayerController[PhotonNetwork.PlayerList.Length];
        alivePlayers = players.Length;

        photonView.RPC("ImInGame", RpcTarget.AllBuffered);
    }

    [PunRPC]
    void ImInGame()
    {
        playersInGame++;

        if (PhotonNetwork.IsMasterClient && playersInGame == PhotonNetwork.PlayerList.Length)
            photonView.RPC("SpawnPlayer", RpcTarget.All);
    }

    [PunRPC]
    void SpawnPlayer()
    {
        GameObject playerObj = PhotonNetwork.Instantiate(playerPrefabLocation, spawnPoints[Random.Range(0, spawnPoints.Length)].position, Quaternion.identity);

        // initialize the player for all other players
        playerObj.GetComponent<OVRPlayerController>().photonView.RPC("Initialize", RpcTarget.All, PhotonNetwork.LocalPlayer);
    }

    public PlayerController GetPlayer(int playerId)
    {
        return players.First(x => x != null && x.id == playerId);
    }

    public PlayerController GetPlayer(GameObject playerObject)
    {
        return players.First(x => x != null && x.gameObject == playerObject);
    }

    public void CheckWinCondition()
    {
        if (alivePlayers == 1)
            photonView.RPC("WinGame", RpcTarget.All, players.First(x => !x.dead).id);
    }

    [PunRPC]
    void WinGame(int winningPlayer)
    {
        // set the UI win text
        GameUI.instance.SetWinText(players.First(x => x.id == winningPlayer).photonPlayer.NickName);

        Invoke("GoBackToMenu", postGameTime);
    }

    void GoBackToMenu()
    {
        NetworkManager.instance.ChangeScene("Menu");
    }
}

Comments

  • JohnTube
    JohnTube ✭✭✭✭✭
    Options
    Hi @PeskyPug,

    Thank you for choosing Photon!

    Looking at the stack trace you can easily spot the line where the null reference exception is thrown:
    playerObj.GetComponent<OVRPlayerController>().photonView.RPC("Initialize", RpcTarget.All, PhotonNetwork.LocalPlayer);
    

    So there are possible cases:
    • 1. playerObj is null. (not likely)
    • 2. playerObj (prefab) does not have OVRPlayerController
    • 3. oVRPlayerController.photonView is null. (most likely? maybe timing issue, the photonView is not initialized/set yet?)

    You could replace the RPC call in 3 with Photon's custom instantiation data.
    Or better, get rid of it altogether and instead, since the PhotonView knows its owner via photonView.Owner, implement IPunInstantiateMagicCallback.OnPhotonInstantiate in OVRPlayerController and call (or do) "Initialize" there.

    You need to add checks there and make sure each of these is not null.