Neither null nor type check detecting destroyed object/component? - c#

I have an issue for which I've found a workaround, but can't understand why Unity isn't working the way I expect, or what I'm doing wrong. Basically, I am referencing a renderer component of a destroyed object in a script (long after it's been destroyed), and neither null checks nor type checks let me know it's destroyed.
I have a script that's modifying the shaders on some objects temporarily so I can apply effects on to them; when the effects are done it puts the materials/shaders back the way they were.
It happened that I was swapping out a character's weapon in the same frame I began one of these effects, so the script wound up with a reference to the renderer of a destroyed object (the old weapon). Many frames later when the effect was to finish, it naturally err'd out.
I tried a type check first to see if the reference was still to a renderer -- no luck. Unity's doc says a null check should work, but it still doesn't.
if (pair.Key != null && pair.Key is Renderer renderer) { renderer.materials = pair.Value; }
I get:
MissingReferenceException: The object of type 'SkinnedMeshRenderer' 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.
activeInHierarchy likewise does not work.
Adding a DestroyImmediate to the code where I destroy the object works fine; the object isn't there by the time the effect starts, so the reference is never made. However, that only solves this one problem, and not other such cases.
Can anyone tell me how to check properly for references to destroyed objects or components, or if I'm doing something wrong in my checking? Should I be referencing the gameObject instead of the renderer component or something like that?
EDIT:
Since a commenter asked for it, here's a gist of the full code for the effect. The destruction is happening outside this, as it's totally unrelated.
https://gist.github.com/jackphelps/f507d949a84c53457f570c5009bcb6d4
SOLVED -- SORT OF?
I changed the dictionary to store the renderer's game object instead of the renderer, and as expected a simple null check worked for detecting a destroyed object. I'd love to know why it doesn't work on the renderer, and if there's a proper way to check!

The best way I could find to solve this was to store a reference to the renderer's gameObject rather than to the renderer component itself. In this case, a simple null check at the end of the script works fine, as expected.
I'm not accepting this answer because it still doesn't tell me how to check for a component's presence when the game object was removed ¯(°_o)/¯

Related

An assigned variable in Unity 3D becomes null during code even though nothing is done to it directly

I have a building grid system in Unity that is based on the TileMap system that already exists. I have systems for placing objects by clicking, and randomly generating objects that also get placed. Placing an object paints the tile a different color to indicate that it's placed.
I'm trying to make a system where you can remove placed objects, which means that the tilemap also needs to be updated to reflect the removal. This doesn't work at all, and creates some issues that I have never encountered and I have no idea how to solve.
I have a function that paints the tiles, and it goes like this:
public void TakeArea(Vector3Int start, Vector3Int size)
{
MainTilemap.BoxFill(start, tileName, start.x, start.y, start.x + size.x, start.y + size.y);
}
This all works for placing, but when I try to do it using an OnClick function, I get some errors. The "start" and "size" are passed on correctly, and when printing the values for the area, I get the correct ones. The problem is with the "tileName" variable this time, as it randomly becomes "Null" when I try to print it, even though in the game itself I an see that the assigned value is not "Null", but it references a tile that should be painted.
I have tried so many things for a couple of hours, but I think I finally found a solution. I made some adjustments to that function so that it's exactly the same as it is when an object is generated, as that already works, so I'm first Instantiating the object with this code:
GameObject oreObj = Instantiate(prefab, poss, Quaternion.identity);
Then I will get the object position and size from there the same way I would as if I was placing the object, so it would be like this:
Vector3Int start = gridLayout.WorldToCell(oreBeingRemoved.GetStartPosition()); TakeAreaWithTile(start, oreBeingRemoved.Size, emptyTile);
When I do all this, I get an error that says
"The variable 'grid' of 'BuildingSystem' has not been assigned. You probably need to assign the grid variable of the 'BuildingSystem' script in then inspector."
The problem is that the variable is assigned. The "grid" is always there in the editor, just like that tile that I talked about was, but it says that it isn't for some reason.
Here's a screenshot:
I have tried rewriting the code so many times, I spent about 10 hours here but nothing seems to be working.
As said in the comments the lines you show do not contain the assignment of the variables or what happens to them in between. Nor does it show how your object is instantiated.
One thing that could be your problem: Prefabs can only be serialized with references to themselves or other Prefabs. References to other objects in the scene will get lost.
A fix would be a method that assigns your grid reference in the Awake method of your BuildingSystem class.

Destroying Instantiated Clone Components Breaks Original Game Object Components?

Maybe im missing something obvious but to keep it short. I have working code for a character. When you "dash" I want to leave behind an after-image type effect, by cloning the player, removing its unneeded components and then applying a shader. Issue is as soon as I instantiate the clone the original player stops functioning (cant move etc). It still has all its components and everything as normal, and the clone does get the correct components removed. But it still breaks. As soon as I remove that line, its back to normal. Any ideas?
This is the only relevant code in the script that instantiates the clone.
private void DodgeEffect()
{
GameObject _DodgeSFX = Instantiate(gameObject, transform.position, transform.rotation );
Destroy(_DodgeSFX.GetComponent<PlayerController>());
Destroy(_DodgeSFX.GetComponent<PlayerCombat>());
Destroy(_DodgeSFX.GetComponent<WarpController>());
Destroy(_DodgeSFX.GetComponent<Animator>());
}
its because you are making a copy of gameObject. while Instantiate() returns a GameObject, it also returns whatever you put into the first section of the method. instead, make a seperate gameObject than the player in the editor, and make _DodgeSFX public so you can put the copy into the slot. Then, just instantiate that seperate GameObject and you wont have to destroy the components through script(because you remove the components in the editor), saving time
Ok so from some testing I think its down to the Unity.InputSystem only allowing 1 instance of each Input to be enabled at once. When I instantiated a clone of the player, it still goes through the Enable/Awake functions before those components are destroyed, and since the Inputs on that clone were never disabled, that becomes the "main" one. If I set the main player's scripts with Inputs deactive and then active again, it all works as normal. Note that the script was still working on the main character just fine, it was only the inputs that was broken. Im still not sure why this is the case, if its a limitation of the input system, or intentional, cant seem to find documentation of this experience anywhere.

Unity: Camera.main is failing after I switch scenes

I am having a weird issue with some of my code, and I could really use some help.
I have a script attached to a gameobject that is unique to a particular scene, so anything within Start() will only run when that scene is loaded. In this script, I am accessing Camera.main, since I use settings attached to the camera gameobject (it may sound inefficient, but it is necessary for the style of game we are creating). Anyway, if I start from that scene directly in Unity, it works just fine, but if I start from my intro scene and then load into the aforementioned scene, I get this error:
MissingReferenceException: The object of type 'Camera' 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 weird thing is, I can Debug.Log(Camera.main) and it gives me the name of the camera. So Camera.main is not null, and it is not destroyed like it says in the error message. Here is my full script:
private void Start()
{
Debug.LogError(Camera.main);
gameManager = FindObjectOfType<GameManagerScript>();
if (Camera.main == null)
{
Debug.LogError("Camera.main is null");
}
else
{
gameManager.LoadMusic(Camera.main);
gameManager.LoadAmbient(Camera.main);
gameManager.FadeStereoPan(Camera.main.gameObject.GetComponent<SwipeActivator>().stereoPanInNode);
}
}
The three functions above are custom functions I wrote, but I don't know why they would be causing the issue, since they work if I start up the scene directly.
I wasn't having this issue for months, and then all of a sudden, I am getting this error, even though I didn't change any of the code. Any help would be greatly appreciated.
I believe your MainCamera is being destroyed between scenes. Set it up as "DontDestroyOnLoad()"
See this Unity question:
https://answers.unity.com/questions/430141/need-the-same-main-camera-for-multiple-scenes.html
Ah, I fixed it. I dove into my custom functions and found that I was accessing a camera variable that is set at the beginning of the game, and then on each camera transition, but it was failing since I was loading a scene and no transitions that scene had yet to be completed. I just had to reassign that variable in the script above so it was not pointing to the destroyed object in the previous scene. Thank you JiveTurkey for pointing me in the right direction!

Unity - Prefab GUID and GetCorrespondingObjectFromOriginalSource

I was considering making a pooling system using the prefab GUID as keys in dictionary. One issue I'm having tho is getting the "original" GUID or prefab from an instance of it.
AssetDatabase.TryGetGUIDAndLocalFileIdentifier()
Works fine when handing it a reference to the original, but not on instances.
PrefabUtility.GetCorrespondingObjectFromOriginalSource()
Should return the original prefab, but it always returns null (unless its handed a reference to the original in which case it just returns itself).
Any ideas? :)
As far as I remember. PrefabUtility is only working in Editor, not in the game itself.
Look at the docs:
https://docs.unity3d.com/ScriptReference/PrefabUtility.GetCorrespondingObjectFromOriginalSource.html
that method need an argument to work.
And like Kaffi said it's only working in the Editor (PrefabUtility is in UnityEditor class).

Player can't move after restarting

I created a simple Scene with a SceneManager implemented as a Singleton.
When using only that scene, It works flawless.
Then I added a restart button and problems started.
Initially I had some NullReferenceException Issue: I found the cause, the problem was due to some arrays of the singleton instance that were not cleared; the script found an array with x elements, but all of them were null references. So I added some code in the Start() function in order to check if the array is empty or not. If it isn't I clear the array (before the start the array must be empty, if it is not, it's an array of null references).
After solving all the NullReferenceException issues (that drove the application to crash), I am still not able to make the application run after restart.
The new issue is that the player, whose GameObject is defined in the scene as a GameObject with a CapsuleCollider, a RigidBody and a RigidBodyFPSController, doesn't move after restarting. I think it's a problem with the attached script that maybe are not created again after restarting.
Any idea?
Try
Application.LoadLevel("your_scene");
Add this to the restart_function/code that you have.
This should reset the level/scene to the original that was loaded.
I think you can initialize array first with game objects and after restart it has fixed reference in array that is first defined so it point to the old objects that are now destroy, and the script trying to referenced it.
reinitialize objects with creating array null and assigning value on it.
or application.load("scene_name")

Categories