I'm wanting to turn off multiple game objects and all their associated children at once with a single method call.
My thinking behind this was to create a list to hold all of the game objects I want to deactivate and pass all those objects in. However, I'm trying to implement the actual SetActive method call with my list and Im running into some issues.
Here is my code just now:
public List<GameObject> deactivate_Screen = new List<GameObject>();
void OnClick()
{
for( int i = 0; i < deactivate_Screen.Count; i++)
{
deactivate_Screen.SetActive(false);
}
}
Now the obvious reason this isn't working is clear to me. A list can't access to the SetActive function I'm trying to achieve. However, I'm at a loss to implement the functionality I require.
Could someone please show me what I need to do, or point me in the right direction to fix my error?
As you correctly recognized, SetActive is a method of a GameObject, not of the List<GameObject>.
You have to invoke SetActive in each iteration for the game object the index i of the current iteration refers to - you can access that object with the List<T> indexer, i.e. by placing square brackets with the index behind deactivate_Screen.
Thus, the "current item" in each iteration is deactivate_Screen[i], hence your loop should look as follows:
for (int i = 0; i < deactivate_Screen.Count; i++)
{
deactivate_Screen[i].SetActive(false);
}
Just replace
deactivate_Screen.SetActive(false);
to
deactivate_Screen[i].SetActive(false);
As List itself is not a game object but its elements are List[0] List[1] List[2] List[3].....
In your loop you need to access the element of the list at the index i:
for( int i = 0; i < deactivate_Screen.Count; i++)
{
deactivate_Screen[i].SetActive(false);
}
or using a foreach loop
foreach (var gobj in deactivate_Screen)
{
gobj.SetActive(false);
}
Related
I'm working on a school project in Windows Forms where we're making an animal registry. I have an Animal class where the animal object is created with the properties Id, Name, Age, Gender, and Friendly. I also have an AnimalManager class that handles a list and through which more animals can be added. At runtime, when having filled out the appropriate boxes with information and pressed the Add Animal-button, the information should be added to a row in a listview (the important part of the task. Everything was peaceful when I could just display it in a listbox).
When I printed my information to a listbox I simply sent a string with the appropriate information through public string AnimalInformation() from the Animal class to the AnimalManager that compiled an array of it and sent it to Form1 to be printed. Since I want to display it in a listview, I figured I should make AnimalInformation() into an array instead, so that I get a 2D array after passing through the AnimalManager, much like it will be displayed when printed. However, I get the exception message System.NullReferenceException: 'Object reference not set to an instance of an object.' in my AnimalManager when running the program and trying to add the animal.
This is the method in the Animal class:
public string[] AnimalInformation()
{
string[] strOut = { id, name, age.ToString(), gender.ToString(), FriendlyStr()};
return strOut;
}
This is the method in the AnimalManager class:
public string[][] GetAnimalInfoArray()
{
string[][] animals = new string[animalList.Count][];
for(int i = 0; i < animalList.Count; i++)
{
Animal animal = animalList[i];
for(int j = 0; j < animal.CountAnimalInfo(); j++)
{
string[] info = animal.AnimalInformation();
animals[i][j] = info[j];
}//here comes the exception
}
return animals;
}
And since I am not too familiar with listview (read: not at all), here's the loop I wrote for printing it all into the listview in my UpdateGUI() method, in case this is what is causing the trouble:
if (manager.Count() > 0)
{
foreach(string[] row in manager.GetAnimalInfoArray())
{
foreach(string item in row)
lvwAnimals.Items.Add(item);
}
}
I've been at this for hours and don't know what's up or down anymore. Am I even on the right track? Do I have to rework this completely? What am I doing wrong to get an error message after that curly bracket?
The first problem is that you're creating an array of arrays but not allocating the arrays in the array... which is mildly confusing, but this is the line:
string[][] animals = new string[animalList.Count][];
animals is an array of string[]. At this point, each element in the animals array is null, not an array. The point where you're having a problem is this:
animals[i][j] = info[j];
At this point animals[i] is uninitialized (null), so there is no array for you to index with j.
There are a number of solutions to the problem, but the most direct method is to simply assign the result of calling AnimalInformation to the array slot and be done with it:
public string[][] GetAnimalInfoArray()
{
string[][] animals = new string[animalList.Count][];
for(int i = 0; i < animalList.Count; i++)
animals[i] = animalList[i].AnimalInformation();
return animals;
}
If you're OK with using LINQ there's an even simpler option: ToArray().
public string[][] GetAnimalInfoArray()
=> animalList.Select(a => a.AnimalInformation()).ToArray();
A couple of notes on the rest of the code...
Your inner loop calls CountAnimalInfo() and AnimalInformation() for each item in the information array. That's 5 times you're calling those two methods, creating at least one array each time you do (and I'm assuming you're not calling AnimalInformation() from inside CountAnimalInfo(), if you are then that's two arrays per item). It would be better to simply call it once and use the resultant array for the loop, if you have to loop at all.
for (int i = 0; i < animalList.Count; i++)
{
var info = animalList[i].AnimalInformation();
animals[i] = new string[info.Length];
for (int j = 0; j < info.Length; j++)
animals[i][j] = info[j];
}
You're spreading what appears to be display code across several methods and classes. While there are times when it's useful to be able to override things in sub-classes, it's generally preferable (in my experience at least) to have the display code in one place: a single method or tightly coupled group of methods in a single class. An Animal doesn't need to know how it is being displayed, it just needs to present properties that other code can use. Your AnimalManager class probably doesn't need to act as go-between for your display code either, it's up to the display code to figure out how things should look. Try this:
foreach (var animal in manager.animalList)
{
lvwAnimals.Items.Add(animal.id);
lvwAnimals.Items.Add(animal.name);
lvwAnimals.Items.Add(animal.age.ToString());
lvwAnimals.Items.Add(animal.gender.ToString());
lvwAnimals.Items.Add(animal.FriendlyStr());
}
This way you've separated the 'display' concern out from the non-display classes, letting them focus more on doing their own job. And extra bonus, it's a lot less lines of code to debug later.
I know this doesnt directly answer , JeremyLakeMan comments about why you have a problem at the moment. But here is what I would do
Given an IEnumerable of Animals called animalList (which you already have)
Assuming Animal Looks Like this
public class Animal{
public string ID;
public String Name;
public int Age;
}
Now
foreach(var animal in animals)
{
lvwAnimals.Items.Add(animal.ID);
lvwAnimals.Items.Add(animal.Name);
lvwAnimals.Items.Add(animal.Age.ToString());
}
thats it, the whole GetAnimalInfoArray is not needed
I have a list of all spawned objects in the game. I'm trying to search through that list to find only objects that are tagged "Road Tag", get their transform position, and convert it to a new variable.
If I use
Debug.Log(spawnedObjects[i].transform.position;
It actually prints out the x, y, z coordinates fine. But I don't know how to assign them to a new Vector3 variable. The syntax for arrays seems to be different.
I have tried:
roadCoordinates[i] = new Vector3(spawnedObjects[i].transform.position.x, spawnedObjects[i].transform.position.y, spawnedObjects[i].transform.position.z);
and
roadCoordinates[i] = spawnedObjects[i].transform.position;
public static Vector3[] roadCoordinates;
public static void FindSpawnedRoads()
{
loopCount = spawnedObjects.Count;
for (int i = 0; i < loopCount; i++)
{
if (spawnedObjects[i].tag == "Road Tag")
{
//This prints perfectly
Debug.Log(spawnedObjects[i].transform.position);
//This gives me NullReferenceException
roadCoordinates[i] = new Vector3(spawnedObjects[i].transform.position.x, spawnedObjects[i].transform.position.y, spawnedObjects[i].transform.position.z);
Debug.Log(roadCoordinates[i]);
}
}
}
I want the variable to be set to the Vector3 coordinates of roadCoordinates[i].
I instead get NullReferenceException. Unless I print SpawnedObjects in which it prints the coordinates like I want.
You have to initialize the roadCoordinates array otherwise you would get the NullReferenceException error. Other than that your logic is okay. You can solve this by adding this line after loopCount assignment:
roadCoordinates = new Vector3[loopCount];
What happens when you do not initialize your array is you create a variable named roadCoordinates but there is no memory allocated or referenced for this variable. Therefore, it does not point to anything in the memory and when you try to use it it says i can't reference to any memory block.
As you mentioned in your question that you have a list of all spawned object you can also store these points in a List then you do not have to allocate any memory, you can just add elements to the list.
List<Vector3> roadCoordinates = new List<Vector3>()
Then you can add elements using roadCoordinates.Add(spawnedObjects[i].transform.position)
If you need to store the indexes of spawned object that has a Road Tag you can use a Dictionary
Dictionary<int, Vector3> roadCoordinates = new Dictionary<int, Vector3>()
Then you can add elements like roadCoordinates.Add(i,spawnedObject[i].transform.position)
You should probably use array.push() (see: https://docs.unity3d.com/ScriptReference/Array.Push.html) instead of writing 'roadCoordinates[i]', as this array is still empty. By using 'push', you add the new position/3D vector to the end of the array, which also works if the array is still empty. I hope this helps you out!
PS: if you first initialize the array to be of a certain length as suggested by the other answer, you will have a lot of empty array items as not every item in your loop is a 'Road tag'.
Yes, as Alexander suggests, using Lists is the way to go. You don't need to use a dictionary as you already know that the roadCoordinates List will only contain items of tag 'Road Tag'.
public static List<Vector3> roadCoordinates = new List<Vector3>();
public static void FindSpawnedRoads()
{
loopCount = spawnedObjects.Count;
for (int i = 0; i < loopCount; i++)
{
if (spawnedObjects[i].tag == "Road Tag")
{
roadCoordinates.Add(new Vector3(spawnedObjects[i].transform.position.x, spawnedObjects[i].transform.position.y, spawnedObjects[i].transform.position.z));
}
}
}
Using Lists is better in this case, because we do not know from the start how many objects we are going to have.
List<Vector3> roadCoordinates = new List<Vector3>();
...
roadCoordinates.Add(new Vector3(...));
Here is the tutorial about lists.
I am creating meshes to send through to Unity to be applied to game objects to be rendered. I have a method that is passed an instance of my MeshData class containing (Vertices, a list of vertex indexes to make triangles, and UV coordinates) and merges that with the current instance of MeshData to produce a final merged MeshData containing the Vertices, Triangle Indexes, and UVs of both merged sets to pass as a Mesh object to Unity.
Reasoning being, I'm making a voxel-type game and I am creating a mesh for each individual block and then merging all the block meshes into a 16x16 "chunk" of blocks. This larger mesh is converted to a Unity Mesh object and applied to a GameObject for the chunk.
When this method is run it is currently getting stuck in an infinite loop that seems to be updating this._tris and data._tris at the same time, so as it loops through data._tris it never reaches the end because every time this._tris is added to, it also updates data._tris. This is unintended behaviour and I'm not sure what is causing it to behave that way.
This particular bit of code was working fine earlier and hasn't been modified at all. But when I modified code in another class it started getting stuck in an infinite loop until it hits an Out of Memory Exception. Regardless of what is being passed to Merge() it should never be getting stuck like that.
// Class for creating MeshData
public class MeshData
{
// MeshData objects
private List<Vector3> _verts = new List<Vector3>();
private List<int> _tris = new List<int>();
private List<Vector2> _uvs = new List<Vector2>();
// MeshData constructor with parameters
public MeshData(List<Vector3> v, List<int> i, Vector2[] u)
{
this._verts = v;
this._tris = i;
this._uvs = new List<Vector2>(u);
}
// MeshData default constructor
public MeshData()
{
}
// Merge Mesh Data
public void Merge(MeshData data)
{
// if data is empty, don't do anything
if(data._verts.Count <= 0)
{
return;
}
// if current meshdata is empty, just set it to be the new data set
if(this._verts.Count <= 0)
{
this._verts = data._verts;
this._tris = data._tris;
this._uvs = data._uvs;
return;
}
// if neither case above is true, merge the two data sets
int count = this._verts.Count;
this._verts.AddRange(data._verts);
for(int i = 0; i < data._tris.Count; i++)
{
this._tris.Add(data._tris[i] + count);
}
this._uvs.AddRange(data._uvs);
}
}
The Merge method should be merging the current MeshData (this) with the given MeshData (data) so that the current MeshData (this) contains the elements of both.
In the Merge method at the for loop "for(int i = 0; i < data._tris.Count; i++)"
it is getting into an infinite loop. When I run the debugger in Visual Studio and watch the Locals variable list, everytime this._tris is updated/added to, it also updates/adds to data._tris. So data._tris is constantly getting larger so the for loop never hits the end of it.
Any help with why it is behaving that way and how to avoid that behavior?
At first glance I'd say your problem is this._tris = data._tris;, you're making this._tris point to data._tris. Might want to add the contents of data._tris to this._tris instead.
Whenever you set a List<T> equal to another List<T> with the = operator, you're establishing a reference to the list, not copying the values over:
In your example you are first creating a new List<int>:
private List<int> _tris = new List<int>();
But then, in your constructor, you set _tris to reference a different list:
this._tris = i;
The old new List<int>()you had is no longer being referenced and will be garbage collected (eventually). This over-write can also happen in your Merge method with the line:
this._tris = data._tris;
If you want this._tris to reference another list (and thus adding to one adds to the other), you use the =. If you instead want to copy all the values over, you use AddRange like you do with _verts:
this._tris.AddRange(data._tris);
I'm having a problem in Unity with C#, I'm trying to create a List or Array that has all the information I need.
0000, 0001,0002, etc.
I want to put this into an List/Array and use this information to instantiate a model on a character selection screen. However, this is the part of code where everything starts to get messed up.
I'm just trying to read out the numbers and add them into the list.
void Start () {
gestureListener = this.GetComponent<GestureListener>();
for (int i = 0; i < numberOfModels; i++) {
string b = i.ToString("0000");
List<string> mylist = new List<string>(new string[b]);
Debug.Log (mylist);
break;
}
}
I get this error:
error CS0029: Cannot implicitly convert type `string' to `int'
The error happens on line 5, but to me this seems an to be an irreplaceable line...
The variable B is a string so I wouldn't know why the lists sees it as an int.
Please let me know if you can help, much appreciated!
If you are trying to instantiate a list to then add elements to this list then you got it wrong. You are currently instantiating a new list with just one element every time you iterate. In other words, you are not putting the list to use, you are just creating a new one every time you loop.
Create your List of Strings outside the loop then add to it from inside the loop.
You should have something like this to populate the list.
void Start()
{
List<string> mylist = new List<string>();
gestureListener = this.GetComponent<GestureListener>();
for (int i = 0; i < numberOfModels; i++) {
string b = i.ToString("0000");
myList.Add(b);
Debug.Log (mylist);
break;
}
}
With that said, at the end of your for-loop your myList will have a collection of models per say. You can then iterate that collection to see all the elements you have pushed.
foreach(var item in mylist)
{
//Do whatever with each Item.
}
If you need more examples, take a look at DotNetPerls List Examples
and this video example with Unity in mind.
void Start () {
gestureListener = this.GetComponent<GestureListener>();
List<string> myList = new List<string>();
for (int i = 0; i < numberOfModels; i++) {
string b = i.ToString("0000");
myList.Add(b);
Debug.Log (mylist);
break;
}
//myList is populated with all the numberOfModels here.
}
Don't create a new list inside the loop. The way you are doing it now, you are trying to create a new list (which you throw away anyway), that has progressively larger empty string arrays. For example with a numberOfModels of 100, you would have 100! empty string elements in the list (if you saved it).
Just create a list outside of the for loop, and add the string b to the list inside the loop.
I have a list of objects and I'm doing a foreach on the list and I'm sending each object to a method that modifies the object and returns it; like this:
foreach (MyObjectModel o in TheMasterObject.ListOfMyObjectModel)
{
o = MyMethod(o);
}
That doesn't work. What I want to do is replace the object I'm iterating on with the modified version that gets returned by MyMethod.
What do I need to change?
Thanks.
You cannot reassign to the loop variable, and reassigning the variable wouldn't affect the object inside the list anyway. If you need to write over the object in a list, use a for loop.
for (int index = 0; index < list.Count; index++)
{
var obj = list[index];
list[index] = MyMethod(obj);
}
You cannot do it like this. C# does not allow modifying the iteration variable of the foreach. Your best option is to hold a secondary list and put the modified values in there and then replace the initial list. Alternatively, if your data structure allows direct indexing, you can replace the foreach with a for and then you will be able to assign the object directly.
If MyObjectModel is a class (not a struct) then you don't need to reassign it. A class is a reference type which means your method will get a pointer to the object in the collection. Any modifications you do in the method will be done on the actual object in the list. You don't need to replace it.
If it's a completely new object you are returning, then do what Anthony Pegram is suggesting.
for (int i =0; i < TheMasterObject.ListOfMyObjectModel.size(); ++i) {
TheMasterObject.ListOfMyObjectModel.set(i, MyMethod(TheMasterObject.ListOfMyObjectModel.get(i)));
}
I wrote this with java thinking, if some changes need to let it work on C#, be my guest
Use a for-loop instead of an iterator.
for (int i = 0; i < objectModels.Count; i++) {
objectModels[i] = MyMethod(objectModels[i]);
}
As for the why, this questions explains it:
Why is the iteration variable readonly
Hope this helps
Y