最近有机会用Unity做了一个简单的手势解锁,工具:Unity5.4,语言:C#
界面及实现方法相当low,只是作为交流,请各位大虾轻喷,且我做的这个有一个坑后面会交代。
最开始做的时候,大概思路是,把手势解锁分为三块来做:
一是搭建界面;
二是实现手势划线逻辑;
三是实现解锁的各种逻辑,比如密码创建、储存、修改、删除,手势的创建、判断、储存、手指中途离开屏幕的处理、经过点自定连线的手势处理等等。
我在这里主要记录手势划线逻辑的处理,因为最开始做之前在网上搜索实现方法,都不是很理想,做过的实现的方法和我想要的不太一样,所以干脆花上点时间来做一个,且Unity贵为一款3D游戏引擎来做“手势解锁”,似乎也难倒了我这个大汉,大虾看到此别怪我,我交代我是菜鸟。如果想在这篇文章上看到完整的解锁代码,请出门左转吧。
----------------------------------------------------------分割线·先占个坑以后再更-------------------------------------------------------------
我的想法:要实现手势滑动,就得先实现用鼠标(编辑器模式下用鼠标代替手指)划一条线处理,且点击第一下画出线的第一个点,然后按照鼠标左键拖拽实现划线,即线的第二个点要跟随触碰点移动。能顺利画下第一条线后就加入“经过手势点相关逻辑,比如存储该点代表的密码,并将该点设置为下一条线的起点等等逻辑”,然后画第二条线,第三条,第四条、、、、、
那么问题来了,如何实现划线?Unity中并没有“线”这个组件,我是使用UGUI中的Image组件,添加一个Image,将它做成一个小长方块(不要做得太宽,因为你的线本就很细),然后在运行时动态改变其位置position、角度rotation、尺寸size,线预设如下图:
线是这样画出来的:创建一个脚本,让脚本继承IBeginDragHandler, IDragHandler, IEndDragHandler这几个接口,并实现接口的三个方法:
拖拽前处理: public void OnBeginDrag(PointerEventData eventData){ }
拖拽中处理: public void OnDrag(PointerEventData eventData){ }
拖拽后处理: public void OnEndDrag(PointerEventData eventData){ }
备注:继承接口,必须实现接口的全部方法,如果接口方法中这次不会被使用,仍然要实现该方法,但是不用覆写里面的具体代码(弄一个空方法就可以),比如OnEndDrag就里面就可以写个空的。
以下是实现划线的代码:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine.EventSystems; //Unity事件系统命名空间
public class Test : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public GameObject GoLinePrefab; //线预设
public static GameObject Line; //线对象
public RectTransform LineRectTranform; //二维方位
public static Vector3 firstPosition; //起点位置
public static Vector3 secondPosition; //终点位置
public GameObject GoButtonPanel; //按钮面板
void Start()
{
//加载Line
GameObject goLine = GameObject.Instantiate(GoLinePrefab) as GameObject;
Line = goLine;
//设置其父对象
goLine.transform.parent = GoButtonPanel.transform;
}
/// <summary>
/// 拖拽前处理
/// </summary>
/// <param name="eventData"></param>
public void OnBeginDrag(PointerEventData eventData)
{
/* 设置线的第一个点位置 */
Vector3 globalMousePosition; //转换后的鼠标的位置
//屏幕坐标,转二维矩阵坐标
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(LineRectTranform, eventData.position, eventData.pressEventCamera, out globalMousePosition))
{
firstPosition = globalMousePosition; //将鼠标位置赋值给第一个点
}
}
/// <summary>
/// 拖拽中处理
/// </summary>
/// <param name="eventData"></param>
public void OnDrag(PointerEventData eventData)
{
Vector3 globalMousePosition; //转换后的鼠标的位置
//屏幕坐标,转二维矩阵坐标
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(LineRectTranform, eventData.position, eventData.pressEventCamera, out globalMousePosition))
{
if (PasswordHandle.IsEnterArea == false)
{
secondPosition = globalMousePosition; //拖拽时,实时的将鼠标位置赋值给第二个点
}
if (Line != null)
{
//设置position
Line.transform.position = (firstPosition + secondPosition) / 2;//**向量的中点坐标公式,不要说你忘了
//设置rotation
Vector3 vecRotation = (secondPosition - firstPosition); //**向量减法来求方向
Line.transform.eulerAngles = new Vector3(0, 0, Mathf.Atan2(vecRotation.y, vecRotation.x) * Mathf.Rad2Deg);//**这里使用了反三角函数,中学知识
//设置scale
Line.transform.localScale = new Vector3(Mathf.Sqrt(Vector3.Dot(vecRotation, vecRotation)) / 100, 1, 1);
//Vector3.Dot(vecRotation, vecRotation)点乘,此处相当于求vecRotation向量的模,即长度
//这里“/100”是一个坑,调了好久才调出来,除以多少和屏幕尺寸有关,这样线的第二个点才会紧跟着鼠标移动,整条线不会脱离鼠标(比实际画的短或长)
//我设置的屏幕尺寸是600*800.,其他尺寸自己调吧
}
}
}
/// <summary>
/// 拖拽后处理
/// </summary>
/// <param name="eventData"></param>
public void OnEndDrag(PointerEventData eventData)
{
}
}
代码码好后,在UnIty中搭建一个测试场景,建一个Canvas,在其下建一个Panel,把上面写的“Test.cs”脚本挂到Panel上,把提前做好的线预设(我取得名字叫“Line”)拖到Canvas下面,对 GoLinePrefab线预设,LineRectTranform线二维方位(和线预设是同一个对象),GoButtonPanel按钮面板进行拖拽赋值。
最后设置好屏幕尺寸:600*800,就可以测试进行划线了。
能画出第一条线,那么在手势解锁界面,只需要将手势点作为线的第一个点,线的第二个点的位置(在鼠标未碰到下一个手势点前)则为鼠标的实时位置,碰到下一个手势点时,将该点设置为线的第二点,两点连成一条线,然后用Instantiate创建下一条线继续画,然后添加判断逻辑、中间点自动连线逻辑等等,就实现了一个完整的手势解锁。
我实现这个功能有一个坑,就是只有在屏幕尺寸为600*800时,求出第一个点与第二个点的长度除以100得出的商值,并将此值赋值给线的Size,线的尺寸才能完美的匹配鼠标的轨迹,,,,,所以希望大神能给出更好的方法,评论区可以交流交流。