简体   繁体   English

Unity 3D:垂直布局组未将元素放置在应有的位置

[英]Unity 3D: Vertical Layout Group not placing elements where they should be

I am having a problem with my layout groups and keeping my UI consistent.我的布局组出现问题并保持 UI 一致。 I have a menu that holds two empty elements Blue and Green boxes bellow that get filled at playtime depending on what the player does.我有一个菜单,其中包含两个空元素蓝色和绿色框,它们会在游戏时间填充,具体取决于玩家的行为。 The orange boxes are buttons that may add or remove both purple boxes or orange boxes depending on the button pressed.橙色框是可以根据按下的按钮添加或删除紫色框或橙色框的按钮。 The Gray, Blue, and Green boxes all have Vertical Layout Group and Content Size fitter (vertical fit set to "Prefered Size").灰色、蓝色和绿色框都有垂直布局组和内容大小适配(垂直适配设置为“首选大小”)。 So from my understanding, all children within these boxes should be stacked vertically and will expand and contract the box as needed.所以根据我的理解,这些盒子里的所有孩子都应该垂直堆叠,并根据需要扩展和收缩盒子。

The problem I am having is that the first time I press a button that adds a purple box and expands the blue box, the green box stays where it is.我遇到的问题是,当我第一次按下添加紫色框并展开蓝色框的按钮时,绿色框保持原样。 The second time I press a button the Green box shifts where it should be no matter what gets added to the blue box.我第二次按下按钮时,无论向蓝框添加什么,绿框都会移动到它应该在的位置。 Conversely if pressing a button for the first time removes purple boxes and contracts the blue box, the green box stays where it is until I press a button again then shifts where it should be.相反,如果第一次按下按钮会移除紫色框并收缩蓝色框,则绿色框会保持原位,直到我再次按下按钮然后移动到应有的位置。

在此处输入图片说明

If I manually change any of the Gray, Blue, or Green boxes attributes such as anchor pivot or scale the boxes rearrange themselves the way it should be.如果我手动更改任何灰色、蓝色或绿色框属性,例如锚点枢轴或缩放框,它们会按照应有的方式重新排列。

Am I doing something wrong with Vertical Layout / Content Size fitter?我在垂直布局/内容大小钳工上做错了吗? I hope this is enough to go on.我希望这足以继续下去。 I can provide code if need be.如果需要,我可以提供代码。 I just don't know exactly what to share.我只是不知道要分享什么。

When the button is pressed call this method.当按钮被按下时调用这个方法。

void AcceptUnlockButtonInput(Exit roomExit) {
    if (PlayerHasKeyForExit(roomExit)) {
        LogAction(roomExit.exitUnlockDescription);
        roomExit.attemptedToExit = false;
        roomExit.attemptedToUnlock = false;
    } else if (roomExit.attemptedToUnlock == false) {
        LogAction(roomExit.exitLockedDescription);
    }

    DisplayActionText();

    foreach(Transform child in inputContainer.transform) {
        Destroy(child.gameObject);
    }

    DisplayInputButtons();
    Canvas.ForceUpdateCanvases();
}

LogActrion() just simply adds a string to a list then DisplayActionTest() reads off that list and creats/adds gameObjects to the container. LogActrion()只是简单地将一个字符串添加到列表中,然后DisplayActionTest()读取该列表并将游戏对象创建/添加到容器中。

public void DisplayActionText() {
    actionLog.ForEach(value => {
        var textObject = Instantiate(displayTextPrefab) as GameObject;
        var textMeshPro = textObject.GetComponentInChildren<TextMeshProUGUI>();
        textMeshPro.text = value;
        textObject.transform.SetParent(outputContainer.transform, false);
    });

    actionLog.Clear();
    Canvas.ForceUpdateCanvases();
}

Simply creats a button for each exit and adds the original method to the buttons listener.只需为每个退出创建一个按钮并将原始方法添加到按钮侦听器。

public void DisplayInputButtons() {
    foreach (var exit in Exits) {
            var unlockButton = Instantiate(userActionButton) as GameObject;
            newButton.text = buttonText;
            newButton.transform.SetParent(buttonGroup.transform, false);
            unlockButton.onClick.AddListener(() => AcceptUnlockButtonInput(exit));
    }
}

I have had the same problem with layout groups.我在布局组方面遇到了同样的问题。 From my understanding, layout groups don't update immediately when you put something in them.根据我的理解,当您将某些内容放入其中时,布局组不会立即更新。

Something what worked for me was:对我有用的是:

LayoutRebuilder.ForceRebuildLayoutImmediate(recttransform of the layoutgroup);

You can also try disabling and enabling the layout group component:您还可以尝试禁用和启用布局组组件:

IEnumerator UpdateLayoutGroup()
{
layoutGroupComponent.enabled = false;
yield return new WaitForEndOfFrame();
layoutGroupComponent.enabled = true;
}

If enabling and disabling the layoutgroup component doesn't work you can also try this:如果启用和禁用 layoutgroup 组件不起作用,您还可以尝试以下操作:

IEnumerator UpdateLayoutGroup()
{
yield return new WaitForEndOfFrame();

layoutGroupComponent.enabled = false;

layoutGroupComponent.CalculateLayoutInputVertical();

LayoutRebuilder.ForceRebuildLayoutImmediate(recttransform of the layoutgroup);

layoutGroupComponent.enabled = true;
}

I have had similar problems and I found this to be helpful: https://docs.unity3d.com/ScriptReference/Canvas.ForceUpdateCanvases.html我遇到了类似的问题,我发现这很有帮助: https : //docs.unity3d.com/ScriptReference/Canvas.ForceUpdateCanvases.html

Sometimes when instantiating elements into a gameobject with a vertical layout group, the elements will only get into their right position after updating the canvas.有时,当将元素实例化为具有垂直布局组的游戏对象时,元素只会在更新画布后才能进入正确的位置。

I know there is an issue with the VLG not updating correctly.我知道 VLG 存在未正确更新的问题。 Changing any of its value after adding/ removing elements forces its layout to resize and may fix your issue (at least that's the way I do it).添加/删除元素后更改其任何值会强制其布局调整大小并可能解决您的问题(至少我是这样做的)。

Can you try : - Add/remove your element - VerticalLayoutLement.spacing += Random.Range(-1,1);您可以尝试: - 添加/删除您的元素 - VerticalLayoutLement.spacing += Random.Range(-1,1);

To be sure it rescales properly i usually do it in a coroutine with an"yield return new WaitForNewFrame()" before the spacing part.为了确保它正确地重新缩放,我通常在一个协程中使用“yield return new WaitForNewFrame()”在间距部分之前进行。

After trying a lot of tricks that I found on the web I felt a little frustrated because none of them worked for me.在尝试了我在网上找到的很多技巧后,我感到有点沮丧,因为它们都不适合我。 When I ran the scene all the elements in the vertical layout looked messed, but when I clicked on Pause and Continue they magically fitted perfectly in the right position, so I wrote this simple funcion to emulate it, that works fine in my app:当我运行场景时,垂直布局中的所有元素看起来都一团糟,但是当我单击“暂停”和“继续”时,它们神奇地完美地安装在正确的位置,所以我编写了这个简单的函数来模拟它,在我的应用程序中运行良好:

IEnumerator RefreshPrefab(GameObject prefabInstance)
{
    prefabInstance.SetActive(false);
    yield return new WaitForSeconds(0.1f);
    prefabInstance.SetActive(true);
}

And call it sending as parameter the parent prefab that contains the layout group:并调用它作为参数发送包含布局组的父预制件:

StartCoroutine(RefreshPrefab(instantiatedPrefab));

In my case there were multiple layout groups nested into each other, so the rebuild of the parent layout group did not work.就我而言,有多个布局组相互嵌套,因此父布局组的重建不起作用。 I had to rebuild all in hierarchy, to properly update the layout.我必须在层次结构中重建所有内容,以正确更新布局。

LayoutGroup[] parentLayoutGroups = gameObject.GetComponentsInParent<LayoutGroup>();
foreach (LayoutGroup group in parentLayoutGroups) {
  LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)group.transform);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM