Search Unity

Recursion with Structs

Discussion in 'Entity Component System' started by officialfonee, Nov 3, 2020.

  1. officialfonee

    officialfonee

    Joined:
    May 22, 2018
    Posts:
    44
    Hello eveyone, I am trying to create hierarchical tree inside a DOTS Job. My struct layout for the nodes of the tree look like this:

    Code (CSharp):
    1.     public unsafe struct Node
    2.     {
    3.         public float3 position;
    4.         public Node* left;
    5.         public Node* right;
    6.     }
    And in my struct tree:
    Code (CSharp):
    1. public unsafe struct Tree
    2. {
    3.     public Node* _root;
    4. }
    The thing that has really gotten me stuck is I was trying to create an array with my struts so I was trying to use NativeArray<Node*> (which you cant do) so then I was trying to use UnsafeUtility with a void* but it is really wonky. You cant use Node* as a type for UnsafeUtility.Malloc and its just a train wreck.
    Code (CSharp):
    1. //Node* array
    2. void* _open = UnsafeUtility.Malloc(Count * UnsafeUtility.SizeOf(typeof(IntPtr)), 4, Allocator.Persistent);
    I was trying to use this method but it seems things get dereferenced / are null e.g. float3 position is somehow not referenced when I use (Node*)UnsafeUtility.ReadArrayElement<IntPtr>(_open, index);

    If anyone has some more elegant ideas or If I am just doing this completely wrong any input would be nice!
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,264
    Make a struct called NodeWrapper which stores a Node* as a field with [NativeDisableUnsafePtrRestriction]. Then it should work in NativeArray fine.
     
    officialfonee likes this.
  3. officialfonee

    officialfonee

    Joined:
    May 22, 2018
    Posts:
    44
    Its that easy! Thanks you!
     
  4. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Use index(int) as reference instead of pointer, then it will work with NativeList. pointer will be lost when List reallocate on capacity change. index will work.
     
    CPlusSharp22 likes this.
  5. officialfonee

    officialfonee

    Joined:
    May 22, 2018
    Posts:
    44
    What exactly do you mean by using index(int) as reference instead of pointer.
     
  6. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Code (CSharp):
    1. public struct Tree
    2. {
    3.     public struct Node
    4.     {
    5.         public float3 position;
    6.         public int leftIndex;
    7.         public int rightIndex;
    8.     }
    9.  
    10.     public NativeList<Node> Nodes;
    11.  
    12.     public Node GetLeft(Node p) => Nodes[p.leftIndex];
    13.     public Node GetRight(Node p) => Nodes[p.rightIndex];
    14.     public Node Root => Nodes[0];
    15.     public Node this[int i]=>Nodes[i];
    16. }
     
  7. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Your tree has only 2 children for each Node. so it is a binary tree. So you don't need to store any reference. parent/child index can be calculated very easily.
    Code (CSharp):
    1.  
    2.         public struct BinaryTree<T> where T : unmanaged
    3.         {
    4.             public BinaryTree(T rootValue, Allocator allocator, int initialCapacity = 7)
    5.             {
    6.                 Nodes = new NativeList<Node>(initialCapacity, allocator);
    7.                 Nodes.Add(new Node() { Value = rootValue });
    8.             }
    9.  
    10.             public struct Node
    11.             {
    12.                 public T Value;
    13.             }
    14.  
    15.             public NativeList<Node> Nodes;
    16.  
    17.             public int RootIndex => 0;
    18.             public Node Root => Nodes[0];
    19.             public Node this[int i] => Nodes[i];
    20.  
    21.             public int GetLeftChildIndex(int nodeIndex) => (nodeIndex << 1) + 1;
    22.             public int GetRightChildIndex(int nodeIndex) => (nodeIndex << 1) + 2;
    23.             public int GetParentIndex(int nodeIndex) => (nodeIndex - 1) >> 1;
    24.  
    25.             public Node GetLeftChild(int nodeIndex) => Nodes[GetLeftChildIndex(nodeIndex)];
    26.             public Node GetRightChild(int nodeIndex) => Nodes[GetRightChildIndex(nodeIndex)];
    27.             public Node GetParent(int nodeIndex) => Nodes[GetParentIndex(nodeIndex)];
    28.  
    29.             public bool TryGetLeftChild(int nodeIndex, out Node c)
    30.             {
    31.                 var cId = GetLeftChildIndex(nodeIndex);
    32.                 if (math.asuint(cId) < Nodes.Length)
    33.                 {
    34.                     c = Nodes[cId];
    35.                     return true;
    36.                 }
    37.                 c = default;
    38.                 return false;
    39.             }
    40.  
    41.             public bool TryGetRightChild(int nodeIndex, out Node c)
    42.             {
    43.                 var cId = GetRightChildIndex(nodeIndex);
    44.                 if (math.asuint(cId) < Nodes.Length)
    45.                 {
    46.                     c = Nodes[cId];
    47.                     return true;
    48.                 }
    49.                 c = default;
    50.                 return false;
    51.             }
    52.  
    53.             public bool TryGetParent(int nodeIndex, out Node p)
    54.             {
    55.                 var pID = GetRightChildIndex(nodeIndex);
    56.                 if (math.asuint(pID) < Nodes.Length)
    57.                 {
    58.                     p = Nodes[pID];
    59.                     return true;
    60.                 }
    61.                 p = default;
    62.                 return false;
    63.             }
    64.  
    65.             public void PushLeftChild(int nodeIndex, Node n)
    66.             {
    67.                 var cId = GetLeftChildIndex(nodeIndex);
    68.                 Nodes.Resize(cId, NativeArrayOptions.ClearMemory);
    69.                 Nodes[cId] = n;
    70.             }
    71.  
    72.             public void PushRightChild(int nodeIndex, Node n)
    73.             {
    74.                 var cId = GetRightChildIndex(nodeIndex);
    75.                 Nodes.Resize(cId, NativeArrayOptions.ClearMemory);
    76.                 Nodes[cId] = n;
    77.             }
    78.         }
    There could be bug. I just made the tree from algorithm concept. You may want to test it before using.
    If the tree is not balanced (left/right branch of any node filled with equal child count).
    There will be gaps (Nodes filled with default value (all memory zeroed))

    this can be extended to any fixed branch count tree. but power of two branch count runs fastest. as index can be calculated by shift operator <</>>.
     
    Last edited: Nov 4, 2020
    Sarkahn and officialfonee like this.
  8. officialfonee

    officialfonee

    Joined:
    May 22, 2018
    Posts:
    44
    Wow thanks for the response, this was a lot of help! Could I put this inside a job even though it has a NativeList inside it?
     
  9. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Yes