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.
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
For Level 4
For Level 5
As the last time I made a pull request for this ;)