help understanding class with array of class

Hi , it s a quite noobish question i guess , but i’ve been at it for 3 days without success.
I am doing a classic generative grid mesh and some Perlin. I have troubles with a class .

i have a vetex class that is working perfectly:

public class  Vertex  {
    public Vector3 position;
    public Vector3 normal;
    public Vector2 uv;
    public Color rock;
    public float influenceTexture ;
    public float influenceTerrain ;
    public Zone zone;
    public int ID;

    public Vertex(){
        position = new Vector3();
        normal = new Vector3();
        uv = new Vector2();
        rock = Color.grey;
        influenceTexture =0;
        influenceTerrain =0;
        zone = Zone.empty;
        ID = 0;
    }
}
public enum Zone
{
    empty,sand,water,greens,moss,bamboo,rock,tree,plate,unique,house,other
}

i want to make 2 class one Grid class that hold (1 instance of the second class) Vertices that is equal to an array of (class)Vertex.
I want that array of vertex to be possible to call from the grid or the vertices classes whereever is ok.
My idea is to be able to get the array positions[ ] of the set of vertex by calling grid.vertices.positions[ ]
but also be able to get one position by doing grid.vertices.arrayofvertex[someint].position or grid.arrayofvertex[someint].position
(grid is an instance of the Grid class and so on)

i ve tried both approches :

public class GridA {
    public Vertices vertices{ get; set;}

    public Grid() {
        vertices = new Vertices ();//vertexArray);
    }
}

public class VerticesA{
    public Vertex[] array{ get; set;}
    public Vector3[] positions{ get; set;}
    public Vector3[] normals{ get; set;}
    public Vector2[] uvs{ get; set;}
    public Color[] rocks{get; set;}
    public float[] influencesTexture{get; set;}
    public float[] influencesTerrain{get; set;}
    public Zone[] zones{get; set;}
    public int[] ids{get; set;}
    public Vertices() {
        array = new Vertex[(Garden.data.resolutionX + 1) * (Garden.data.resolutionY + 1)];
        positions = new Vector3[array.Length];
        normals = new Vector3[array.Length];
        uvs = new Vector2[array.Length];
        rocks = new Color[array.Length];
        influencesTexture = new float[array.Length];
        influencesTerrain = new float[array.Length];
        zones = new Zone[array.Length];
        ids = new int[array.Length];
        for (int v = 0, z = 0; z <= Garden.data.resolutionY; z++) {
            for (int x = 0; x <= Garden.data.resolutionX; x++, v++) {
                array [v] = new Vertex ();
                positions [v] = array [v].position;
                normals [v] = array [v].normal;
                uvs [v] = array [v].uv;
                rocks [v] = array [v].rock;
                influencesTexture [v] = array [v].influenceTexture;
                influencesTerrain [v] = array [v].influenceTerrain;
                zones [v] = array [v].zone;
                ids [v] = array [v].ID;
            }
        }
    }
}

and

public class GridB {
    public Vertices vertices{ get; set;}
    public Vertex[] vertexArray{ get; set;}

    public Grid() {
        vertexArray = new Vertex[(Garden.data.resolutionX + 1) * (Garden.data.resolutionY + 1)];
        for (int v = 0, z = 0; z <= Garden.data.resolutionY; z++) {
            for (int x = 0; x <= Garden.data.resolutionX; x++, v++) {
                vertexArray [v] = new Vertex ();
            }
        }
        vertices = new Vertices(vertexArray);
    }
    public Grid(int gridResolutionX,int gridResolutionY) {
        vertexArray = new Vertex[(gridResolutionX + 1) * (gridResolutionY + 1)];
        for (int v = 0, z = 0; z <= Garden.data.resolutionY; z++) {
            for (int x = 0; x <= Garden.data.resolutionX; x++, v++) {
                vertexArray [v] = new Vertex ();
            }
        }
        vertices = new Vertices(vertexArray);
    }
}

public class Vertices{
    public Vector3[] positions{ get; set;}
    public Vector3[] normals{ get; set;}
    public Vector2[] uvs{ get; set;}
    public Color[] rocks{get; set;}
    public float[] influencesTexture{get; set;}
    public float[] influencesTerrain{get; set;}
    public Zone[] zones{get; set;}
    public int[] ids{get; set;}
    public Vertices(Vertex[] actualArray) {
        positions = new Vector3[actualArray.Length];
        normals = new Vector3[actualArray.Length];
        uvs = new Vector2[actualArray.Length];
        rocks = new Color[actualArray.Length];
        influencesTexture = new float[actualArray.Length];
        influencesTerrain = new float[actualArray.Length];
        zones = new Zone[actualArray.Length];
        ids = new int[actualArray.Length];
        for (int v = 0, z = 0; z <= Garden.data.resolutionY; z++) {
            for (int x = 0; x <= Garden.data.resolutionX; x++, v++) {
                positions [v] = actualArray [v].position;
                normals [v] = actualArray [v].normal;
                uvs [v] = actualArray [v].uv;
                rocks [v] = actualArray [v].rock;
                influencesTexture [v] = actualArray [v].influenceTexture;
                influencesTerrain [v] = actualArray [v].influenceTerrain;
                zones [v] = actualArray [v].zone;
                ids [v] = actualArray [v].ID;
            }
        }
    }
}

in GridA approach i would get 1 vertex by going grid.vertices.array[theIndexOfTheVertexINeed] but i also could grab all the vertices positions like so : grid.vertices.positons = anArrayOfVector3;

in GridB approach i do the same with another syntax grid.vertexArray[theIndexOfTheVertexINeed] for one vertex , and still grid.vertices.positons = anArrayOfVector3; for the positions

Either syntax is fin for me , but it doesn’t work because what i want to do in the Vertices class is not working .
When i iterate though the double for loops , it creates copy of the values in the array , not references . and i dont know how to do it differently .
What i dont want to do is to have to put everything in a update to make them constantly equal to each other .

when i write to arrays like positions[ ] in the vertices class , everything’s fine , but if i try to get one vertex.position somewhere else it is not equal to what i wrote inside the positions[ ]

i hope it is clear . My question could be asked as , how do i make references (pointer?) to the same variable
so that if i write grid.vertices.positions[10] = new Vector3(1f,2f,3f); and somewhere else i try to get the vertex position doing Debug.Log(grid.vertexArray[10].position); i get 1,2,3 and not 0,0,0

so i ve been mailing back and forth with friends and i am still stuck , also i ve spend some time trying to debug this on empty projects from scratch and i want to write down my conclusions so maybe people can confirm if i am right or wrong

i ll explain my way of thinking
first we can agree that this code :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestArrayReference : MonoBehaviour {
    int[] ints;

    // Use this for initialization
    void Start () {
        ints = new int[5];
        ints [2] = 123456789;
        int[] testeur = ints;

        testeur [2] = 111111;
        Debug.Log (ints [2]);// this prints 111111
    }
}

mean that if i dont initialize variables and directly make them equal to other variables , then one or the other is a reference to the other .

so technically references are possible.

then i start again with runtime-mesh-generation , i just draw a (horizontal)quad , simple.
the line vertices [1].position = new Vector3 (vertices [1].position.x, 0.2f ,vertices [1].position.z);
makes the top left corner (from the top) go up in the Y direction , I ll use this detail in the futur

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Main : MonoBehaviour {
    Vertex[] vertices;
    Mesh m;
    void Start () {
        vertices = new Vertex[4];

        vertices [0] = new Vertex();
        vertices [1] = new Vertex();
        vertices [2] = new Vertex();
        vertices [3] = new Vertex();

        vertices [0].position = new Vector3 (0, 0, 0);
        vertices [1].position = new Vector3 (0, 0, 1f);
        vertices [2].position = new Vector3 (1f, 0, 1f);
        vertices [3].position = new Vector3 (1f, 0, 0);

        int[] triangles = new int[6];
        triangles [0] = 0;
        triangles [1] = 1;
        triangles [2] = 2;

        triangles [3] = 0;
        triangles [4] = 2;
        triangles [5] = 3;

        vertices [1].position = new Vector3 (vertices [1].position.x, 0.2f ,vertices [1].position.z);

        m = new Mesh ();
        m.name = "MyMesh";
        GetComponent<MeshFilter>().mesh = m;
        m.vertices = vertices.Positions ();
        m.triangles = triangles;
        m.RecalculateNormals ();
    }
}
public static class ExtensionMethods{
    public static Vector3[] Positions(this Vertex[] vertexArray){
        Vector3[] positions = new Vector3[vertexArray.Length];
        for (int i = 0; i < vertexArray.Length; i++) {
            positions [i] = vertexArray [i].position;
        }
        return positions;
    }
}
public class Vertex  {
    public Vector3 position;
    public int moreInformationThatINeed;
}

from this , if i want to remove my extension method Positions() i have to make create a Vector3 array somewhere

public class Main : MonoBehaviour {
    Vertex[] vertices;
    Vector3[] positions;
    Mesh m;
    void Start () {
        vertices = new Vertex[4];
        positions = new Vector3[4];

        vertices [0] = new Vertex();
        vertices [1] = new Vertex();
        vertices [2] = new Vertex();
        vertices [3] = new Vertex();

        vertices [0].position = new Vector3 (0, 0, 0);
        vertices [1].position = new Vector3 (0, 0, 1f);
        vertices [2].position = new Vector3 (1f, 0, 1f);
        vertices [3].position = new Vector3 (1f, 0, 0);
        positions [0] = vertices [0].position;
        positions [1] = vertices [1].position;
        positions [2] = vertices [2].position;
        positions [3] = vertices [3].position;

        int[] triangles = new int[6];
        triangles [0] = 0;
        triangles [1] = 1;
        triangles [2] = 2;

        triangles [3] = 0;
        triangles [4] = 2;
        triangles [5] = 3;

        //vertices [1].position = new Vector3 (0,1f,1f); // this is doing nothing
        positions [1] = new Vector3 (0,1f,1f);//this is doing something

        m = new Mesh ();
        m.name = "MyMesh";
        GetComponent<MeshFilter>().mesh = m;
        m.vertices = positions;
        m.triangles = triangles;
        m.RecalculateNormals ();
    }
}

i can see that positions[ ] are not a reference to vertices[ ].position because if i change a vertices[index].position it doesnt change anything . so now i can clearly say that this is not good for what i want to do

i also tried this next code thinking that if i initialize my positions[ ] directly to vertices[ ].positions , then they would be references , but no

public class Main : MonoBehaviour {
    Vertex[] vertices;
    Vector3[] positions;
    Mesh m;

    void Start () {
        vertices = new Vertex[4];
        positions = new Vector3[4];

        vertices [0] = new Vertex();
        vertices [1] = new Vertex();
        vertices [2] = new Vertex();
        vertices [3] = new Vertex();

        positions [0] = vertices [0].position;
        positions [1] = vertices [1].position;
        positions [2] = vertices [2].position;
        positions [3] = vertices [3].position;

        positions [0] = new Vector3 (0, 0, 0);//if i comment these 4 lines i get all vertex at 0,0,0 i think cause i see nothing
        positions [1] = new Vector3 (0, 0, 1f);
        positions [2] = new Vector3 (1f, 0, 1f);
        positions [3] = new Vector3 (1f, 0, 0);

        int[] triangles = new int[6];
        triangles [0] = 0;
        triangles [1] = 1;
        triangles [2] = 2;

        triangles [3] = 0;
        triangles [4] = 2;
        triangles [5] = 3;

        positions [1] = new Vector3 (positions [1].x,1f,positions [1].z); // moves the vertex up
        vertices [1].position = new Vector3 (10f,11110f,2f);  //dont change anything

        m = new Mesh ();
        m.name = "MyMesh";
        GetComponent<MeshFilter>().mesh = m;
        m.vertices = positions;
        m.triangles = triangles;
        m.RecalculateNormals ();

    }
}

i dont think what i want to do is out of the ordinary , i guess there is a solution with something like struct instead of class for the Vertex class , or making some of my variables static in my class would solve the problem but the more i code the more i understand that i have no idea of what i m doing , so for once i ask for help instead of trying until i entirely give up the project (i cant actually give this one up).

what ever happens i need an array of Vector3 to feed the mesh , and as soon as i create one , my vertices[ ].positions become useless , as the memory is already used , it is impossible that i cant get it by reference ,
if i dont initialize my vector3 array ( positions = new Vector3[4]; ) i cant loop though my array to assign like so (positions [1] = vertices [1].position; )

i ve been stuck for so long that i am almost ready to continue working without Vertex class and use the fact that the index of the loop for looking up the vertices in a grid mesh will not change and hard code everything depending on that fact, but that s really not logical that there is not a more simple approach

I’ll be honest…long post, so I just tried to figure out what you were asking. And I think I got it.

So this

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
  
    public class TestArrayReference : MonoBehaviour {
        int[] ints;
  
        // Use this for initialization
        void Start () {
            ints = new int[5];
            ints [2] = 123456789;
            int[] testeur = ints;
  
            testeur [2] = 111111;
            Debug.Log (ints [2]);// this prints 111111
        }
    }

And this

public class Main : MonoBehaviour {
    Vertex[] vertices;
    Vector3[] positions;
    Mesh m;
    void Start () {
        vertices = new Vertex[4];
        positions = new Vector3[4];
        vertices [0] = new Vertex();
        vertices [1] = new Vertex();
        vertices [2] = new Vertex();
        vertices [3] = new Vertex();
        vertices [0].position = new Vector3 (0, 0, 0);
        vertices [1].position = new Vector3 (0, 0, 1f);
        vertices [2].position = new Vector3 (1f, 0, 1f);
        vertices [3].position = new Vector3 (1f, 0, 0);
        positions [0] = vertices [0].position;
        positions [1] = vertices [1].position;
        positions [2] = vertices [2].position;
        positions [3] = vertices [3].position;
    }
}

Are doing two different things. In your first example, you are creating two arrays of ints. You are setting testeur == ints. Which means they both point to the same reference. So changing one changes the other.

In your second example. You are creating two different arrays. Then you assign a value to each index in vertices position. Note that a Vector is a strut which is a value type and not a class which is a reference type.

You then assign the value from the vertices array to an index in the position array. You aren’t creating a reference. You are essentially just doing

Vector3 pos = vertice.position;

You now have two variables that have their own copy of the value. They don’t point to the same place. Changing one doesn’t change the other.

No different then if you assigned the values in the int arrays to each other vs copying the array. There are actually terms for it (deep copy, shallow copy, etc).

If I understand what you want, you want positions and vertices to point to the same reference, is that correct?

maybe things get more clear about why i try to do this , when you think that i want to refresh my mesh in the update , and it has many vertices , and every vertex has a lot of data , it get logical to not have to :

  • create 1 array per vertexdatatype
  • loop thought all the vertices to fill all the arrays

all of this every frame

@ Brathnann

  • yes exactly

actually not ,
i want an array of Vector3 that is all the vertices positions, to feed the mesh (not a copy of their values).
So if i later edit a Vector3 in this array , i ll editing the vertex’s position related to this Vector3 .

to take your question :
If I understand what you want, you want positions and vertices’s position to point to the same reference, is that correct?

If you’re trying to keep a reference to the mesh’s vertex array, you’ll be out of luck.

In order to update a mesh, you always have to explicity assign the vertex array using the mesh’s property. This property creates a new array and copies all values, it’s the same when you use the property to get the vertex array. It’s just another copied array, no reference to the internally managed array of vertices.

Yes, vertices’s position is what I meant :).

@Suddoha seems to have more experience with meshes. I haven’t really messed with them much myself.

@Suddoha
yes i noticed that i cant edit one vertex of the mesh , i would do nothing .
but the problem here is different . i create a class named Vertex , cause i need to “encode” different kind of values at each vertex (one of these value is a Vector3 named position) . the problem is creating an array of Vector3 so i can feed it to the mesh ,
if i create an array of Vector3 (named positions with an ‘s’ ) it will always be a copy of all my vertex instances’s position values , but i want it to be a reference , so if i modify one position in my vertex array or one of the positions of my positions array , it will do the same thing
then i feed the positions array to the mesh to draw the new frame

Well, you can’t. Vector3 is a struct, which means it’s a value type rather than a reference type.

So if I understand you correctly, you have an array of Vertex objects, which contain (among other things) a position for each vertex. And you have an array of Vector3, which you assign to the mesh. You wish that the Vector3’s in this array could be references to the ones in your Vertex objects, so that changing one changed the other.

But, you can’t. As value types, a Vector3 is just like an int: when you do “a = b”, you’re merely copying the value from b into a; you are not in any way conjoining a and b so that they always have the same value.

So, yeah, you have to zip through your Vertexes and copy all the values into a Vector3 array whenever you update the mesh.

The more efficient way to do this would be to flip your data structures around, so that instead of an array of Vertex objects, you keep just an array of Vector3 positions, plus parallel arrays of whatever else you need (color, etc.). Hide the ugliness behind a clean class interface, so the rest of your code merely says “set the color of vertex 5 to red” and doesn’t worry about how/where this is stored. And then you can stuff these arrays directly into the Mesh very efficiently on each frame.

Cheers,

  • Joe
    hiding ugliness since 1983

@JoeStrout

what about this then

public class TestArrayReference : MonoBehaviour {
    int[] ints;
    // Use this for initialization
    void Start () {
        ints = new int[5];
        ints [2] = 123456789;
        int[] testeur = ints;
        testeur [2] = 111111;
        Debug.Log (ints [2]);// this prints 111111
    }
}

I already explained that. Arrays are reference types. ints are value types. In this example, you are creating an array and then having another array point to the same reference as the first array.

Your Vector3 example, you don’t do that. You are creating one array of Vertex and another array of Vector3. But then you assign the values from your vertex.position to an index in the Vector3 array. In this case, you’re copying a value, not pointing two objects to the same reference.

If you can copy an array of Vector3, and both arrays point to the same thing, then changing one will change the other, which will behave as your int array example does.

@Brathnann ok , i kinda understand .
but how can i do exactly that if i have to iterate through an array containing the vector ?
that is actually my problem .

i have one array of vectors that i can reference , but on the other hand the other vectors are individual vectors (on an array of the class holding them)
Is there a way to do it in this configuration ?

1 array of vector = reference = the vectors of an array of instances of a class containing vector (one vector per instance)

No, there isn’t any way to do that.

yes ok , i tried everything possible and i understand .
i can reference arrays one to another , but if i start iterating inside arrays i cant reference anything because however you write it , you cannot reference value type. (is it only frustrating to me ?)

sooo… @JoeStrout could you tell me more about hiding ugliness ?

if i had a class Vertex with data per vertex , then a class Vertices that hold an array of Vertex and arrays to reflect the data of the Vertex set ;
i should kill the Vertex class and keep only the Vertices class and work with “parallel arrays” trying to reflect the behavior of the deceased Vertex class?
that mean i can not pass 1Vertex as an argument in a methods anymore ?

or i could i “dynamically” create a temporaryVertex packed with all the data i grab from the dataArrays (that mean i need to keep the Vertex class as some sort of model or template ?).

what are people doing usually in this kind of situation ?

Okay then, I thought that was the idea behind everything.

Yes, you can use such a helper type.
When you access the vertices class (e.g. per getter or indexer) you’ll create a new instance of that type, initialize it with values from the internally managed arrays and return that instance to the outside world.
It works the other way 'round as well, you’d get such an instance of the helper class, set all the values and pass it in via any kind of setter (method or indexer), read all the values and write them to the internal arrays.

It’s also possible to write through these instances to the internal arrays directly, but this is evil as it can easily produce bugs.

@Suddoha

you re right , at some point i thought about it and tried it , that helped me understand a bit more generally my problem and how things were made (if unity is not using reference to its vertex array so you can move vertex on the fly , there is maybe a reason i cant do it either).

as for the rest , it is too complex and i have no idea how this would be done , i ll wait next time i visit my friend who can help me and i ll ask him how such things would be done , as for now , i can still manage the data i am using per vertex and know what arrays to use to still be able to do what i want , so i ll continu working that way .

thank you all for your help

i want to show you the idea behind all this

the thing is as everything is generated every frame , so the water or the “hills” move in real time , and influence the rest.
so i start getting horrible framerate , that s why i was asking all these questions about not having to duplicate arrays lefts and rights but i am sure i can optimize in many other places , i ll ask friends when they are available .

thanks again everybody

Well, there’s lot’s of processing to do when you set the vertices, if you kept manipulating it from the outside, this might just mess things up and you’d also needed to tell the engine about changes in the array, because it wouldn’t recognize that automatically unless it checked the arrays frequently - which is non-sense and a huge waste of performance.

The duplicated arrays themselves are not the problem. You’ve got enough memory I hope.

The actual problem are all the copying operations, which happen to be especially slow on your side (due to the loops copying one vector after another). I know that your current type architecture did not allow anything else, but if you ever need to copy arrays quickly, use efficient tools from the .Net framework, see Copy-Methods in System.Array and System.Buffer.BlockCopy. The latter is a little more flexible but it’s also easier to mess things up, I’d personaly only recommend it for arrays of primitive types though (it’s also useful for a few other things in regards to massive array-based bit-conversions among primitive types).

However, in this case, if you want to have moving water, you’d be better off using a shader which does all the vertex-transformations on the GPU. It’ll be much faster as the GPU can process this in parallel (using thousands of ‘threads’).

Here’s a basic example, I used a (non-encapsulated) class for the vertex which could cause quite a lot of garbage if you get the information in a tight loop.

You could also implement the helper type as a struct, but then I’d suggest passing them the information in and out with a setter using the ‘ref’ keyword and a getter using the ‘out’ keyword to avoid expensive copy operations.

public class Vertices
{
    private Vector3[] _positions;
    private Vector3[] _normals;
    // ...
 
    public Vertices(int num)
    {
        _positions  = new Vector3[num];
        _normals    = new Vector3[num];
        // ...
    }
    // other overloads to create a new instance from
    // and existing instance etc...
 
    // only getters, otherwise arrays could be messed up by having different lengths
    public Vector3[] Positions  { get { return _positions; } }
    public Vector3[] Normals    { get { return _normals; } }
 
    //TODO: implement common properties for an array or a list
    // e.g. Length / Count
 
    public VertexData this[int index]
    {
        get
        {
            var vertex = new VertexData();
            vertex.Position = _positions[index];
            vertex.Normal = _normals[index];
            return vertex;
        }
        set
        {
            if (ReferenceEquals(value, null)) { throw new ArgumentNullException(); }

            _positions[index] = value.Position;
            _normals[index] = value.Normal;
        }
    }
}

public class VertexData
{
    public Vector3 Position;
    public Vector3 Normal;
    // ...
}

Note that this is not necessarily the best solution, rather the basic idea behind it.

I don’t see this approach as much better. Indeed, it might be worse, because it tempts the caller into getting and setting these VertexData objects more often (depending on what calculations you’re doing), which means more copies than the old approach.

The right approach, efficiency-wise, is to have your class simply provide accessors for the data directly, e.g.:

public class Vertices
{
    private Vector3[] _positions;
    private Vector3[] _normals;

    public Vector3 GetPosition(int i) {
        return _positions[i];
    }
   
    public Vector3 SetPosition(int i, Vector3 value) {
        _positions[i] = value;
    }
   
    public Vector3 GetNormal(int i) {
        return _normals[i];
    }
   
    public Vector3 SetNormal(int i, Vector3 value) {
        _normals[i] = value;
    }
   
    public void UpdateMesh(Mesh m) {
        m.vertices = _positions;
        m.normals = _normals;
    }
}
2 Likes

Yes, of course if you only need to set/get one value, this is more efficient. However, if you want to set / get all values for a specific entry, you’d end up storing them somehow in a helper type anyway in order to pass them around efficiently.

I’d just mix both then.

1 Like