- Home /
Strange GameObject null reference only in RPC
Hi.
I am working on a multi-player Frisbee simulator and I am having trouble working out why I am getting a Null Reference to a GameObject only in a RPC.
Code description:
I am using an authoritative server system. Every player who joins uses Network.Instatiate() to create their own player, but everything else is server side. I have a script (FrisbeeThrow.cs) that is disabled on all clients on Awake() so it only runs on the server. This is the problematic script that calculates who possesses the Frisbee and updates the position of the Frisbee.
In FrisbeeThrow.cs I use a GameObject reference (frisbeeOwner) to store which player has the Frisbee. If a clients player GameObject gets within range they will pick the Frisbee up and frisbeeOwner is set to point to that player GameObject.
Problem:
If the server wants to throw the Frisbee, a local method will be called and the frisbeeOwner reference will be set to null so the script knows that nobody is carrying the Frisbee. This works 100% correctly and the server can repeatedly throw and collect the Frisbee any amount of times, and there are no null references. If a client wants to throw the Frisbee, the client executes the code-
networkView.RPC("ClientThrowFrisbee", RPCMode.Server, cameraTransform.position, cameraTransform.forward);
-and the RPC method defined in FrisbeeThrow.cs is called. I have spent hours placing Debug.Log() statements throughout the script and I have enabled full network log information in the console, and am certain that the RPC is called correctly.
The problem is that I get a null reference exception when trying to use the frisbeeOwner reference, only in the RPC.
Just so you know, the parameters passed in the RPC are the clients position and look direction at the time of mouse click, to provide a more accurate experience for clients.
Things I have tried:
Checking on both client and server that ALL unnecessary components are disabled (cameras, audio listeners, scripts ETC)
Lots of Debug.Log() statements
Creating a new RPC with a different name and removing the parameters
Checking all the network views are set up correctly
Numerous other tiny things
Additional information:
All other global variables are not null in the RPC, only frisbeeOwner is null
I am using the default unity networking system
I am fairly new to unity networking, so I could have a misunderstanding of some basic concept, (although I have done lots of research and am quite confident)
ClientClick.cs is what the client uses to remotely request to throw the frisbee.
ClientClick.cs belongs to the same prefab GameObject that FrisbeeThrow.cs belongs to.
Scripts:
FrisbeeThrow.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FrisbeeThrow : MonoBehaviour
{
//Various properties to set in the editor
public float distanceFromFace;
public float height;
public float checkRadius;
public float throwPower;
public float rotationSpeed;
public float pickupDelay;
public Camera fpsCamera;
Messages messager; //Class I wrote that displays messages to the player
GameObject frisbee;
GameObject frisbeeOwner;
GameObject frisbeeOwnerCamera;
bool canPickUp = true;
float nextPickUpAllowed = 0;
List<NetworkPlayerGameobject> networkPlayers = new List<NetworkPlayerGameobject>();
void Awake()
{
if (Network.isServer)
{
fpsCamera = GetComponentInChildren<Camera>();
frisbee = GameObject.FindWithTag("Frisbee");
messager = GameObject.Find("Messaging Object").GetComponent<Messages>();
networkPlayers.Add(new NetworkPlayerGameobject(Network.player));
}
if (!networkView.isMine || Network.isClient) //Disable if client
{
enabled = false;
}
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//If mouse not locked
//Throw frisbee
if (Screen.lockCursor)
{
if (Network.isServer)
{
ServerThrowFrisbee();
}
}
}
}
void FixedUpdate()
{
//Double check just in case client gets to this part
if (Network.isServer)
{
//If frisbee is null, find it
if (frisbee == null)
{
frisbee = GameObject.FindWithTag("Frisbee");
}
//Determine whether the frisbee can be picked up again
if (Time.time > nextPickUpAllowed)
{
canPickUp = true;
}
//If everything exists and frisbee can be picked up
if (frisbee != null && frisbee.rigidbody != null && canPickUp && frisbeeOwner == null)
{
//Find all players near the frisbee
List<GameObject> playersInRange = new List<GameObject>();
Collider[] colliders = Physics.OverlapSphere(frisbee.transform.position, checkRadius);
playersInRange.Clear();
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i].CompareTag("Player"))
{
playersInRange.Add(colliders[i].gameObject);
}
}
//If atleast one player is near frisbee
if ((playersInRange != null) && (playersInRange.Count > 0))
{
//Set frisbee owner to the first player in list
frisbeeOwner = playersInRange[0];
frisbeeOwnerCamera = frisbeeOwner.transform.FindChild("Main Camera").gameObject;
TakeFrisbee();
}
}
//If somebody owns the frisbee
if (frisbee != null && frisbee.rigidbody != null && frisbeeOwner != null)
{
//Update its position so it follows the player
frisbee.transform.position = frisbeeOwnerCamera.transform.position
+ frisbeeOwnerCamera.transform.forward * distanceFromFace +
frisbeeOwnerCamera.transform.up * height;
frisbee.transform.rotation = frisbeeOwnerCamera.transform.rotation;
}
}
}
//The server calls this function to throw the frisbee
void ServerThrowFrisbee()
{
if (frisbee != null && frisbeeOwner != null && frisbeeOwner == gameObject)
{
//Set parameters of frisbee to allow flight
Debug.Log("FRISBEE THROWN");
frisbee.rigidbody.isKinematic = false;
frisbee.rigidbody.detectCollisions = true;
frisbee.rigidbody.velocity = frisbeeOwnerCamera.transform.forward * throwPower;
frisbee.rigidbody.angularVelocity = frisbee.transform.TransformDirection(new Vector3(0, rotationSpeed, 0));
frisbee.GetComponent<FrisbeeScript>().thrown = true;
SetFrisbeePickupDelay();
frisbeeOwner = null;
frisbeeOwnerCamera = null;
}
}
//This is the problematic function!
//"frisbeeOwner" is null for some reason in this RPC
[RPC]
void ClientThrowFrisbee(Vector3 posAtThrow, Vector3 forwardAtThrow, NetworkMessageInfo msi)
{
messager.AddMessage("ClientThrowFrisbee() called");
//This probably over complicated code determines whether the RPC sender owns the frisbee
bool RPCSenderOwnsFrisbee = false;
for (int i = 0; i < networkPlayers.Count; i++)
{
if (networkPlayers[i].networkPlayer == msi.sender && networkPlayers[i].networkPlayerGameObject == frisbeeOwner)
{
RPCSenderOwnsFrisbee = true;
}
}
if (frisbee != null && frisbeeOwner != null && RPCSenderOwnsFrisbee)
{
//Set parameters of frisbee to allow flight
messager.AddMessage("Conditions passed");
Debug.Log("FRISBEE THROWN");
frisbee.transform.position = posAtThrow + forwardAtThrow * distanceFromFace;
frisbee.transform.forward = forwardAtThrow;
frisbee.rigidbody.isKinematic = false;
frisbee.rigidbody.detectCollisions = true;
frisbee.rigidbody.velocity = forwardAtThrow * throwPower;
frisbee.rigidbody.angularVelocity = frisbee.transform.TransformDirection(new Vector3(0, rotationSpeed, 0));
frisbee.GetComponent<FrisbeeScript>().thrown = true;
SetFrisbeePickupDelay();
frisbeeOwner = null;
frisbeeOwnerCamera = null;
}
else
{
Debug.Log("frisbeeOwner is null!");
}
}
//Changes some properties of the frisbee so it behaves nicely when picked up
void TakeFrisbee()
{
frisbee.rigidbody.velocity = Vector3.zero;
frisbee.rigidbody.angularVelocity = Vector3.zero;
frisbee.rigidbody.isKinematic = true;
frisbee.rigidbody.detectCollisions = false;
frisbee.GetComponent<FrisbeeScript>().thrown = false;
}
//Sets a timer that prevents the frisbee from being picked up immediately after throw
void SetFrisbeePickupDelay()
{
canPickUp = false;
nextPickUpAllowed = Time.time + pickupDelay;
}
void OnPlayerConnected()
{
StartCoroutine("DelayedAdditionToNetworkPlayerList");
}
//All code below: Maps each GameObject to their NetworkPlayer, so the script can determine whether
//the person who is trying to throw the frisbee actually owns it
IEnumerator DelayedAdditionToNetworkPlayerList()
{
yield return new WaitForSeconds(5);
networkPlayers.Add(new NetworkPlayerGameobject(Network.player));
}
class NetworkPlayerGameobject
{
public NetworkPlayer networkPlayer;
public GameObject networkPlayerGameObject;
public NetworkPlayerGameobject(NetworkPlayer np)
{
networkPlayer = np;
networkPlayerGameObject = FindNetworkPlayersGameObject(np);
}
GameObject FindNetworkPlayersGameObject(NetworkPlayer np)
{
GameObject[] listOfPlayerGameObjects = GameObject.FindGameObjectsWithTag("Player");
for (int i = 0; i < listOfPlayerGameObjects.Length; i++)
{
if (listOfPlayerGameObjects[i].networkView.owner == np)
return listOfPlayerGameObjects[i];
}
return null;
}
}
}
ClientClick.cs
using UnityEngine;
using System.Collections;
public class ClientClick : MonoBehaviour
{
public Transform cameraTransform;
void Start()
{
cameraTransform = transform.FindChild("Main Camera");
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (Screen.lockCursor)
{
if (!cameraTransform)
{
cameraTransform = transform.FindChild("Main Camera");
}
if (cameraTransform)
{
networkView.RPC("ClientThrowFrisbee", RPCMode.Server, cameraTransform.position, cameraTransform.forward);
}
}
}
}
}
Do you have any idea of how to fix this? Or even a suggestion of something I could try?
Thanks very much for your time.
Your answer
Follow this Question
Related Questions
Unity networking tutorial? 6 Answers
Server receives RPCs after RemoveRPCs and Destroy 0 Answers
Networking-multplayer issue 1 Answer
How can I use a GameObject to hold all references in a network game? 0 Answers
[Mirror Networking] NullReferenceException on player when trying to start server a second time 1 Answer