- Home /
how to check if an element has been removed from a GameObject array C#
Hi, i have four GameObject[] arrays where i store certain gameObjects inside once they spawn.
For example:
public GameObject[] battleshipAmount;
//i can find and place them in the array once they spawn thanks to their tag
battleshipAmount = GameObject.FindGameObjectsWithTag("battleship");
The thing that happens is that these objects will get destroyed and i want to do something once one of these gameObjects becomes null and gets removed from the array. I want to it detect if an element from just this array has been destroyed and then i want to do something (increasing an int by 1 for example).
I figure since this is happening during game time it has to be updated when an element has been removed from the array so i will need to loop through the array to see if the index of it has been decreased by a certain amount. How will i go about doing this the correct but most simplest way (not that good with unity and c#).
Do you plan to call
battleshipAmount = GameObject.FindGameObjectsWithTag("battleship");
every frame ?
Yes, that way i can know if a new object with that tag has been instantiated and is active
Answer by Hellium · Sep 22, 2015 at 07:41 AM
I would use a property (the equivalent of a getter & setter in another programming language)
// C#
// Note : I haven't tested this code, I wrote it in a simple Notepad, do not hesitate to tell me if there is an error.
private GameObject[] battleshipAmount ;
public GameObject[] BattleshipAmount
{
get { return battleshipAmount ; }
set
{
if( battleshipAmount == null )
{
battleshipAmount = value ;
return ;
}
if( battleshipAmount.Length > value.Length )
{
// A GameObject has been destroyed
// Check which gameobject has been destroyed
for( int i = 0 ; i < battleshipAmount.Length ; ++i )
{
for( int j = 0 ; j < value.Length ; ++j )
{
// Check if gameobject is in the new array
if( battleshipAmount[i].GetInstanceID() == value[j].GetInstanceID() )
{
// GameObject still here
continue ;
}
}
// If this part of the code is reached, a destroyed gameobject has been found
Debug.Log( "The gameobject #" + i + " has been destroyed!" ) ;
}
}
else if( battleshipAmount.Length < value.Length )
{
// A GameObject has been added
}
battleshipAmount = value ;
}
}
Then, in your code, never use battleshipAmount, but use BattleshipAmount instead (notice the capital letter). BattleshipAmount can be use in the exact same way you would use battleshipAmount.
NullReferenceException: Object reference not set to an instance of an object Placement.set_BattleshipAmount (UnityEngine.GameObject[] value) (at Assets/Scripts/Placement.cs:20) Placement.Update () (at Assets/Scripts/Placement.cs:138)
for the line:
if( battleshipAmount.Length > value.Length )
Oops, indeed.
[...]
set
{
if( battleshipAmount == null )
{
battleshipAmount = value ;
return ;
}
[...]
$$anonymous$$issingReferenceException: The object of type 'GameObject' 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. Placement.set_BattleshipAmount (UnityEngine.GameObject[] value) (at Assets/Scripts/Placement.cs:42) Placement.Update () (at Assets/Scripts/Placement.cs:143)
i managed to get this from the line:
// If this part of the code is reached, a destroyed gameobject has been found Debug.Log( "The GameObject named " + battleshipAmount[i].name + " has been destroyed!" );
which seems to be correct, or should it display the Debug.Log and not the error?
another side note, even though the array is public i cant find it in the inspector of the script holder.
It seems to work now! Do you know a good way to use this method on 3 other arrays aswell? It will be alot of code (a lot of repeated code aswell). Thanks for your contribution anyways @Hellium
Here is a more generic code.
I use delegates to call a function I want when a gameobject is missing. Thus, for every array, you can have a different function. I don't have time to explain you how delegates works, but I invite you to read about it, it's very useful.
// Prototype of the functions to call when a gameobject is missing
private delegate void $$anonymous$$yDelegate();
[SerializeField]
private GameObject[] battleshipAmount ;
[SerializeField]
private GameObject[] anotherArray ;
public GameObject[] BattleshipAmount
{
get { return battleshipAmount ; }
set
{
CheckArray( battleshipAmount, value, Foo ) ;
}
}
public GameObject[] AnotherArray
{
get { return anotherArray ; }
set
{
CheckArray( anotherArray, value, Bar ) ;
}
}
private void CheckArray( oldArray, newArray, $$anonymous$$yDelegate delegate )
{
if( oldArray == null )
{
oldArray = newArray ;
return ;
}
if( oldArray.Length > newArray.Length )
{
// A GameObject has been destroyed
// Check which gameobject has been destroyed
for( int i = 0 ; i < oldArray.Length ; ++i )
{
for( int j = 0 ; j < newArray.Length ; ++j )
{
// Check if gameobject is in the new array
if( oldArray[i].GetInstanceID() == newArray[j].GetInstanceID() )
{
// GameObject still here
continue ;
}
}
// If this part of the code is reached, a destroyed gameobject has been found
delegate() ;
}
}
else if( oldArray.Length < newArray.Length )
{
// A GameObject has been added
}
oldArray = newArray ;
}
// The prototype of this function $$anonymous$$UST be the same as the prototype of the delegate
private void Bar()
{
Debug.Log( "Bar" );
}
// The prototype of this function $$anonymous$$UST be the same as the prototype of the delegate
private void Foo()
{
Debug.Log( "Foo" );
}
seems complex, ill go with the repeated code for now but i bookmarked this question so i can look into ur view on delegates at some other point. Thanks for your time!
Hi, Have a look On This
Hope this may help you. Nsks
Answer by Suddoha · Sep 22, 2015 at 12:07 PM
You can also use an event-based approach to not rely on a loop each time a battleship is destroyed.
Basically, you'd have a script on each battleship which contains the following:
Action <BattleshipScript> DestroyShip;
From your script that handles the changes (you mentioned an int that will increase, for example) you would then subscribe to this action. Before a ship is destroyed, call DestroyShip(this) and it will execute all methods that have been subscribed to that particular ship. This allows for more control and less polling, no loops etc.
Unfortunately i cannot provide a code sample as it is not exactly clear what you already have, but this will only need a couple of lines to set up, probably not more than 3-4.