首页 > 编程技术 > csharp

Unity3D使用GL实现图案解锁功能

发布时间:2020-6-25 10:37

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

GL相关介绍及官方文档

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

主要实现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,如下设置:

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

标签:[!--infotagslink--]

您可能感兴趣的文章: