Administrator
发布于 2024-07-15 / 21 阅读
0
0

简单的Unity UI的知识

Unity UI

学:ui的控件使用,ui控件的事件响应,ui的分辨率自适应

发展史:GUI->NGUI->UGUI->UIElements(编辑器开发)

Inspector面板扩展(EditorGUI、EditorGUILayout、GUI、GUILayout)-CSDN博客

GUI

GUI是什么

全称:即时模式游戏用户交互界面(IMGUI),被简称为GUI,代码驱动的UI系统

GUI的主要作用:

1.作为程序员的调试工具,创建游戏那调试工具

2.为脚本组件创建自定义检视面板

3.创建新的编辑器窗口和工具以拓展Unity本身(一般作为内置游戏工具)

//不要用它为玩家制作UI功能,不是所见即所得,要运行才有

//可以做程序用的工具和地图编辑器

GUI的工作原理

1.在MonoBehaviour的脚本中的特殊函数里

2.调用GUI提供的方法,类似生命周期函数

private void OnGUI(){

//在其中书写 GUI相关代码 即可显示GUI内容

//控制,事件监听

}

注意:

1.它每帧执行,相当于是用于专门绘制GUI界面的函数(update)

2.一般只在其中执行GUI相关界面绘制和操作逻辑

3.该函数在OnDisable之前 LateUpdate之后执行

4.只要是继承MonoBehaviour的脚本 都可以在OnGUI中绘制GUI

重要参数以及文本和按钮

参数详情见:Unity 用户手册 (2019.4 LTS) - Unity 手册

GUI控件绘制的共同点

1.他们都是GUI公共类中提供的静态函数,直接调用即可

2.他们的参数大同小异

位置参数:Rect(矩形)x,y位置 w,h 尺寸

显示文本: string参数

图片信息:Texture参数(材质)

综合信息: CUIContent参数

自定义样式:GUIStyle参数

3.每一种控件都有多种重制,都是各个参数的排列组合,必备的参数是位置信息显示信息

文本和按钮 Label Button

public class lesson1: MonoBehaviour
{
    public Texture a1; //材质/图形的参数
    public Rect pic_a1;//位置参数
    public GUIContent sss;//打包的参数,可以有图片和string
    public GUIStyle aaa; //各参数见下面
    private void OnGUI()
    {
        //文本控件
        GUI.Label(new Rect(0,0,100,20),"夜雨"); 
        GUI.Label(pic_a1,sss,aaa);
       //GUI.toolip可以获取当前鼠标或者键盘选中的GUI控件对应的tooltip信息
        //按键控件
        GUI.Button(pic_a1,a1,buttonstyle);
        //按钮有一个bool类型的返回值可以判断点击 按下和抬起才算一次点击
        //长按的按钮只要在按钮范围内按下就一直返回ture
        GUI,RepeatButton(位置,东西,style);
    }
}

多选框和单选框 toggle

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
​
public class lesson1: MonoBehaviour
{
    private bool flag= true;
    private bool flag2 = true;
    public GUIStyle sss;
    private int a = 1;
    private void OnGUI()
    {
        //多选框,普通样式
        // flag = GUI.Toggle(new Rect(0, 0, 100, 30), flag, "效果开关");
        /*
        Toggle 开/关状态由 true/false 布尔值表示。必须提供布尔值作为参数来使 Toggle 表示实际状态。
        如果点击,则 Toggle 函数将返回一个新的布尔值。为了捕获此交互性,必须指定布尔值来接受 Toggle 函数的返回值。
        */
        // 自定义样式 显示问题
        //修改固定高宽 fixedWidth和fixedHeight
        //修改从GUIStyle边缘到内容起始处的空间 padding,把字体和图标移动一点
        flag2 = GUI.Toggle(new Rect(0, 0, 100, 50), flag2, "app开关",sss);
        //单选框基于多选框(多选一)
        //可以把中间的判断改成 int t==1
        if (GUI.Toggle(new Rect(0, 100, 100, 30), a == 1, "app开关1")) a = 1;
        if (GUI.Toggle(new Rect(0, 200, 100, 30), a == 2, "app开关2 ")) a = 2;
    }
}

输入框和拖动条

    public string inputstr;
    public string sss;
    public float nowval;
    private void OnGUI()
    {
        //可以在屏幕上输入字符,读取输入的字符再打印到屏幕上,不赋值会在下一帧刷新掉
        //参数 位置,显示的string,最大string的长度
        inputstr= GUI.TextField(new Rect(0, 0, 100, 30), inputstr,5);
        //密码输入,把输入的字符串替换成另外的字符
        sss = GUI.PasswordField(new Rect(0, 50, 100, 30), sss,'*');
        //拖动条都类似的,表示从最左托到最右边的数值变化
        nowval=GUI.HorizontalSlider(new Rect(0, 100, 100, 50), nowval, 0, 1); //水平
        nowval=GUI.VerticalSlider(new Rect(0, 150, 20, 50), nowval, 0, 1); //竖直
    }

图片绘制和框 DrawTexture Box

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
​
public class lesson1: MonoBehaviour
{
    public Rect weizhi;
    public Texture tex;
    private void OnGUI( )
    {
        //传一张图设置宽高,位置和图片,参数有
        //ScaleMode
        //ScaleAndCrop 
        //会通过宽高比来计算图片但是会进行裁剪
        //ScakeToFit   
        //会自动根据狂高比进行计算 不会拉变形会一直保持图片的完全显示的状态
        //StretchToFill 始终填充满你传入的 Rect范围
        // alpha 用来控制图片是否开启透明通道的
        //imageAspect 
        //自定义宽高比 如果不填 默认为0 就会使用图片原始的宽高
        GUI.DrawTexture(weizhi, tex,mode,alpha,wh);
        GUI.Box(weizhi, "框"); //没啥用
    }
}

工具栏和选择网格 Toolbar SelectionGrid

   public Rect weizhi;
   private int toolbaridx=0;
   private int seidx = 0;
   private string[] toolbarinfors = new string[] { "选项一", "选项二", "选择三" };
   private void OnGUI( )
   {
       //类似单选栏
       //toolbaridx=GUI.Toolbar(weizhi, toolbaridx,toolbarinfors);
       //可以用这个索引来做操作
       switch(toolbaridx)
       {
          case 0:
               break;
       }
       //选择网格 相对于toolbar多了一个参数xCount代表水平方向最多显示的按钮数量
       seidx=GUI.SelectionGrid(weizhi, seidx, toolbarinfors, 3);
   }

分组和滑动列表 BeginGroup EndGroup

    public Rect grouppos;
    public Rect showpos;
    public Rect scPos;
    private Vector2 nowpos;
    private void OnGUI( )
    {
        #region 分组
        //用于批量控制控件位置
        //可以理解为 包裹着的控件加了一个父对象
        //可以通过控制分组来控制包裹控件的位置
        GUI.BeginGroup(new Rect(0,0,0,0) ); //以这个位置为原点
        GUI.Button(new Rect(0, 0, 100, 50), "测试按钮");
        GUI.Label(new Rect(0, 60, 100, 30), "文本");
        GUI.EndGroup();
        #endregion ;
        //滚动列表
        //scpos可见范围 nowpos整体位置 showpos内容大小
        nowpos = GUI.BeginScrollView(scPos, nowpos, showpos);
        GUI.EndScrollView();
    }

窗口 window

​
    public Rect weizhi = new Rect(100, 100, 200, 150);
    private void OnGUI( )
    {
        // 窗口
        //第一个参数 id是窗口的唯一ID 不要和别的窗口重复
        //委托参数 是用于绘制窗口用的函数 传入即可
        //id除了区分不同的窗口还可以在一个函数中去处理多个窗口的逻辑
        GUI.Window(1, weizhi, hanshu, "测试窗口");
        //模态窗口 可以让该其它控件不在有用
        //你可以理解该窗口在最上层 其它按钮都点击不到了
        //只能点击该窗口上的控件
        GUI.ModalWindow(3, new Rect(300, 100, 200, 100), hanshu, "模态窗口");
        //警告和提示的作用,必须先处理完
        // 拖动窗口,因为函数有返回值所有还是可以改变
        weizhi = GUI.Window(4, weizhi, hanshu2, "窗口4");
​
    }
    private void hanshu2(int id)
    {
        GUI.DragWindow();
        //该API写在窗口函数中调用,可以让窗口被拖动
        //传如Rect参数的重载 作用
        //是决定窗口中那一部分位置 可以被拖动
        //无参重载,默认所有位置都可以拖动
​
    }
    private void hanshu(int id)
    {
        GUI.Button(new Rect(0, 30, 30, 20), "1");
    }

颜色

用TextMeshPro 显示时间

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class ShowTime : MonoBehaviour
{
    // Start is called before the first frame update
    public TextMeshProUGUI Text;
    // Start is called before the first frame update
    void Start()
    {   
        Text = transform.GetComponent<TextMeshProUGUI>();
        // TxtCurrentTime = GetComponent<TextMeshPro> ();
    }

    // Update is called once per frame
    void Update()
    {
        //获取系统当前时间
        System.DateTime NowTime = System.DateTime.Now.ToLocalTime();
        Text.text = NowTime.ToString("HH:mm");
    }
}

所有UI都必须显示在canvous上

如何实现Scroll上的text能够无限滑动 C#脚本

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;
using TMPro;

//读取位置和宽度的特性
[RequireComponent(typeof(GridLayoutGroup))]
[RequireComponent(typeof(ContentSizeFitter))]
public class InfinityGridLayoutGroup : MonoBehaviour
{

    [SerializeField]
    int minAmount = 0;
    /*
    实现无限滚动,需要的最少的child数量,上面一个下面一个 2个就够
    */

    RectTransform rectTransform;  //位置
    GridLayoutGroup gridLayoutGroup;
    ContentSizeFitter contentSizeFitter;

    ScrollRect scrollRect;

    List<RectTransform> children = new List<RectTransform>();

    Vector2 startPosition;

    int amount = 0;

    public delegate void UpdateChildrenCallbackDelegate(int index, Transform trans);   
    public UpdateChildrenCallbackDelegate updateChildrenCallback = null;
    //回调每次要更新每个btn的Text

    int realIndex = -1;
    int realIndexUp = -1; //两个指针从下往上;

    bool hasInit = false;
    Vector2 gridLayoutSize;
    Vector2 gridLayoutPos;
    Dictionary<Transform, Vector2> childsAnchoredPosition = new Dictionary<Transform, Vector2>();
    Dictionary<Transform, int> childsSiblingIndex = new Dictionary<Transform, int>();

    IEnumerator InitChildren()
    {
        yield return 0;
        if (!hasInit)
        {
            //获取Grid的宽度;
            rectTransform = GetComponent<RectTransform>();
            gridLayoutGroup = GetComponent<GridLayoutGroup>();
            gridLayoutGroup.enabled = false;
            contentSizeFitter = GetComponent<ContentSizeFitter>();
            contentSizeFitter.enabled = false;
            /*关于Anchors布局 https://blog.lujun.co/2017/09/07/unity_rect_transform/ */
            gridLayoutPos = rectTransform.anchoredPosition;
            gridLayoutSize = rectTransform.sizeDelta;

            //注册ScrollRect滚动回调;
            scrollRect = transform.parent.GetComponent<ScrollRect>();
            scrollRect.onValueChanged.AddListener((data) => { ScrollCallback(data); });
            /*
             * onValueChanged 用于监视 ScrollRect 对象中的更改。 onValueChanged 调用将使用 UnityEvent.AddListener API 来监视 更改。
             * 发生更改时,将调用用户提供的脚本代码。 ScrollRect.onValueChanged 的 UnityEvent.AddListener API 要求一个 Vector2 参数。
             */

            //获取所有child anchoredPosition 以及 SiblingIndex;
            /* SiblingIndex即为同级索引,意为「同一父节点下的兄弟节点间的位置」。
               siblingIndex 越小的节点排越前,索引最小值为 0,也就是第一个节点的索引值。
            使用 GetSiblingIndex 查找 GameObject 在该层级视图中的位置。当 GameObject 的同级索引发生更改时,其在“Hierarchy”窗口中的顺序也会改变。
            如果需要更改 GameObject 子项的排序(例如使用布局组组件时很有用。布局组还将按索引以可视化方式对组进行重新排序
             */
            for (int index = 0; index < transform.childCount; index++)
            {
                Transform child = transform.GetChild(index);
                RectTransform childRectTrans = child.GetComponent<RectTransform>();
                childsAnchoredPosition.Add(child, childRectTrans.anchoredPosition);
                childsSiblingIndex.Add(child, child.GetSiblingIndex());
            }
            hasInit = true;
        }
        else
        {
            rectTransform.anchoredPosition = gridLayoutPos;
            rectTransform.sizeDelta = gridLayoutSize;

            children.Clear();

            realIndex = -1;
            realIndexUp = -1;

            //children重新设置上下顺序;
            foreach (var info in childsSiblingIndex)
            {
                info.Key.SetSiblingIndex(info.Value);
            }

            //children重新设置anchoredPosition;
            for (int i= 0; index < transform.childCount; i++)
            {
                Transform child = transform.GetChild(i);
                RectTransform childRectTrans = child.GetComponent<RectTransform>();
                if (childsAnchoredPosition.ContainsKey(child))
                    childRectTrans.anchoredPosition = childsAnchoredPosition[child];
                else Debug.LogError("childsAnchoredPosition no contain " + child.name);
            }
        }

        //获取所有child;
        for (int i = 0; index < transform.childCount; i++)
        {
            Transform trans = transform.GetChild(i);
            trans.gameObject.SetActive(true);

            children.Add(transform.GetChild(index).GetComponent<RectTransform>());

            //初始化前面几个更新一次;
            UpdateChildrenCallback(children.Count - 1, transform.GetChild(i));
        }

        startPosition = rectTransform.anchoredPosition;

        realIndex = children.Count - 1;

        /*
         * Debug.Log( scrollRect.transform.TransformPoint(Vector3.zero));
        Debug.Log(transform.TransformPoint(children[0].localPosition));
        */
        //如果需要显示的个数小于设定的个数;
        for (int i= 0; index < minAmount; i++)  children[i].gameObject.SetActive(i< amount);

        if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
        {
            //如果小了一行,则需要把GridLayout的高度减去一行的高度;
            int row = (minAmount - amount) / gridLayoutGroup.constraintCount;
            if (row > 0)
            {
                rectTransform.sizeDelta -= new Vector2(0, (gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y) * row);
            }
        }
        else
        {
            //如果小了一列,则需要把GridLayout的宽度减去一列的宽度;
            int column = (minAmount - amount) / gridLayoutGroup.constraintCount;
            if (column > 0)
            {
                rectTransform.sizeDelta -= new Vector2((gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x) * column, 0);
            }
        }
    }

    void ScrollCallback(Vector2 data)
    {
        UpdateChildren();
    }

    void UpdateChildren()
    {
        if (transform.childCount < minAmount)
        {
            return;
        }

        Vector2 currentPos = rectTransform.anchoredPosition;

        if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount) //看cell够不够将列数约束为指定数量。
        {
            float offsetY = currentPos.y - startPosition.y;

            if (offsetY > 0)
            {
                //向上拉,向下扩展;
                {
                    if (realIndex >= amount - 1)
                    {
                        startPosition = currentPos;
                        return; //剪枝(
                    }

                    float scrollRectUp=scrollRect.transform.TransformPoint(Vector3.zero).y;

                    Vector3 childBottomLeft = new Vector3(children[0].anchoredPosition.x, children[0].anchoredPosition.y - gridLayoutGroup.cellSize.y, 0f);
                    //Debug.Log(children[0].anchoredPosition.x+".............."+children[0].anchoredPosition.y)
                    float childBottom = transform.TransformPoint(childBottomLeft).y;

                    if (childBottom >= scrollRectUp)
                    {
                        //Debug.Log("childBottom >= scrollRectUp");

                        //移动到底部;
                        for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                        {
                            children[index].SetAsLastSibling();
                            children[index].anchoredPosition = new Vector2(children[index].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y - gridLayoutGroup.cellSize.y - gridLayoutGroup.spacing.y);
                            realIndex++;
                            if (realIndex > amount - 1)
                            {
                                children[index].gameObject.SetActive(false);
                            }
                            else
                            {
                                UpdateChildrenCallback(realIndex, children[index]);
                            }
                        }

                        //GridLayoutGroup 底部加长;
                        rectTransform.sizeDelta += new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                        //更新child;
                        for (int index = 0; index < children.Count; index++)
                        {
                            children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                        }
                    }
                }
            }
            else
            {
                //Debug.Log("");
                //向下拉,下面收缩;
                if (realIndex + 1 <= children.Count)
                {
                    startPosition = currentPos;
                    return;
                }
                RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
                Vector3 scrollRectAnchorBottom = new Vector3(0, -scrollRectTransform.rect.height - gridLayoutGroup.spacing.y, 0f);
                float scrollRectBottom = scrollRect.transform.TransformPoint(scrollRectAnchorBottom).y;

                Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);

                float childUp = transform.TransformPoint(childUpLeft).y;

                if (childUp < scrollRectBottom)
                {
                    //Debug.Log("childUp < scrollRectBottom");

                    //把底部的一行 移动到顶部
                    for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                    {
                        children[children.Count - 1 - index].SetAsFirstSibling();
                        children[children.Count - 1 - index].anchoredPosition = new Vector2(children[children.Count - 1 - index].anchoredPosition.x, children[0].anchoredPosition.y + gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
                        //Debug.log()
                        children[children.Count - 1 - index].gameObject.SetActive(true);
                        UpdateChildrenCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
                        //更新
                    }

                    realIndex -= gridLayoutGroup.constraintCount;

                    //GridLayoutGroup 底部缩短;
                    rectTransform.sizeDelta -= new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);

                    //更新child;
                    for (int index = 0; index < children.Count; index++)
                    {
                        children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                    }
                }
            }
        }
        else
        {
            float offsetX = currentPos.x - startPosition.x;

            if (offsetX < 0)
            {
                //向左拉,向右扩展;
                {
                    if (realIndex >= amount - 1)
                    {
                        startPosition = currentPos;
                        return;
                    }

                    float scrollRectLeft = scrollRect.transform.TransformPoint(Vector3.zero).x;

                    Vector3 childBottomRight = new Vector3(children[0].anchoredPosition.x + gridLayoutGroup.cellSize.x, children[0].anchoredPosition.y, 0f);
                    float childRight = transform.TransformPoint(childBottomRight).x;

                    // Debug.LogError("childRight=" + childRight);

                    if (childRight <= scrollRectLeft)
                    {
                        //Debug.Log("childRight <= scrollRectLeft");

                        //移动到右边;
                        for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                        {
                            children[index].SetAsLastSibling();

                            children[index].anchoredPosition = new Vector2(children[children.Count - 1].anchoredPosition.x + gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, children[index].anchoredPosition.y);

                            realIndex++;

                            if (realIndex > amount - 1)
                            {
                                children[index].gameObject.SetActive(false);
                            }
                            else
                            {
                                UpdateChildrenCallback(realIndex, children[index]);
                            }
                        }

                        //GridLayoutGroup 右侧加长;
                        rectTransform.sizeDelta += new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, 0);

                        //更新child;
                        for (int index = 0; index < children.Count; index++)
                        {
                            children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                        }
                    }
                }
            }
            else
            {
                //Debug.Log("Drag Down");
                //向右拉,右边收缩;
                if (realIndex + 1 <= children.Count)
                {
                    startPosition = currentPos;
                    return;
                }
                RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
                Vector3 scrollRectAnchorRight = new Vector3(scrollRectTransform.rect.width + gridLayoutGroup.spacing.x, 0, 0f);
                float scrollRectRight = scrollRect.transform.TransformPoint(scrollRectAnchorRight).x;

                Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);

                float childLeft = transform.TransformPoint(childUpLeft).x;

                if (childLeft >= scrollRectRight)
                {
                    //Debug.LogError("childLeft > scrollRectRight");

                    //把右边的一行 移动到左边;
                    for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
                    {
                        children[children.Count - 1 - index].SetAsFirstSibling();
                        children[children.Count - 1 - index].anchoredPosition = new Vector2(children[0].anchoredPosition.x - gridLayoutGroup.cellSize.x - gridLayoutGroup.spacing.x, children[children.Count - 1 - index].anchoredPosition.y);
                        children[children.Count - 1 - index].gameObject.SetActive(true);
                        UpdateChildrenCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
                    }
                    //GridLayoutGroup 右侧缩短;
                    rectTransform.sizeDelta -= new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, 0);
                    //更新child;
                    for (int index = 0; index < children.Count; index++)
                    {
                        children[index] = transform.GetChild(index).GetComponent<RectTransform>();
                    }
                    realIndex -= gridLayoutGroup.constraintCount;
                }
            }
        }
        startPosition = currentPos;
    }

    void UpdateChildrenCallback(int index, Transform trans)
    {
        if (updateChildrenCallback != null)
        {
            updateChildrenCallback(index, trans);
        }
    }


    /// <summary>
    /// 设置总的个数;
    /// </summary>
    /// <param name="count"></param>
    public void SetAmount(int count)
    {
        amount = count;

        StartCoroutine(InitChildren());
    }
}


评论