美文网首页unity
2019-08-26Unity3D使用GL实现图案解锁功能

2019-08-26Unity3D使用GL实现图案解锁功能

作者: Nmao | 来源:发表于2019-08-26 14:39 被阅读4次

    天是时候看到有人问如何在Unity3D的UGUI中实现图案解锁的功能,然后便试了一下。刚开始想用LineRender来实现,但又一想是要在UGUI中,然后就用了另外一种方法,即使用GL类来实现。

    GL相关介绍及官方文档

    实现后在Android手机上跑的效果如下:

    image.png

    主要实现GraphicUnLockManager类。代码如下:
    \\

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

    public class GraphicUnLockManager : MonoBehaviour
    {
    [Tooltip("含有Selectable及Image组件的UI对象,作为连接点。")]
    public List<RectTransform> _lstPoints = new List<RectTransform>();
    [Tooltip("用于设置所画线的颜色。(可使用“unlit/Color”Shader)")]
    public Material _matLineColor;
    [Tooltip("用于设置所画线的高度。")]
    public int _nHalfHeight = 15;
    [Tooltip("用于设置选择时Image的颜色。")]
    public Color _clrSelect = Color.red;
    [Tooltip("用于设置未选择时Image的颜色。")]
    public Color _clrUnSelect = Color.white;

    [HideInInspector]
    public List<RectTransform> _lstSelectPoints = new List<RectTransform>();//已选择连接点
    [HideInInspector]
    public List<int> _lstPassword = new List<int>();//以输入密码
    [HideInInspector]
    public Action<bool> onInputState;//true为开始输入,false为结束输入

    private bool _isPressing = false;//是否按下
    private Vector2 _vtPressPos;//按下点坐标

    private float _fDistance;//距离
    private float _fDegree;//夹角
    private Matrix4x4 _matrixTrans;//变换矩阵
    private Vector2[] _vertexPos = new Vector2[4];//顶点数组

    private Vector2 _tempPos;

    void Awake()
    {
    InitEnterEvent();//初始化所有连接点的消息
    ClearLines();//清空相关数据
    }

    void Update()
    {
    if (!IsPressed())
    {//当未按下时清空是数据
    ClearLines();
    }
    }

    bool IsPressed()
    {
    //触摸
    if (Input.touchCount > 0)
    {
    switch (Input.touches[0].phase)
    {
    case TouchPhase.Began:
    _isPressing = true;
    if (onInputState != null)
    {
    onInputState(true);//状态改变
    }
    break;
    case TouchPhase.Ended:
    case TouchPhase.Canceled:
    _isPressing = false;
    if (onInputState != null)
    {
    onInputState(false);//状态改变
    }
    break;
    }

    _vtPressPos = Input.touches[0].position;
    }
    else
    {
    //鼠标
    if (Input.GetMouseButtonDown(0))
    {
    _isPressing = true;
    if (onInputState != null)
    {
    onInputState(true);//状态改变
    }
    }
    if (Input.GetMouseButtonUp(0))
    {
    _isPressing = false;
    if (onInputState != null)
    {
    onInputState(false);//状态改变
    }
    }

    _vtPressPos = Input.mousePosition;
    }

    return _isPressing;
    }

    void OnPostRender()
    {
    DrawLines();//画所有线
    }

    void OnGUI()
    {
    string msg = "";

    msg += "是否正在输入:" + IsPressed() + "\n";
    msg += "密码:";
    for (int i = 0; i < _lstPassword.Count; i++)
    {
    msg += _lstPassword[i] + ",";
    }

    GUIStyle guiStyle = new GUIStyle();
    guiStyle.normal.textColor = new Color(1, 1, 1); //设置字体颜色
    guiStyle.fontSize = 75; //设置字体大小
    GUILayout.Label(msg, guiStyle);
    }

    void InitEnterEvent()
    {
    //为每个点添加Enter事件
    _lstPoints.ForEach((rtTrans) =>
    {
    EventTrigger trigger = rtTrans.GetComponent<EventTrigger>();
    if (trigger == null)
    {
    trigger = rtTrans.gameObject.AddComponent<EventTrigger>();
    }

    //添加事件
    EventTrigger.Entry entryEnter = new EventTrigger.Entry();
    entryEnter.eventID = EventTriggerType.PointerEnter;//进入事件
    EventTrigger.TriggerEvent evtEnter = new EventTrigger.TriggerEvent();
    evtEnter.AddListener(OnSelectPoint);
    entryEnter.callback = evtEnter;
    trigger.triggers.Add(entryEnter);

    EventTrigger.Entry entryDown= new EventTrigger.Entry();
    entryDown.eventID = EventTriggerType.PointerDown;//按下事件
    EventTrigger.TriggerEvent evtDown = new EventTrigger.TriggerEvent();
    evtDown.AddListener(OnSelectPoint);
    entryDown.callback = evtDown;
    trigger.triggers.Add(entryDown);
    });
    }

    public void OnSelectPoint(BaseEventData obj)
    {
    //转换数据类型
    PointerEventData data = obj as PointerEventData;

    GameObject target = null;
    if (null != data.pointerEnter)
    {
    target = data.pointerEnter;
    }
    else if (null != data.pointerPress)
    {
    target = data.pointerPress;
    }

    AddSelectPoint(target);//添加选择连接点
    }

    void AddSelectPoint(GameObject obj)
    {
    if (IsPressed() && null != obj)
    {
    //将未连接的点添加到需要连接的点的列表中去
    RectTransform rtTrans = obj.GetComponent<RectTransform>();
    if (null != rtTrans && !_lstSelectPoints.Contains(rtTrans))
    {
    //添加到绘制列表
    _lstSelectPoints.Add(rtTrans);
    //添加密码序列
    _lstPassword.Add(_lstPoints.IndexOf(rtTrans));
    //改变颜色
    rtTrans.GetComponent<Image>().color = _clrSelect;
    }
    }
    }

    void ClearLines()
    {
    //清空选择及密码列表
    _lstSelectPoints.Clear();
    _lstPassword.Clear();

    //还原颜色
    _lstPoints.ForEach((rtTrans) =>
    {
    rtTrans.GetComponent<Image>().color = _clrUnSelect;
    });
    }

    void DrawLine(Vector2 vtStart, Vector2 vtEnd)
    {
    _tempPos = vtEnd - vtStart;
    _fDistance = Vector3.Distance(Vector3.zero, _tempPos);//距离
    _fDegree = Vector3.Angle(_tempPos, Vector3.right);//与x轴正方向的夹角

    //判断旋转方向,逆时针为正,顺时针为付
    if (_tempPos.y < 0)
    {
    _fDegree *= -1;
    }

    //设置变换矩阵
    _matrixTrans.SetTRS(vtStart, Quaternion.Euler(0, 0, _fDegree), Vector3.one);//设置变换矩阵

    //设置绘制顶点坐标
    _vertexPos[0].x = 0;
    _vertexPos[0].y = -_nHalfHeight;
    _vertexPos[1].x = 0;
    _vertexPos[1].y = _nHalfHeight;
    _vertexPos[2].x = _fDistance;
    _vertexPos[2].y = _nHalfHeight;
    _vertexPos[3].x = _fDistance;
    _vertexPos[3].y = -_nHalfHeight;

    //绘制
    GL.PushMatrix();
    GL.LoadPixelMatrix();//使(0,0,0)为左下角,(Screen.width,Screen.height,0)为右上角
    GL.MultMatrix(_matrixTrans);
    GL.Begin(GL.QUADS);//绘制四边形
    for (int n = 0; n < 4; n++)
    {
    GL.Vertex(_vertexPos[n]);
    }
    GL.End();
    GL.PopMatrix();
    }

    void DrawLines()
    {
    //设置线的材质
    _matLineColor.SetPass(0);

    //连接已选择的点
    for (int nIndex = 0; nIndex < _lstSelectPoints.Count - 1; nIndex++)
    {
    DrawLine(_lstSelectPoints[nIndex].position, _lstSelectPoints[nIndex + 1].position);
    }

    //连接到Press点
    if (IsPressed() && _lstSelectPoints.Count > 0)
    {
    DrawLine(_vtPressPos, _lstSelectPoints[_lstSelectPoints.Count - 1].position);
    }
    }
    }

    \
    上面的实现中都有注明,如有不清楚的地方请留言。绘制主要在DrawLine函数中。

    然后将GraphicUnLockManager类添加到MainCamera上,只有这样OnPostRender才会被正确调用,使线能够在场景中的物体都渲染完成后再绘制。

    创建连接点,如下设置:

    设置GraphicUnLockManager,如下设置:

    接下来就可以在编辑器中运行看看结果了!按下鼠标左键开始选择,释放结束选择,效果如下:

    image.png

    相关文章

      网友评论

        本文标题:2019-08-26Unity3D使用GL实现图案解锁功能

        本文链接:https://www.haomeiwen.com/subject/evnrectx.html