首页 > 其他分享 >ugui学习 - 自己实现InputField光标的显示,闪烁,左右移动

ugui学习 - 自己实现InputField光标的显示,闪烁,左右移动

时间:2023-07-28 23:55:05浏览次数:44  
标签:InputField void private renderer new ugui public 光标

最终效果

 

代码

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

[DisallowMultipleComponent]
[RequireComponent(typeof(CanvasRenderer))]
[RequireComponent(typeof(RectTransform))]
public class MyInputField_Caret : MonoBehaviour, ICanvasElement
{
    private VertexHelper m_CaretVh;
    private Mesh m_CaretMesh;

    public Text m_Text;

    [Range(1, 5)]
    public int m_CaretWidth = 1; //光标宽度
    public Color m_CaretColor = Color.black; //光标颜色

    [Range(0f, 4f)]
    public float m_CaretBlinkRate = 0.85f; //光标闪烁频率, 1s闪烁多少次, 显示-隐藏, 知道下次显示前为1次闪烁
    private float m_CaretBlinkStartTime;
    private bool m_CaretVisibleFlag = true;
    private Coroutine m_BlinkCo;

    private int m_CaretPos; //光标在哪个字符处, 光标处字符指示的是光标后面那个字符

    private CanvasRenderer m_CaretRenderer;
    public CanvasRenderer caretRenderer
    {
        get
        {
            if (null == m_CaretRenderer)
                m_CaretRenderer = GetComponent<CanvasRenderer>();
            return m_CaretRenderer;
        }
    }

    void Start()
    {
        m_CaretVh = new VertexHelper();
        m_CaretMesh = new Mesh();

        var renderer = this.caretRenderer;

        //设置材质和贴图
        renderer.materialCount = 1;
        var caretMat = Graphic.defaultGraphicMaterial;
        renderer.SetMaterial(caretMat, 0);

        var caretTexture = Texture2D.whiteTexture;
        renderer.SetTexture(caretTexture);

        //设置顶点
        PopulateCaretMesh(m_CaretVh);
        m_CaretVh.FillMesh(m_CaretMesh);
        renderer.SetMesh(m_CaretMesh);

        m_BlinkCo = StartCoroutine(CaretBlinkCo());
    }

    public virtual void Rebuild(CanvasUpdate update)
    {
        switch (update)
        {
            case CanvasUpdate.LatePreRender:
                UpdateCaretVerts();
                break;
        }
    }

    public void LayoutComplete() { }
    public void GraphicUpdateComplete() { }
    public bool IsDestroyed() { return (null == this); }


    private void UpdateCaretVerts()
    {
        var renderer = this.caretRenderer;

        m_CaretVh.Clear();
        //设置顶点
        if (m_CaretVisibleFlag)
            PopulateCaretMesh(m_CaretVh);
        m_CaretVh.FillMesh(m_CaretMesh);
        renderer.SetMesh(m_CaretMesh);
    }

    private void PopulateCaretMesh(VertexHelper vh)
    {
        var textGen = m_Text.cachedTextGenerator;
        if (textGen.characterCount <= 0)
            return;

        //光标显示在字符位置处
        float minX = 0;
        if (m_CaretPos < textGen.characters.Count)
        {
            var chInfo = textGen.characters[m_CaretPos];
            minX = chInfo.cursorPos.x;
            minX /= m_Text.pixelsPerUnit;
        }

        //找出光标处字符在哪一行上
        var caretLine = textGen.lineCount - 1;
        for (int i = 1; i < textGen.lineCount; ++i)
        {
            var tLineInfo = textGen.lines[i];
            if (tLineInfo.startCharIdx > m_CaretPos)
            {
                caretLine = i - 1;
                break;
            }
        }

        var lineInfo = textGen.lines[caretLine];
        float maxY = lineInfo.topY / m_Text.pixelsPerUnit;
        float minY = maxY - lineInfo.height / m_Text.pixelsPerUnit;

        //这边添加一个四边形
        vh.AddVert(new Vector3(minX, maxY), m_CaretColor, new Vector2(0f, 1f)); //左上
        vh.AddVert(new Vector3(minX + m_CaretWidth, maxY), m_CaretColor, new Vector2(1f, 1f)); //右上
        vh.AddVert(new Vector3(minX + m_CaretWidth, minY), m_CaretColor, new Vector2(1f, 0f)); //右下
        vh.AddVert(new Vector3(minX, minY), m_CaretColor, new Vector2(0f, 0f)); //左下

        //顺时针
        vh.AddTriangle(0, 1, 2);
        vh.AddTriangle(0, 2, 3);
    }

    IEnumerator CaretBlinkCo()
    {
        m_CaretVisibleFlag = true;
        m_CaretBlinkStartTime = Time.unscaledTime;
        yield return null;

        while (m_CaretBlinkRate > 0)
        {
            float blinkFullPeriod = 1 / m_CaretBlinkRate;
            float blinkVisiblePeriod = blinkFullPeriod * 0.5f;
            //比如: 2s闪烁1次, 那[0, 1)光标是显示状态, [1, 2)光标是隐藏状态
            bool visibleFlag = ((Time.unscaledTime - m_CaretBlinkStartTime) % blinkFullPeriod) < blinkVisiblePeriod;
            if (m_CaretVisibleFlag != visibleFlag)
            {
                m_CaretVisibleFlag = visibleFlag;
                UpdateCaretVerts();
            }

            yield return null;
        }

        m_BlinkCo = null;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.LeftArrow))
        {
            if (m_CaretPos > 0)
            {
                m_CaretPos--;

                //光标闪烁重置(重新开始计时)
                m_CaretVisibleFlag = true;
                m_CaretBlinkStartTime = Time.unscaledTime;
                CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); //下一帧统一更新
            }
        }
        if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            if (m_CaretPos < m_Text.text.Length)
            {
                m_CaretPos++;

                //光标闪烁重置(重新开始计时)
                m_CaretVisibleFlag = true;
                m_CaretBlinkStartTime = Time.unscaledTime;
                CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); //下一帧统一更新
            }
        }
    }

}

 

ugui的InputField做了,我们这边简化掉了的:

上下箭头让光标上下移动

ctrl+右箭头,移到下一行行首

ctrl+左箭头,移到当前行首或上一行行首

Home键,移到第1个字符前

End键,移到最后1个字符后

控件有焦点的情况才响应左右箭头键

   

标签:InputField,void,private,renderer,new,ugui,public,光标
From: https://www.cnblogs.com/sailJs/p/17587477.html

相关文章

  • Unity UGUI的Physics2DRaycaster (2D物理射线检测)组件的介绍及使用
    UnityUGUI的Physics2DRaycaster(2D物理射线检测)组件的介绍及使用一、什么是Physics2DRaycaster组件?Physics2DRaycaster是Unity中的一个UGUI组件,用于在2D场景中进行物理射线检测。它可以检测鼠标或触摸事件在UI元素上的碰撞,并将事件传递给相应的UI元素。二、Physics2DRaycaste......
  • Unity UGUI的PhysicsRaycaster (物理射线检测)组件的介绍及使用
    UnityUGUI的PhysicsRaycaster(物理射线检测)组件的介绍及使用1.什么是PhysicsRaycaster组件?PhysicsRaycaster是UnityUGUI中的一个组件,用于在UI元素上进行物理射线检测。它可以检测鼠标或触摸事件是否发生在UI元素上,并将事件传递给相应的UI元素。2.PhysicsRaycaster的工作......
  • ugui源码阅读 - Graphic渲染原理
    3d部分使用MeshRenderer来渲染,ugui的使用CanvasRenderer来进行渲染。把顶点,材质,贴图设置给CanvasRenderer,就能渲染出来了。 下面的代码,我们直接使用CanvasRenderer来进行渲染,等同于Graphic渲染部分的核心代码。usingUnityEngine;usingUnityEngine.UI;[RequireComponent(......
  • Unity UGUI的EventSystem(事件系统)组件的介绍及使用
    UnityUGUI的EventSystem(事件系统)组件的介绍及使用1.什么是EventSystem组件?EventSystem是UnityUGUI中的一个重要组件,用于处理用户输入事件,如点击、拖拽、滚动等。它负责将用户输入事件传递给合适的UI元素,并触发相应的事件回调函数。2.EventSystem组件的工作原理EventSystem......
  • Unity UGUI的EventSystem(事件系统)组件的介绍及使用
    UnityUGUI的EventSystem(事件系统)组件的介绍及使用1.什么是EventSystem组件?EventSystem是UnityUGUI中的一个重要组件,用于处理用户输入事件,如点击、拖拽、滚动等。它负责将用户输入事件传递给合适的UI元素,并触发相应的事件回调函数。2.EventSystem组件的工作原理EventSystem......
  • Unity UGUI的RawImage(原始图片)组件的介绍及使用
    UnityUGUI的RawImage(原始图片)组件的介绍及使用1.什么是RawImage组件?RawImage是UnityUGUI中的一个组件,用于显示原始图片。与Image组件不同,RawImage可以直接显示原始图片的像素数据,而不需要经过额外的处理。2.RawImage组件的工作原理RawImage组件通过将原始图片的像素数据直......
  • Unity UGUI的RectMask2D(2D遮罩)组件的介绍及使用
    UnityUGUI的RectMask2D(2D遮罩)组件的介绍及使用1.什么是RectMask2D组件?RectMask2D是UnityUGUI中的一个组件,用于实现2D遮罩效果。它可以限制子对象在指定的矩形区域内显示,超出区域的部分将被遮罩隐藏。2.RectMask2D的工作原理RectMask2D组件通过裁剪子对象的渲染区域来实现......
  • Unity UGUI的LayoutElement(布局元素)组件的介绍及使用
    UnityUGUI的LayoutElement(布局元素)组件的介绍及使用1.什么是LayoutElement组件?LayoutElement是UnityUGUI中的一个布局元素组件,用于控制UI元素在布局中的大小和位置。它可以用于自动调整UI元素的大小,以适应不同的屏幕分辨率和布局需求。2.LayoutElement组件的工作原理Layou......
  • Unity UGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用
    UnityUGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用1.什么是VerticalLayoutGroup组件?VerticalLayoutGroup是UnityUGUI中的一种布局组件,用于在垂直方向上自动排列子对象。它可以根据子对象的大小和布局设置,自动调整子对象的位置和大小,实现垂直布局效果。2.VerticalLay......
  • Unity UGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用
    UnityUGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用1.什么是VerticalLayoutGroup组件?VerticalLayoutGroup是UnityUGUI中的一种布局组件,用于在垂直方向上自动排列子对象。它可以根据子对象的大小和布局设置,自动调整子对象的位置和大小,实现垂直布局效果。2.VerticalLayo......