TL;DR: I need a way to copy an array with integers, in which the copy will not change the original array, and that will work in Unity.
I am trying to implement the following algorithm: I have an array field with numbers that are editable in the inspector. These numbers indicate the number of enemies of each type to spawn:
public int[] EnemiesToSpawn;
And, for example, I set the array to 3 digits, the index of each in the array corresponds to the index of the object in the array with enemy prefabs.
Then, in the function, I randomly generate enemies, each time decreasing the corresponding number in the array by one (if the element integer is 0, another enemy is selected, and if the integer of all elements becomes 0, the spawn function ends execution).
Everything worked well until I needed to have multiple points on the level, each of which would generate enemies independently. What I did: I added another array, into which I copy the values from the first one every time I need to spawn enemies at a new point, and edited this particular second array instead of the original one (simply by assigning the value via "="). Of course it didn't work, I googled it and realized that arrays in C # are stored as references, not values, so the original array was edited as well.
I tried a lot of options that offer for correct copying (such as System.Array.Copy and others), but for some reason none of them worked in Unity.
The only thing I came up with myself was iterating over in a loop and assigning values separately:
EnemiesToSpawnCurrent = new int [EnemiesToSpawn.Length];
for (int i = 0; i <EnemiesToSpawn.Length; i ++)
{
EnemiesToSpawnCurrent [i] = i;
}
But, this code also does not work correctly for some reason, the numbers do not correspond to those entered in the inspector (most likely the indexes of the elements do not match).
I will be grateful if you tell me how you can optimally solve this problem.
Option A:
int[] a; // original array you want to deep copy
int[] b = new int[a.length]; // your copy
System.Array.Copy(a, b, a.length);
Option B:
int[] a; // original array you want to deep copy
int[] b = (int[]) a.Clone();
edit: in your code, the numbers won't match, because you are assigning them i.
EnemiesToSpawnCurrent = new int [EnemiesToSpawn.Length];
for (int i = 0; i <EnemiesToSpawn.Length; i ++)
{
EnemiesToSpawnCurrent [i] = i; // wrong!
}
this should work:
EnemiesToSpawnCurrent = new int[EnemiesToSpawn.Length];
for (int i = 0; i <EnemiesToSpawn.Length; i ++)
{
EnemiesToSpawnCurrent[i] = EnemiesToSpawn[i]; // copy each value from original to new Array.
}
Explanation: For int Array, this works because Integers are "Value type" and not reference Type. The whole array is Reference Type, so you need to create a new, independent instance. But assigning the values (ints) to the new array will work.
If you however want to copy something like Player[] you would need to create deep copies of the Player instances as well.
Related
There i try to specify bounds of an array:
public string[] scaleChanged()
{
int j =0;
for(int i=0;i<42;i++)
{
if(MinScale == ItemScale[i]) //MinScale is the min value I want to use to start array
{
ItemsScale[j] = MinScale; //ItemScal is an array of 42 string
for(int h =0; h<42-i; h++)
{
ItemScale[j+1] = ItemScale[i];
j++;
if(ItemScale[j] == MaxScale) //MaxScale is my max value I want to use for stop my array
{
return ItemScale[ ???MinScale to MaxScale];
}
}
}
}
}
So I recover 2 value from a server which allow me to specify bounds of my array.
So I try to define a new array with this two values as bounds.I precise this "two values" are always declared anywhere in my ItemScale array (that is why i use comparaison).
If really depends what you are trying to do here. The bounds of an array are fixed at creation, and in the case of a vector (string[] is a vector), it is always zero-based. If you want an actual array, then you'll need to copy out the sub-range into a second array - just new the array of the correct size, and Array.Copy the element range you want - i.e. Array.Copy(source, startIndex, destination, 0, count);. However, there are ways to represent a range without copying:
if the receiver just needs to iterate the data (rather than access it by index), an IEnumerable<T> - i.e. return source.Skip(firstIndex).Take(count);
Span<T> or ReadOnlySpan<T>, i.e. return new Span<string>(source, firstIndex, count) - a "span" works much like an array, and doesn't require any copying, and is allocation-free; the offset etc is applied appropriately; note that once you have a span, .Slice(...) creates smaller and smaller sub-sections inside that span, again without any copying or allocations
one nice thing here is that you can use the span approach as a simpler way of dealing with creating a new array if you want - once you have a [ReadOnly]Span<T>, you can use .ToArray() to create a new array with those contents
Memory<T> or ReadOnlyMemory<T> is effectively "I can give you a span when you want one" - because you can't store a "span" as a field (it is only legal on the stack), but you can store a "memory"
ArraySegment<T> is an older metaphor for expressing an array with offset and count; it relies on the caller doing everything correctly
Let's say I have the following code:
float[] test = new float[10];
for(int i = 0; i < 10; i++)
{
test[i] = i + 1.0f;
}
and I reassign a new float to test array like this:
test = new float[10];
After debugging through Console.WriteLine, it shows that the value of the reassign test is 0. Does that mean test is a reference to a newly created float array, or the previous array is being cleared and recreate again?
I have read some articles about heap and stack but that didn't resolve my confusion.
Deep down, test is a pointer to a chunk of memory of size sizeof(float) * 10) (the chunk might be a bit larger, but that is outside the point).
Within the loop, you start putting values within that chunk of memory. Then, when you do test = new float[10];, the CLR will give you a new pointer to a new chunk in memory.
The previous chunk in memory will be reclaimed by the garbage collector (unless it is being used some place else) at some point in the future.
Let me give you a more detailed example:
float[] test = new float[10];
float[] test2 = new float[10];
test = Enumerable.Range(10, 10).Select(x => (float)x).ToArray();// 10 - 20
test2 = Enumerable.Range(20, 10).Select(x => (float)x).ToArray();// 20 - 30
float[] testBak = test;
test = test2;
test[0] = 1;
Console.WriteLine(test[0]);// prints 1 as it was just modified
Console.WriteLine(test2[0]);// prints 1 because it has the same value of the reference as 'test'
Console.WriteLine(testBak[0]);// prints 10 which is the old value of test[0]
I have rewritten your code using two variables to hold both references of the arrays (which are value types). What you are doing when you are making an assignment is changing the value of the reference which that variable is holding to another value of another reference.
The memory locations which the two arrays hold are still different even after assignment, you are just keeping in your variable another address. This is why if you change the value of one of the entries in the first array, it will be visible in all the variables holding the same reference of that array, but not in the variables holding the reference of another one (as testBak holds an older reference).
I have an Array variable. I can use the Rank property to get the number of dimensions and I know that you can use a foreach to visit each element as if the array was flattened. However, I wish to modify elements and change element references. I cannot dynamically create the correct number of for loops and I cannot invalidate an enumerator.
EDIT
Thanks for the comments, sorry about the previous lack of clarity at the end of a long tiring day. The problem:
private void SetMultiDimensionalArray(Array array)
{
for (int dimension = 0; dimension < array.Rank; dimension++)
{
var len = array.GetLength(dimension);
for (int k = 0; k < len; k++)
{
//TODO: do something to get/set values
}
}
}
Array array = new string[4, 5, 6];
SetMultiDimensionalArray(array);
Array array = new string[2, 3];
SetMultiDimensionalArray(array);
I had another look before reading this page and it appears all I need to do is create a list of integer arrays and use the overloads of GetValue and SetValue -
Array.GetValue(params int[] indices)
Array.SetValue(object value, params int[] indices)
Everything seems clear now unless someone can suggest a superior method. svick has linked to this so I will accept this answer barring any further suggestions.
It's hard to tell what exactly do you need, because your question is quite unclear.
But if you have a multidimensional array (not jagged array) whose rank you know only at runtime, you can use GetValue() to get the value at specified indices (given as an array of ints) and SetValue() to set it.
hello i will much apreciate any help.
ok let's see, first i have declare a jagged array like this and the next code
int n=1, m=3,p=0;
int[][] jag_array =new[n];
now my jagged array will have 1 array inside, next y have to fill the array like this:
car=2;
do
{
jag_array[p]= new double[car];
for (int t = 0; t < carac; t++)
{
jag_array[p][t] = variableX;
}
p=p+1
}
while(p==0)
now my jagged array looks like this(also insert some data for this example):
jag_array[0][0]=4
jag_array[0][1]=2
now my question how can i insert a new array whit out losing my previos data if i declare
jag_array[p+1]= new double[car];
i will lose the data from the previos one, i will like to look something likes this:
jag_array[0][0]=4
jag_array[0][1]=2
jag_array[1][0]=5
jag_array[1][1]=6
the reason i did not declare from the begining 2 array is beacuse i dont know how many i am going to use it could be just 1 or 20 and every time i have to create a new array whit out losing the previous data that has been already fill, thaks all for the attention,
The size of an array, once created, is by definition invariable. If you need a variable number of elements, use a List<T> - in your case, probably a List<int[]>.
The only alternative solution would be to created a new array with the new size (and assign that to your jag_array variable) and copy all the previous elements from your old array into the new array. That is unnecessarily complicated code when you can just use List<T>, but if you cannot use List<T> for any reason, here is an example:
// increase the length of jag_array by one
var old_jag_array = jag_array; // store a reference to the smaller array
jag_array = new int[old_jag_array.Length + 1][]; // create the new, larger array
for (int i = 0; i < old_jag_array.Length; i++) {
jag_array[i] = old_jag_array[i]; // copy the existing elements into the new array
}
jag_array[jag_array.Length - 1] = ... // insert new value here
I have an array of ints. They start out with 0, then they get filled with some values. Then I want to set all the values back to 0 so that I can use it again, or else just delete the whole array so that I can redeclare it and start with an array of all 0s.
You can call Array.Clear:
int[] x = new int[10];
for (int i = 0; i < 10; i++)
{
x[i] = 5;
}
Array.Clear(x, 0, x.Length);
Alternatively, depending on the situation, you may find it clearer to just create a new array instead. In particular, you then don't need to worry about whether some other code still has a reference to the array and expects the old values to be there.
I can't recall ever calling Array.Clear in my own code - it's just not something I've needed.
(Of course, if you're about to replace all the values anyway, you can do that without clearing the array first.)