实现效果:

  1. 标题以打字机的方式呈现
  2. A、B、C在标题结束后显示
  3. 选项的背景长度会跟着字的长度变化
  4. 选项的排列会根据长度动态变化

实现思路

把所有需要排版的逻辑全部交给Unity的Layout组件,然后根据当前的长度,动态赋值给对应的GameObject作为他的子物体

Layout Group设定

首先我们需要创建如下的层级结构

  • Content 添加Horizontal Layout,用于调整Left 和Right的位置
  • Left、Right 添加Vertical Layout,在上面添加4个空物体,来固定位置
  • 然后把Selection 放在Default中,动态赋值Selection的父物体

可根据内容长度变化的Text

因为这里的需求是选项的内容长度最多为一行,所以遵照下图设置

根据Text 的大小改变Image的大小

获取Text的大小

这里需要注意的是, Content Size Fitter 组件的更新顺序在FixUpdate后获取,所以这里动态获得Text的大小需要卸载FixUpdate中

public static Vector2 GetRectTransformSize(RectTransform trans) {

     return trans.sizeDelta;
}

设置Image的大小

在这之前需要对Sprite进行设置,因为上面的背景图是中间部分进行拉伸,所以在九宫格中要对中间部分进行处理

public static void SetRectTransformSize(RectTransform trans, Vector2 newSize) {

    Vector2 oldSize = trans.rect.size;
    Vector2 deltaSize = newSize - oldSize;
    trans.offsetMin = trans.offsetMin - new Vector2(deltaSize.x * trans.pivot.x, deltaSize.y * trans.pivot.y);
    trans.offsetMax = trans.offsetMax + new Vector2(deltaSize.x * (1f - trans.pivot.x), deltaSize.y * (1f - trans.pivot.y));
}

判断当前应该放在左边还是右面

这个需求就很简单了,直接看代码就好

private bool CheckIsRight(Vector2 one, Vector2 other, int leftIndex, int rightIndex) {

    float sum = one.x + other.x;

    if (sum < (_contentSize.x * _contentThreshold) && (leftIndex > rightIndex))
        return true;

        return false;
}

源码

public class WalkingDogQuestion : MonoBehaviour {

        public Text questionText;
        public UIBehaviour[] selectionsGObjs;
        public Transform left;
        public Transform right;
        public Transform defaultTrans;
        public Button skipBtn;

        private Walkingdog_Question _question;
        private Tween _t;
        private bool _start;
        private Vector2 _contentSize;

        private float _contentThreshold = 0.9f;

        public Walkingdog_Question Question {
            get
            {
                if (_question == null)
                    _question = new Walkingdog_Question();
                return _question;
            }
            set
            {
                this.gameObject.SetActive(true);
                _question = value;
                OnSet();
            }
        }

        private void Awake() {

            _contentSize = this.GetComponent<RectTransform>().sizeDelta;
        }

        private void OnSet() {

            SetSectionDisplay(false);

            questionText.text = "";
            _t = questionText.DOText(Question.question, 2f).SetEase(Ease.Linear);

            _t.onComplete += () => {

                for (int i = 0; i < Question.selections.Count; i++) {

                    selectionsGObjs[i].gameObject.SetActive(true);
                    UIUtils.SetChildText(selectionsGObjs[i], Question.selections[i].selection);
                }
                skipBtn.gameObject.SetActive(false);
                _start = true;
            };
        }

        private void FixedUpdate() {

            if (_start) {

                int rightIndex = 0;
                int leftIndex = 1;

                SetSelectionSize(selectionsGObjs[0]);
                SetSelectionParent(selectionsGObjs[0].GetComponent<RectTransform>(), 0);

                for (int i = 1; i < Question.selections.Count; i++) {

                    var rt = SetSelectionSize(selectionsGObjs[i]);

                    var lastRt = selectionsGObjs[i - 1].GetComponent<RectTransform>();

                    if(CheckIsRight(rt.sizeDelta, lastRt.sizeDelta, leftIndex, rightIndex)) {

                        SetSelectionParent(rt, rightIndex, true);
                        rightIndex++;
                    } else {

                        SetSelectionParent(rt, leftIndex);
                        leftIndex++;
                    }
                }
                _start = false;
            }
        }

#region Tools

        private RectTransform SetSelectionSize(UIBehaviour selection) {

            var rt = selection.GetComponent<RectTransform>();
            var size = UIUtils.GetChildRectTransformSize(selection);
            size.x += 100;
            size.y = 100;
            UIUtils.SetRectTransformSize(rt, size);

            return rt;
        }

        private void SetSelectionParent(RectTransform selection, int index, bool isRight = false) {

            selection.transform.SetParent(isRight ? right.transform.GetChild(index) : left.transform.GetChild(index));
            selection.anchoredPosition = Vector2.zero;
            selection.GetComponent<CanvasGroup>().DOFade(1, 0.5f);
        }

        private bool CheckIsRight(Vector2 one, Vector2 other, int leftIndex, int rightIndex) {

            float sum = one.x + other.x;

            if (sum < (_contentSize.x * _contentThreshold) && (leftIndex > rightIndex))
                return true;

            return false;
        }

        private void SetSectionDisplay(bool flag) {

            for (int i = 0; i < selectionsGObjs.Length; i++) {

                selectionsGObjs[i].gameObject.SetActive(flag);
                selectionsGObjs[i].transform.SetParent(defaultTrans);
                selectionsGObjs[i].GetComponent<CanvasGroup>().alpha = 0;
            }

            skipBtn.gameObject.SetActive(true);
        }
#endregion

        public void OnBtnSkip() {

            _t.Complete();
        }

        public void OnBtnSubmit(int index) {

            MessageAggregator<int>.Instance.Publish(MessageDef.WalkingDogQuestion_Select, this, new MessageArgs<int>(index));
            this.gameObject.SetActive(false);
        }
    }

What doesn’t kill you makes you stronger.