Unity C# allows for embedded arrays? [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 4 years ago.
I was learning how to use Json to save data into files when a continuous error was appearing, its "Nullreference, object reference not set to an instance of an object" and I started to think that I was doing the save file part right but the error was in my usage of arrays somehow, so I started a new project and I started using arrays the same way without any usage of Json, and indeed the error was there, so I know there is something wrong with this but I cant really tell what, I know how simple arrays are initialized and how every array has to be initialized before being used, but in this case something is wrong for some reason, can you tell me what is that?
The scenario is really simple as its new project only to test the arrays the way I was using them in my bigger project, so its mainly a GameClass that hold several hi-scores tables (an array of them), these tables are made by a class (HiScoreClass) that holds arrays of names and points, and then well I was just initializing this and the error appeared, so no need to dig further, can you tell me what is wrong here? thanks a lot for your help, Im totally lost in this point. (To test this in a project I just attached the code to the camera so its nothing else in scene to think other object could be causing it)
using UnityEngine;
using System.Collections;
public class embed : MonoBehaviour
{
GameClass myGame;
int i, j;
void Awake ()
{
myGame = new GameClass();
myGame.arrptnm = new HiScoreClass[10];
for (i = 0; i < 100; i++)
{
myGame.arrptnm[i].pts = new int[10];
myGame.arrptnm[i].names = new string[10];
for (j = 0; j < 10; j++)
{
myGame.arrptnm[i].pts[j] = i * j;
myGame.arrptnm[i].names[j] = "ASD";
}
}
}
void Update ()
{
}
}
[System.Serializable]
class GameClass
{
[SerializeField]
public HiScoreClass[] arrptnm;
}
[System.Serializable]
class HiScoreClass
{
[SerializeField]
public int[] pts;
public string[] names;
}

The problem is that you're defining an array of HiScoreClass objects, but you're not initializing the elements in that array to new instances of the HiScoreClass class. Then you get a NullReferenceException when you try to reference a property of an item in the array, like myGame.arrptnm[i].pts.
To solve this, you can initialize each item to a new instance when you do your first iteration (also change the 100 to 10, since that's the size we declared for this array):
for (int i = 0; i < 10; i++)
{
// Initialize our array items
myGame.arrptnm[i] = new HiScoreClass();
// rest of code omitted...

Related

How do I remove (or nullify) an object in an array list of generics in C#? [duplicate]

This question already has answers here:
How can I return NULL from a generic method in C#?
(13 answers)
Closed 2 years ago.
In my Data Structures class I've been tasked with creating an ArrayList of generic objects of a given type. This is essentially a class that behaves like an array with some extra functionality attached. It looks something like this.
public class GenericArray<T>
{
// The array
private T[] array;
private int size;
// Constructor
public GenericArray(int arrsize)
{
size = arrsize;
array = new T[size + 1];
}
// Array methods
public T getItem(int index)
{
return array[index];
}
public int getCount()
{
int count = 0;
for(int i = 0; i < array.Length; ++i)
{
if(array[i] != null)
count++;
}
size = count;
return count;
}
}
We're required to create the various methods by which you interact with the array. I'm having a particular problem when it comes to deleting items from the array. I have a deleteLast method which I assumed would simply set the last item to null, like so:
public void deleteLast()
{
int i = this.getCount()-1;
array[i] = null;
}
However I am getting the error that I cannot convert the parameter to null, as T may not be a nullable value type, which makes sense. However, I don't know how to remove them otherwise. I can't find any useful information on this through Google, so I've come to Stack Overflow.
Any help is appreciated. Thanks :)
Why don't you use List<T> instead of T[] ? That should easily solve all your problems!
You haven't handled cases around array size changes (for growing and reducing array size), enumeration, etc. Ideally I would implement IEnumerable and implement rest of the functions!

Deep copy a class with arrays [duplicate]

This question already has answers here:
C# Copy Array by Value
(8 answers)
Copy one 2D array to another 2D array
(4 answers)
Closed 2 years ago.
This is an addendum to a previous question I asked about copying classes.
The basic answer to the previous question (copying classes not as reference type) was to use a memberwise clone method to avoid keeping links between the two classes.
Doing this on a class with only int values works, but this breaks apart as soon as I introduce arrays in the mix. See the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyClass
{
public int[] myNumber;
public MyClass ShallowCopy()
{
return (MyClass)this.MemberwiseClone();
}
}
public class Test : MonoBehaviour
{
Dictionary<string, MyClass> myDictionary1 = new Dictionary<string, MyClass>();
Dictionary<string, MyClass> myDictionary2 = new Dictionary<string, MyClass>();
void Start()
{
myDictionary1.Add("a", new MyClass() { myNumber = new int[] { 1, 1 } });
myDictionary1.Add("b", new MyClass() { myNumber = new int[] { 2, 2 } });
myDictionary2["b"] = myDictionary1["a"].ShallowCopy();
myDictionary2["b"].myNumber[0] = 3;
Debug.Log(myDictionary1["a"].myNumber[0]); //output 3, I'd want it to still be 1
}
}
I've tried implementing the ShallowCopy method (implemented in line 9 and used in line 25) and it doesn't work. I should implement a DeepCopy method, but I don't know how to formulate it applied to arrays.
My new DeepCopy function would be something like
public MyClass DeepCopy()
{
MyClass other = (MyClass)this.MemberwiseClone();
other.myNumber = ?????????????????????? //int[] deep copy
return other;
}
But I have no idea how to formulate a copy of the array. I've found a similar thread dealing with this, but I couldn't adapt the solution to my case.
Thanks in advance!
If you know that your array is going to be full of value types like int, or you have reference types and you want both arrays to refer to the same instance, you can use CopyTo
int[] copyTarget = new int[copyFrom.length];
copyFrom.CopyTo(copyTarget, 0);
If your array can/does have reference types in it, and you don't want the new array to reference the same instances, you will have to instead move through the elements doing a memberwise clone of each:
int[] copyTarget = new int[copyFrom.length];
for(int i = 0; i < copyTarget.Length; i++)
{
copyTarget[i] = copyFrom[i].MemberwiseClone();
}

How to replace element in MeshRenderer.sharedMaterials

We are using a object from the asset store that makes usage's of a mesh renderer with 32 materials in it. We need to implement functionality that makes it possible to replace 1 single material in the list, and keep the rest.
I have tried several things, but I keep struggeling with the materials list, that is why I decided to ask you guys for help.
public Material TestMaterial;
void Update()
{
for (int i = 0; i < meshRenderer.sharedMaterials.Length; i++)
{
if (meshRenderer.sharedMaterials[i].name == "replaceableMat")
{
// Replace with TestMaterial
}
}
}
The above code is how I kinda want to use it.
The TestMaterial object is not null, it is selected from the Unity Editor, so that is fine.
Could someone give me some insight?
Thanks in forward!
from Renderer.sharedMaterials:
Note that like all arrays returned by Unity, this returns a copy of materials array. If you want to change some materials in it, get the value, change an entry and set materials back.
You have to do exactly that. Store the materials in a temporary variable, change entries and write the entire array back to sharedMaterials when done:
var materials = meshRenderer.sharedMaterials;
for(int i = 0; i < materials.Length; i++)
{
if(!string.Equals(materials[i].name, "replaceableMat") continue;
materials[i] = TestMaterial;
}
meshRenderer.sharedMaterials = materials;
The reason in the background is that sharedMaterials is not a field but a property.
Since the assignment of sharedMaterial[i] is not an assignment to the whole property what it does is just replacing that entry in a temporary array but not actually assigning it back to the Renderer component.
Only by assigning a value to the entire property actually makes the property execute it's setter and writes the array back to the Renderer component.
This should work.
void Update()
{
for (int i = 0; i < meshRenderer.sharedMaterials.Length; i++)
{
if (meshRenderer.sharedMaterials[i].name == "replaceableMat")
{
// Replace with TestMaterial
renderer.sharedMaterials[i] = TestMaterial
}
}
}

Halt until object created successfully [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm a beginner programmer and need some help. In the beginning of my program, 5 bunnies (object type Bunny) are created. It looks like this:
List<Bunny> bunnies = new List<Bunny>();
Bunny newBunny;
for(int i = 0; i < 5; i++)
{
newBunny = new Bunny();
bunnies.Add(newBunny);
}
My problem is that the construction of newBunny takes too long and the program continues with the for-loop. This causes the list to have just the first constructed newBunny and duplicates of it.Running the code prints me the following:
Name-Kate, Gender-Male, Color-White, Age-0, Radioactive-False
Name-Kate, Gender-Male, Color-White, Age-0, Radioactive-False
Name-Kate, Gender-Male, Color-White, Age-0, Radioactive-False
Name-Kate, Gender-Male, Color-White, Age-0, Radioactive-False
Name-Kate, Gender-Male, Color-White, Age-0, Radioactive-False
The construction looks like this:
public Bunny()
{
Random randNum = new Random ();
int namesCount = Enum.GetNames(typeof(BunnyName)).Length;
Name=((BunnyName)(randNum. Next(0, namesCount)));
int genderCount = Enum.GetNames(typeof(BunnyGender)).Length;
Gender=((BunnyGender)(randNum. Next(0, genderCount)));
int colorCount = Enum.GetNames(typeof(BunnyColor)).Length;
Color=((BunnyColor)(randNum. Next(0, colorCount)));
Age=-1;
if(randNum. Next(1,101)<2)
Radioactive = true;
else
Radioactive =false;
}
I want the program to halt until the construction of newBunny ends, and only then to add it to the list:
List<Bunny> bunnies = new List<Bunny>();
Bunny newBunny;
for(int i = 0; i < 5; i++)
{
//stop until next line finishes
newBunny = new Bunny();
//continue
bunnies.Add(newBunny);
}
Ps. If I'm debugging the program it runs just fine, exactly like i want it to (new bunnies not 'duplicates')
Also, if I add a messageBox in the end of the for-loop, everything works good.
This is why i thought it might need a halt
for(int i = 0; i < 5; i++)
{
//stop until next line finishes
newBunny = new Bunny();
//continue
bunnies.Add(newBunny);
MessageBox.Show("test");
}
Hope you understood my problem, thanks.
Random in a loop? They may be different objects but all your random numbers are the same.
This is because the constructor for Random, when given no parameters uses a seed from the system clock. So if you new them up quickly like this they all end up the same and will give the same value when you call Next.
Much better to pass one Random into all of the constructors and use that instead.
List<Bunny> bunnies = new List<Bunny>();
Random random = new Random();
for(int i = 0; i < 5; i++)
{
bunnies.Add(new Bunny(random));
}
You also don't need a variable to hold the bunnies at an unnecessarily high scope. If your using something in the for loop only, it doesn't need to exist outside of it.
Having a static Random in the Bunny class has been suggested, but i'd advise against it. There are a lot of benefits of injecting things into the instance (especially when mult-threading), but in your case the advantage is testability.
I've included a class below as an example of something you could use to test your Bunny constructor, you can control the bunny that gets made and then verify that what happened is correct:
class MyRandomIsAlwaysN : Random
{
private readonly int nextValue;
public MyRandomIsAlwaysN(int n){
this.nextValue = n;
}
public override int Next(int x, int y){
return this.nextValue
}
}
The problem you have is in your usage of Random.
See here: C# Random Numbers aren't being "random"
I would suggest you create one Random class outside of bunny, and then pass that into the constructor.
i.e
List<Bunny> bunnies = new List<Bunny>();
Bunny newBunny;
Random randomGenerator = new Random();
for(int i = 0; i < 5; i++)
{
newBunny = new Bunny(randomGenerator);
bunnies.Add(newBunny);
}
Your program execution is totally synchronous so in fact there are 5 different instances of Bunny in the list.
If you don't believe me change the constructor of Bunny and add an integer to it. In the constructor assign this integer to an instance variable of Bunny like so:
public class Bunny{
private int _instanceId;
public Bunny(int instanceId){
_instanceId = instanceId;
}
}
in the loop do this :
newBunny = new Bunny(i);
now use the debugger to step through the code. Hopefully visual studio. Put a break point on the add statement and navigate/hover the cursor over the bunnies variable to see what is inside. You can even inspect the different instances and you will see the instanceid is different.
Your code just creates 5 different instances of Bunny, but because the constructor is the same, you end up with 5 instances that look exactly the same.
To get really random values do this in your constructor :
static Random randNum = new Random ();

Array through parameter by reference affecting another array

CardDetails is a Structure.
public static void ParceIntricaciesJabber(ref CardDetails[] WhichArray)
{
WhichArray[0].ID = 50;
WhichArray[0].Type = "None";
}
In calling:
ParceIntricaciesJabber(ref OpponentCards);
After I call the function though, another Array called PlayerCards is affected in the exact same way as OpponentCards - despite being declared as two different arrays. They have the same number of elements and the same data Type, and that's it.
This probably should be obvious but i'm not seeing it. The code works in VB.NET. What am I doing wrong?
EDIT: Initialization Code:
public static class Module1{
public static CardDetails[] PlayerCards = new CardDetails[100];
public static CardDetails[] OpponentCards = new CardDetails[100];
}
And also when navigating to the Form
for (int n = 1; n <= 100; n++)
{
Module1.PlayerCards[n] = new CardDetails();
Module1.OpponentCards[n] = new CardDetails();
}
My guess is that you are sharing the reference to the arrays. Even though it is structs inside the array, the array itself is a reference type. You will need to post your array instantiation code to verify one way or the other though

Categories