在统一问题中展示二叉树
Showcasing a binary tree in unity problom
我在统一中做了一个函数,它接受一个 Binode - 并将它变成一个 2d 树,但这里的问题是:
我所谓的显示树的想法是这样的:
做一个递归函数,每次level加1,同时给函数传一个bool(旧节点是右还是左),如果为真就通过 level * 55 获取树的根和 - y,那么它要么是 x = level * 55,如果它是对的,如果它是左的 x = level * -55.
现在的问题是假设我有一棵有 3 层的树,然后如果我将一个右节点放在左树上,它只会把它放在最右的节点上并且它会重叠,有点难文字说明,希望大家理解。
这里是存储库:
git
代码:
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));
}
}
感谢您的帮助!
在这里给出答案之前,我会再次建议一些重构和修复:
首先你的 CreateRndTree
是错误的 ;)
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;
}
}
你创建了两个新的子节点,但只在最后一个创建的节点(右)上设置了 left 和 right => 左边的树永远不会有任何子节点。
我会像
这样递归地做
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);
}
然后作为最后一次,我将在圆形预制件上有一个适当的 class Circle
,例如
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;
}
}
}
并在您的 BinNode
中存储它而不是 GameObject
以摆脱重复的 GetComponent
调用。
然后对于定位,我宁愿在节点本身中实现一个 getter 来计算它们的子级别。
我还会存储父节点,而是将新节点相对于父节点定位,而不是总是必须从根节点开始计算
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);
}
...
}
然后在获取节点位置时执行
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);
}
3 级
4 级
5 级
作为上次我为此做了一个pull request ;)
我在统一中做了一个函数,它接受一个 Binode - 并将它变成一个 2d 树,但这里的问题是: 我所谓的显示树的想法是这样的:
做一个递归函数,每次level加1,同时给函数传一个bool(旧节点是右还是左),如果为真就通过 level * 55 获取树的根和 - y,那么它要么是 x = level * 55,如果它是对的,如果它是左的 x = level * -55.
现在的问题是假设我有一棵有 3 层的树,然后如果我将一个右节点放在左树上,它只会把它放在最右的节点上并且它会重叠,有点难文字说明,希望大家理解。
这里是存储库: git
代码:
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));
}
}
感谢您的帮助!
在这里给出答案之前,我会再次建议一些重构和修复:
首先你的 CreateRndTree
是错误的 ;)
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;
}
}
你创建了两个新的子节点,但只在最后一个创建的节点(右)上设置了 left 和 right => 左边的树永远不会有任何子节点。
我会像
这样递归地做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);
}
然后作为最后一次,我将在圆形预制件上有一个适当的 class Circle
,例如
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;
}
}
}
并在您的 BinNode
中存储它而不是 GameObject
以摆脱重复的 GetComponent
调用。
然后对于定位,我宁愿在节点本身中实现一个 getter 来计算它们的子级别。
我还会存储父节点,而是将新节点相对于父节点定位,而不是总是必须从根节点开始计算
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);
}
...
}
然后在获取节点位置时执行
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);
}
3 级
4 级
5 级
作为上次我为此做了一个pull request ;)