This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 3 years ago.
I am trying to troubleshoot the following piece of code which causes a NullReferenceException. Essentially when an object is created, I'm trying to have it register with my game manager class. This is the component for my object:
void Start()
{
Debug.Log("Registering");
if (gameObject != null)
{
GameMngr.Instance.RegisterAttraction(gameObject);
}
else
{
Debug.Log("Gameobject null");
}
}
In my game manager I have the following:
public void RegisterAttraction(GameObject newAttraction)
{
if (newAttraction != null)
{
Debug.Log("Attempting to register gameObject");
attractionLastID++;
sceneAttractions.Add(attractionLastID, newAttraction);
Debug.Log("Registered");
}
else
{
Debug.Log("unable to register: null provided");
}
}
My console output is as following:
Registering
Attempting to register gameObject
NullRefereceException
The fact that my code displays the attempting to register gameObject lines leads me to believe that my newAttraction variable is not null. Why do I get the error ?
Thank for the help
Where's attractionLastId defined by chance? Is it being initialized? Since you're doing a null check inside of your Start function already, try refactoring your RegisterAttraction function to look something like:
public void RegisterAttraction(GameObject newAttraction)
{
Debug.Log("Attempting to register gameObject");
attractionLastID++;
sceneAttractions.Add(attractionLastID, newAttraction);
Debug.Log("Registered");
}
I highly recommend setting a breakpoint on the beginning curly of RegisterAttraction. Take it step by step and hover over each variable to see which one is null.
Related
I want to force the build process to fail if some validation conditions are not met.
I've tried using an IPreprocessBuildWithReport with no success:
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
public class BuildProcessor : IPreprocessBuildWithReport
{
public int callbackOrder => 0;
public void OnPreprocessBuild(BuildReport report)
{
// Attempt 1
// Does not compile because the 'BuildSummary.result' is read only
report.summary.result = BuildResult.Failed;
// Attempt 2
// Causes a log in the Unity editor, but the build still succeeds
throw new BuildFailedException("Forced fail");
}
}
Is there any way to programmatically force the build process to fail?
I'm using Unity 2018.3.8f1.
As of 2019.2.14f1, the correct way to stop the build is to throw a BuildFailedException.
Other exception types do not interrupt the build.
Derived exception types do not interrupt the build.
Logging errors most certainly do not interrupt the build.
This is how Unity handles exceptions in PostProcessPlayer:
try
{
postprocessor.PostProcess(args, out props);
}
catch (System.Exception e)
{
// Rethrow exceptions during build postprocessing as BuildFailedException, so we don't pretend the build was fine.
throw new UnityEditor.Build.BuildFailedException(e);
}
Just for clarity, this will NOT stop the build.:
// This is not precisely a BuildFailedException. So the build will go on and succeed.
throw new CustomBuildFailedException();
...
public class CustomBuildFailedException: BuildException() {}
You can use OnValidate() which seems to be exactly what you're looking for.
Let's say you want to make sure a reference to a UI Text component is not null before building, in the script that should have the text reference, you add
private void OnValidate()
{
if (text == null)
{
Debug.LogError("Text reference is null!");
}
}
Having Debug.LogError calls during the build process actually cause the build to fail.
Okay so I am making a custom feature for my OP-Prison server, one of the things that I need to do is get an integer from the players.yml file, check if it is >= one, if it is take away one, save it and then if it is still above one then they can repeat the action untill it's 0.
The issue comes with the fact that I have to restart the server for the file to change, and even when I do, it will only go down by one integer at a time, before having to reload it again.
GUI Creation code:
Main main = Main.getPlugin(Main.class);
#SuppressWarnings("unused")
private FileControl fc;
#SuppressWarnings("unused")
private FileControl playerfc;
public static String inventoryname = Utils.chat(Main.pl.getFileControl().getConfig().getString("Backpacks.White.InventoryName"));
public List<Player> WhiteOpened = new ArrayList<>();
public static Inventory whiteBackpack(Player player) {
Inventory whiteBackpack = Bukkit.createInventory(null, 27, (inventoryname));
UUID uuid = player.getUniqueId();
whiteBackpack.setItem(10,
new ItemCreator(Material.INK_SACK).setData(8)
.setDisplayname(Utils.chat("&fCommon Packages &8ยป &f&l" + Main.pl.getPlayerFile().getConfig().getInt("Users." + uuid + ".Packages.Common")))
.getItem());
return whiteBackpack;
}
Code for updating the config + item when the Commonpackage is clicked:
#EventHandler
public void whiteBackpackInteract(InventoryClickEvent event) {
Player player = (Player) event.getWhoClicked();
UUID uuid = player.getUniqueId();
ItemStack clicked = event.getCurrentItem();
String title = event.getInventory().getName();
if (title.equals(inventoryname)) {
// Making it so that the item cannot be moved
event.setCancelled(true);
if (clicked != null) {
if (event.getSlot() == 10) {
// Getting the user's common packages section in the config and checking if it is greater than or equal to 1.
if (Main.pl.getPlayerFile().getConfig().getInt("Users." + uuid + ".Packages.Common") >= 1) {
// Saving the user's common package section to 'currentCommon'
Integer currentCommon = Main.pl.getPlayerFile().getConfig().getInt("Users." + uuid + ".Packages.Common");
// Taking away one from 'currentCommon' and saving it to 'newCommon'
Integer newCommon = currentCommon - 1;
// Getting the 'players.yml' file
File file = new File(main.getDataFolder(), "players.yml");
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
// Checking if the current common keys is greater than or equal to 1
if (currentCommon >= 1) {
try {
//Now, Here's where the error lies.
//Gets the player's common package count and sets it to the 'newCommon' count
config.set("Users." + uuid + ".Packages.Common", newCommon);
//Saves the players.yml file
config.save(file);
} catch (IOException e) {
e.printStackTrace();
}
// Updates the inventory they're currently in (Atleast it's meant to...)
player.updateInventory();
// Sends them a message (This is just for testing purposes, making sure it's working.)
player.sendMessage(Utils.chat("&8(&9Vexil&8) &fCommon Package"));
}
}
}
}
}
}
If there is any other code that you need, just ask I'll happily provide it for you.
Right now, you need to restart the server for it to save the data to the file. This should not happen, since you are calling the method config.save(file). The following is simply speculation, but it's the only cause that I think can easily explain what is going on.
In the object that is returned by getPlayerFile().getConfig(), there is likely a variable that stores a FileConfiguration object. That variable houses all the data from the players.yml file. In your whiteBackpackInteract() method, you load the data all over again. You then continue on to write to this NEW FileConfiguration variable, rather than the one that is stored in getPlayerfile().getConfig(). Since you then proceed to save to the file directly, the variables stored in the getPlayerfile().getConfig() is never told that you changed some values around. To fix this, you need to change the following:
config.set("Users." + uuid + ".Packages.Common", newCommon);
config.save(file);
to this:
Main.pl.getPlayerFile().getConfig().set("Users." + uuid + ".Packages.Common", newCommon);
Main.pl.getPlayerFile().getConfig().save(file);
and then delete this line of code:
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
This should solve your problem entirely. If it does not, I would recommend not using your friend's custom config API and instead just use the ones that are built in. Using third party code that you don't properly understand can very often lead to problems such as this.
The following are not the bugs, but are suggestions to help improve your code:
You should be sure to put your comments ABOVE or to the RIGHT over the code they describe. People read from top to bottom, so the comments (before I made the suggested edit to your post) were all below the code they describe.
Typically, you want to try to make sure that if code doesn't need to be run, it isn't. Since the int newCommon is not used until inside that if statement, you should move it in there.
You are using Main.getPlugin();
Now while that doesn't seem like such a bad thing, your getting an unassigned variable, I have no idea how it is working but you're assigning Main to Main. There are 2 proper ways to actually get the main class.
The first, and generally best way, is to use dependency injection.
So basically,
public class Main extends JavaPlugin {
#Override
public void onEnable() {
BackpackListener listener new Backpacklistener(this);
getServer().getPluginManager().registerEvents(listener, this);
}
}
public class BackpackListener implements Listener {
private Main instance;
private BackpackUtil util;
public BackpackListener(Main instance) {
this.instance = instance;
util = new BackpackUtil();
}
#EventHandler
public void onClick(InventoryClickEvent event) {
//code
util.whiteBackpack(instance);
}
public class BackpackUtil {
public Inventory whiteBackpack(Main instance) {
FileConfiguration config = instance.getConfig();
//Do things
instance.saveConfig();
}
}
The next way you can do it is less optimal, and frowned upon, but still an easier option.
public class Main() {
public static Main instance;
#Override
public void onEnable() {
instance = this;
}
}
public class ConfigHelper() {
Main instance = Main.instance;
FileConfiguration config = instance.getConfig();
//Do things
instance.saveConfig();
}
It's good to get out of the habit of using the second method (It's called a singleton), because normally the main class will change, or have multiple instances, etc... but with Spigot there can only be one main instance and one thread.
I have the following script
using System.Collections;
using UnityEngine;
public class Bullet : MonoBehaviour
{
[SerializeField]
private float _speed;
private IEnumerator ShootAt(Vector3 target)
{
while (transform.position != target)
{
yield return null;
transform.localPosition = Vector3.MoveTowards(transform.localPosition, target, _speed * Time.deltaTime);
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Animal")
{
Destroy(gameObject);
}
}
}
After the bullet hits the animal and gets destroyed, I get the following error: MissingReferenceException: The object of type 'Bullet' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.
The error is referring to the while loop in my code. I guess it's still running that loop because the condition to stop it hasn't been met yet
while(transform.position != target)
I tried adding a second condition to stop that loop, like
while(transform.position != target || gameObject != null)
but that doesn't seem to work. Any help would be more than appreciated.
Apart from some refactoring like another user suggested in the comments, you could try the same logic you have, but reversed:
while(gameObject != null || transform.position != target)
The || operator will be evaluated in order it is written, so in your case the transform.position is still being evaluated before your null check.
Also, you could consider using StopCoroutine prior to calling Destroy on your object.
Finally, as I've also personally had trouble with such a case (perhaps related to this behavior)
The only thing that the C# object has is a pointer to the native object, so Destroy(myGameObject) destroys the native object but the managed object in the C# side still exists, and is destroyed when it's no longer referenced and is collected by the garbage collector.
So you might just consider introducing a isHit member variable and setting it to true upon a successful collision, followed up with your Destroy.
This question already has answers here:
How to find child of a GameObject or the script attached to child GameObject via script
(4 answers)
Closed 5 years ago.
I try to access FirstPersonCharacter from my script by using FPSController.
I pass FPSController to my script. Now I try to intialize FirstPersonCharacter which is a child of FPSController.
I pass FPSController to the script, now I try to use FPSController to initialize FirstPersonCharacter, because I try to avoid too many public variables, especially if it is not necessary.
public GameObject FPSController;
private GameObject FirstPersonCharacter;
I need help at the initialisation part:
void Start()
{
FirstPersonCharacter = ???
}
I tried it like this:
void Start()
{
FirstPersonCharacter = FPSController.transform.GetChild(0);
}
But I get Cannot implicitly convert type "UnityEngine.Transform" to "UnityEngine.GameObject"
It works like this:
FirstPersonCharacter = FPSController.transform.GetChild(0).transform.gameObject;
I have two spawn spots where a player will show up upon connection.
I need that, when one of the players connects on one of the spots, any other player will always spawn at the other spot.
Here's some visual in case it helps: https://goo.gl/Y0ohZC
Here is the code I'm using:
using UnityEngine;
using System.Collections;
public class SpawnSpot : MonoBehaviour {
public int teamId=0;
public GameObject[] Spots; //Drag the spots in here (In the editor)
bool[] OccupiedSpawnSpots;
//Using Photon Networking
void OnJoinedRoom()
{
//Request the recent OccupiedSpawnSpots List
PhotonView.RPC("RequestList", PhotonTargets.MasterClient, PhotonNetwork.player);
}
//In "RequestList" the MasterClient sends his List of the SpawnSpots
//by calling "ReceiveList"
[RPC]
void RequestList(PhotonPlayer player)
{
PhotonView.RPC("ReceiveList", PhotonTargets.All, player, OccupiedSpawnSpots);
}
[RPC]
void ReceiveList(PhotonPlayer Sender, bool[] ListOfMasterClient)
{
OccupiedSpawnSpots = ListOfMasterClient;
//Get the free one
if (OccupiedSpawnSpots[0] == false)
{
//Spawn player at 0
if (Sender == PhotonNetwork.player)
PhotonNetwork.Instantiate("PlayerController", Spots[0].transform.position);
OccupiedSpawnSpots[0] = true;
}
else
{
//Spawn player at 1
if (Sender == PhotonNetwork.player)
PhotonNetwork.Instantiate("PlayerController", Spots[1].transform.position);
OccupiedSpawnSpots[1] = true;
}
}
The errors given are:
Assets/Scripts/SpawnSpot.cs(14,28): error CS0120: An object reference
is required to access non-static member `PhotonView.RPC(string,
PhotonPlayer, params object[])'
Assets/Scripts/SpawnSpot.cs(22,28): error CS0120: An object reference
is required to access non-static member `PhotonView.RPC(string,
PhotonPlayer, params object[])'
Assets/Scripts/SpawnSpot.cs(36,47): error CS1501: No overload for
method Instantiate' takes3' arguments
Assets/Scripts/SpawnSpot.cs(43,47): error CS1501: No overload for
method Instantiate' takes3' arguments
Thanks in advance, IC
It appears that you are trying to call an instance function using a static reference. instead of doing PhotonView.RPC("RequestList", PhotonTargets.MasterClient, PhotonNetwork.player);
you need to create a reference to an PhatorView object, and then call the RPC function on it.
public PhotonView photonView;
void OnJoinedRoom()
{
if(photonView == null)
{
photonView = GetComponent<PhotonView>();
}
//Request the recent OccupiedSpawnSpots List
PhotonView.RPC("RequestList", PhotonTargets.MasterClient, PhotonNetwork.player);
}
you should have a PhotonView object on the same GameObject that this script is on, or assign a reference to a PhotonView in the editor.
That should fix your problems, but I think you should look into compiler errors and how to to fix them. In Unity3D if you double click the error in the console it will take you to the line that isn't compiling. It also tends to give you a pretty good hint as to why it isn't compiling.
your error was this
"Assets/Scripts/SpawnSpot.cs(14,28): error CS0120: An object reference is required to access non-static member `PhotonView.RPC(string, PhotonPlayer, params object[])'"
which means that you need an object in order to call this function.
The third and fourth errors are because you are calling Instantiate with only 2 arguments, but it takes at least 4. Include the rotation of the GameObject you're trying to instantiate, and a group number:
PhotonNetwork.Instantiate("PlayerController", Spots[0].transform.position, Quaternion.identity, 0);
Note that you may not want the identity quaternion, so you may need to change this. Also I'm not familiar with PhotonNetwork so 0 may not be an advisable group.