Home > Blockchain >  Showcasing a binary tree in unity problom
Showcasing a binary tree in unity problom

Time:03-02

i made a function in unity that takes a Binode - and making it into a2d tree, but here the problem: my so called idea to display the tree is this:

make a recursive function, and every time the level will go up by 1, and it will also send the function a bool(is the right or is the left of the old node) and then if its true it will get the root of the tree and - the y by level * 55, then it will either x = level * 55, if it right, and if its left the x = level * -55.

now the problome is lets say i have a tree that has 3 levels, then if i will put a right node to the left tree it will just put it were the most right node is and it will overlap, kinda hard to explain in text but i hope you understand.

heres the repository: Image

code:

public void GenrateTreeUI(BinNode<int> t, bool right, int Level)
{

    if (Level == 0)
    {
        cir = Instantiate(CirclePrefab, new Vector2(0, 0), Quaternion.identity);
        t.SetGO(cir);
        cir.transform.SetParent(canvas.transform);
        cir.GetComponent<Image>().color = Color.black;
        roottr = cir.GetComponent<RectTransform>();
        Value = cir.GetComponentInChildren<TMP_Text>();


        roottr.anchoredPosition = new Vector2(-11, 213);
        Value.text = t.GetValue().ToString();
        Value.color = Color.white;
    }
    else
    {
        if (!right)
        {
            cir = Instantiate(CirclePrefab, new Vector2(0, 0), Quaternion.identity);
            cir.transform.SetParent(canvas.transform);
            t.SetGO(cir);
            cir.GetComponentInChildren<TMP_Text>().text = t.GetValue().ToString();
            cir.GetComponent<RectTransform>().anchoredPosition = new Vector2(roottr.anchoredPosition.x - Level * 55,
                roottr.anchoredPosition.y - (Level * 55));
        }
        else
        {
            cir = Instantiate(CirclePrefab, new Vector2(0, 0), Quaternion.identity);
            cir.transform.SetParent(canvas.transform);
            t.SetGO(cir);
            cir.GetComponentInChildren<TMP_Text>().text = t.GetValue().ToString();
            cir.GetComponent<RectTransform>().anchoredPosition = new Vector2(roottr.anchoredPosition.x   Level * 55,
                roottr.anchoredPosition.y - Level * 55);
        }
    }


    if (t.HasLeft())
    {

        GenrateTreeUI(t.GetLeft(), false, Level   1);
        GenrateWire(cir.GetComponent<RectTransform>().anchoredPosition, GetFutreNode(t.GetLeft(), Level, false));

    }
    if (t.HasRight())
    {

        GenrateTreeUI(t.GetRight(), true, Level   1);
        GenrateWire(cir.GetComponent<RectTransform>().anchoredPosition, GetFutreNode(t.GetRight(), Level, true));

    }

}

thanks for the help!

CodePudding user response:

Before giving an answer here I would once again first suggest some refactors and fixes:

First of all your CreateRndTree is wrong ;)

public void CrateRndTree(int levels, BinNode<int> save_node)
{
    BinNode<int> newNode = null;
    for (int i = 0; i < levels; i  )
    {
        newNode = new BinNode<int>(Random.Range(1, 20));
        save_node.SetLeft(newNode);
        newNode = new BinNode<int>(Random.Range(1, 20));
        save_node.SetRight(newNode);
        save_node = newNode;
    }
}

you create two new child nodes but only set left and right on the last created one (right) => The left tree would never have any sub children.

I would do that recursive like e.g.

private static void CreateRndTree(int levels, BinNode<int> rootNode)
{
    if (levels <= 0)
    {
        return;
    }

    var leftNode = new BinNode<int>(Random.Range(1, 20), rootNode);
    var rightNode = new BinNode<int>(Random.Range(1, 20), rootNode);

    rootNode.SetChildren(leftNode, rightNode);

    CreateRndTree(levels - 1, leftNode);
    CreateRndTree(levels - 1, rightNode);
}

Then as the last time I would have a proper class Circle on the circle prefab like e.g.

public class Circle : MonoBehaviour
{
    [SerializeField] private Image image;
    [SerializeField] private RectTransform rectTransform;
    [SerializeField] private TMP_Text text;

    public Image Image
    {
        get
        {
            if (image) return image;

            image = GetComponent<Image>();

            return image;
        }
    }

    public RectTransform RectTransform
    {
        get
        {
            if (rectTransform) return rectTransform;

            rectTransform = GetComponent<RectTransform>();

            return rectTransform;
        }
    }

    public TMP_Text Text
    {
        get
        {
            if (text) return text;

            text = GetComponentInChildren<TMP_Text>();

            return text;
        }
    }
}

and in your BinNode store that instead of GameObject in order to get rid of repeated GetComponent calls.


And then for the positioning I would rather implement a getter into the nodes themselves for getting their child levels count.

I would also store the parent node and rather position the new node relative to the parent node instead of always having to calculate from the root

public class BinNode<T>
{
    ...

    private BinNode<T> parent;
    public BinNode<T> Parent => parent;

    // proper class that will be sitting on your circle prefab
    public Circle circle;
    
    public BinNode(T value, BinNode<T> parent = null)
    {
        this.value = value;
        this.parent = parent;
    }

    public int GetChildLevels()
    {
        var rightChildLevels = right == null ? 0 : 1   right.GetChildLevels();
        var leftChildLevels = left == null ? 0 : 1   left.GetChildLevels();

        return Mathf.Max(rightChildLevels, leftChildLevels);
    }
    
    ...
}

And then when getting a nodes position do

public float spacing = 55;

public Vector2 GetNodePosition(BinNode<int> node, bool right)
{
    var subLevels = node.GetChildLevels();

    // simply get the position relative to the parent node
    // otherwise you would need to sum them up all the time
    var parentRectTransform = node.Parent.Cirlce.RectTransform;

    return parentRectTransform.anchoredPosition   new Vector2(spacing * (Mathf.Pow(2, subLevels)) * (right ? 1 : -1), -spacing);
}

For Level 3

enter image description here

For Level 4

enter image description here

For Level 5

enter image description here


As the last time I made a pull request for this ;)

  • Related