- Home /
NullReference in script? Prefab is linked in inspector
Hey all! I’m a starting developer, been having fun with Unity, C# and the tutorials from Brackeys for a few weeks now. Enjoying the hell out of the Making a Multiplayer FPS tutorials from Brackeys! With help from the 14th episode, I tried to implement an UI bar to show the players stamina, instead of the fuel from the tutorial. Turns out I get a “NullReferenceException: Object reference not set to an instance of an object PlayerUI.Update () (at Assets/Scripts/PlayerUI.cs:19)”. It’s driving me crazy since the code doesn’t give any errors while building, plus, yeah I made sure the right prefabs are connected in the inspector. Still, the stamina for the sprinting works, but the bar doesn’t. Been trying a few days now, but I don’t have much hair left. So I would like some help from any of you super smart people. Anyway, this is my PlayerController:
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
[RequireComponent(typeof(PlayerMotor))]
public class PlayerController : MonoBehaviour {
[SerializeField]
private float speed = 5f;
[SerializeField]
private float defaultSpeed = 5f;
[SerializeField]
private float runspeed = 10f;
[SerializeField]
private float staminaStopSpeed = 1f;
[SerializeField]
private float lookSensitivity = 3f;
private bool cantSprint = false;
private PlayerMotor motor;
private Animator animator;
[SerializeField]
private float staminaAmount = 1f;
private float staminaBurn = 0.3f;
private float staminaRegen = 0.1f;
private float staminaStopSec = 3f;
//getter stamina
public float GetStaminaAmount () {
Debug.Log (staminaAmount);
return staminaAmount;
}
void Start () {
motor = GetComponent<PlayerMotor>();
animator = GetComponentInChildren<Animator>();
}
void Update () {
//movement 3d vector
float xMov = Input.GetAxis("Horizontal");
float zMov = Input.GetAxis("Vertical");
Vector3 movHorizontal = transform.right * xMov;
Vector3 movVertical = transform.forward * zMov;
//final movement vector
Vector3 velocity = (movHorizontal + movVertical) * speed;
//animate movement
animator.SetFloat("ForwardVelocity", zMov);
//apply movement in player motor script
motor.Move (velocity);
float yRot = Input.GetAxisRaw("Mouse X");
Vector3 rotation = new Vector3 (0f, yRot, 0f) * lookSensitivity;
//apply rotation
motor.Rotate(rotation);
// CAMERA rotate
float xRot = Input.GetAxisRaw("Mouse Y");
float cameraRotationX = xRot * lookSensitivity;
//apply camerarotation
motor.RotateCamera(cameraRotationX);
//apply run
if (Input.GetButton ("Sprint") && cantSprint == false && staminaAmount > 0.05f) {
//stamina burn
staminaAmount -= staminaBurn * Time.deltaTime;
speed = runspeed;
animator.speed = 2f;
} else {
//stamina regen
staminaAmount += staminaRegen * Time.deltaTime;
speed = defaultSpeed;
animator.speed = 1f;
}
//clamp stamina
staminaAmount = Mathf.Clamp (staminaAmount, 0f, 1f);
if (staminaAmount <= 0.05f) {
cantSprint = true;
Invoke("StaminaStop", staminaStopSec);
animator.speed = 0.2f;
cantSprint = false;
}
if (staminaAmount >= 1f) {
speed = defaultSpeed;
animator.speed = 1f;
}
//just to test if the stamina calculations are working
GetStaminaAmount();
}
//stamina invoke method
void StaminaStop() {
speed = staminaStopSpeed;
}
}
Then the playerSetup script:
using UnityEngine;
using UnityEngine.Networking;
[RequireComponent(typeof(PlayerManager))]
[RequireComponent(typeof(PlayerController))]
public class PlayerSetup : NetworkBehaviour {
[SerializeField]
Behaviour[] componentsToDisable;
[SerializeField]
string remoteLayerName = "RemotePlayer";
[SerializeField]
string dontDrawLayerName = "DontDraw";
[SerializeField]
GameObject playerGraphics;
[SerializeField]
GameObject playerUIPrefab;
private GameObject playerUIInstance;
Camera sceneCamera;
void Start() {
if (!isLocalPlayer) {
DisableComponents();
AssignRemoteLayer();
} else {
sceneCamera = Camera.main;
if (sceneCamera != null) {
sceneCamera.gameObject.SetActive(false);
}
//disable player graphics for local player
SetLayerRecursively (playerGraphics, LayerMask.NameToLayer(dontDrawLayerName));
//create player UI
playerUIInstance = Instantiate (playerUIPrefab);
playerUIInstance.name = playerUIPrefab.name;
//configure player UI
PlayerUI ui = playerUIInstance.GetComponent<PlayerUI>();
if (ui == null)
Debug.LogError ("No player UI component on player prefab");
ui.SetController (GetComponent<PlayerController>());
}
GetComponent<PlayerManager>().Setup();
}
void SetLayerRecursively (GameObject obj, int newLayer) {
obj.layer = newLayer;
foreach (Transform child in obj.transform){
SetLayerRecursively (child.gameObject, newLayer);
}
}
public override void OnStartClient() {
base.OnStartClient();
string netID = GetComponent<NetworkIdentity>().netId.ToString();
PlayerManager player = GetComponent<PlayerManager>();
GameManager.RegisterPlayer(netID, player);
}
void AssignRemoteLayer() {
gameObject.layer = LayerMask.NameToLayer(remoteLayerName);
}
void DisableComponents () {
for (int i = 0; i < componentsToDisable.Length; i++) {
componentsToDisable[i].enabled = false;
}
}
//when destroyed
void OnDisable () {
Destroy (playerUIInstance);
if (sceneCamera != null) {
sceneCamera.gameObject.SetActive(true);
}
GameManager.UnRegisterPlayer(transform.name);
}
}
And last but not least, the playerUI script:
using UnityEngine;
public class PlayerUI : MonoBehaviour {
[SerializeField]
RectTransform StaminaFill;
private PlayerController controller;
public void SetController (PlayerController _controller) {
controller = _controller;
}
void Start () {
controller = GetComponent<PlayerController>();
}
void Update () {
SetStaminaAmount(controller.GetStaminaAmount());
}
void SetStaminaAmount (float _amount) {
StaminaFill.localScale = new Vector3 (1f, _amount, 1f);
}
}
So once again, the stamina calculates correct. It works. The prefabs are linked in the inspector correctly (the colored bar rect transform in the playerUI). Still, the bar doesn’t empty/ refill and I get this nullreference at the “SetStaminaAmount() in the PlayerUI Update()”, so I guess the script can’t find the right controller? Or it can’t receive the float amount from the public float in the controller OR the configure playerUI in the setup script doenst work… pff. I don’t get it! It should work right? I hope someone has the answer! Thanks in advance!
The first thing to do is to add a check for null after you get the PlayerController component. If it's null there, you know where to start looking.
The SetController() method is also suspicious. Is it possible that it gets called from somewhere and overwrites controller with a null value? Add another Debug.Log() there.
Does the GameObject this is running on have a PlayerController component? You could add logging after line 10 to check that it's found the controller.
Ah, the problem may be to do with the sequence. PlayerUI.Start won't run until PlayerSetUp.Start has finished, so it will overwrite the value you've set controller to. If you want it to keep the value set in PlayerSetUp.Start, you could remove the PlayerUI.Start function (or change its name to Awake so that it runs as soon as you instantiate it).
As I can see you assign the controller 3 times. 1) In the inspector 2) PlayerSetup script 40th line 3) Start at the PlayerUI
The 3) option is what happens before the Update, so maybe it is the source of the problem. $$anonymous$$aybe the PlayerController component and the PlayerUI component are in different object. Or the other possibility is that some other code calls SetController() as doublemax mentioned.
Not in the inspector (it's private). From the code we've seen. the setting of the controller in PlayerSetup is redundant because it happens before it's set in PlayerUI.Start. $$anonymous$$y suspicion is that there's a controller component on the PlayerSetup object not the PlayerUI object.
Wow :) thanks for all the answers peeps! That's really awesome! Yeah I already thought it was the PlayerController which couldnt be found, but I really didnt understand why. Now I guess it has to do with the start() functions, which one in which script comes first right? Darn. I didnt think about that. The setup and the controller are both on the same gameObject and the playerUI is instantiated with every new player joining the game. So. First thing to try tomorrow is changing the Start() from the playerUI script to Awake(). I have a feeling thats it :) can't wait to try, keep You posted!
If the PlayerUI GameObject is never going to have its own PlayerController component, then you're better off just removing its Start() function as it will confuse things.
If you want the PlayerUI to use its own PlayerController component by default (if there is one), and then have other objects be able to override that default (as you're doing when you call its SetController() from PlayerSetup.Start()), then you should change the PlayerUI.Start() to Awake()..
Answer by Blue-Cut · Sep 07, 2016 at 08:33 PM
SetStaminaAmount(controller.GetStaminaAmount());
At this line, the only object that could be null is clearly the 'controller'.
There are 2 ways in your code to set the variable 'controller'. The first one is in PlayerUI, method Start() :
controller = GetComponent<PlayerController>();
And the second one in PlayerSetup, method Start() :
ui.SetController (GetComponent<PlayerController>());
This doesn't sound good. I think that one of these two ways is doing a GetComponent<PlayerController>
on a gameObject that actually doesn't have the PlayerController attached.
If the PlayerSetup is on the gameObject with the PlayerController, so delete the PlayerUI's Start().
If the PlayerUI is on the gameObject with the PlayerController, remove the line that does SetController in the PlayerSetup's Start
Again, thanks a lot for all the replies! You really helped me out here! I changed the start method into an awake and it works! I completely understand why now, cuz with every player joining the game, a prefab UI gameObject (not a child of the player object) is instantiated. The UI script had to get the 'right' player to show the sta$$anonymous$$abar for by 'getting' the 'setter' method I wrote on one of the player scripts. However, I forgot it was being overwritten (eh, english?) by the other scripts so it had to be called first to get the right UI for a newly added player in the game! I know I didn't write that long scripts yet, but still, the length can confuse me I guess so I couldn't see what I did wrong. Learned a lot again, thank you so much!