Unity 操作检测的各种实现#2手机端 - 触屏与手势
写在前面
继续填坑了。这次是讲手机端。
- 电脑端 [ 按我跳转 ]
- 键盘按键控制 [ 上文内容 ]
- 鼠标点击控制 [ 上文内容 ]
- 手机端
- 触屏控制 [ 本文内容 ]
- 手势控制 [ 本文内容 ]
- 重力控制 [ 下文内容 ]
- 陀螺仪控制 [ 下文内容 ]
本文的限制 :接上文,本文主要讲在手机检查操作的方法,至于之后执行的相关命令,读者完全可大开脑洞。如果你有什么好的想法,欢迎告诉我!
正文
1 触屏控制
试想象 神庙逃亡 这类游戏,你能想到些什么操作方式?通常玩家会使用 手指 或 手势 来操控游戏。所谓的手势指的是,类似下划,上划一类的操作方式。
检测触屏,需要用到,由MonoBehaviour提供的OnMouseDown()方法。这个方法在电脑和手机端都可以调用。
例子
void OnMouseDown()
Debug.Log("The Button is pressed"+this.name);
}
这个方法可以检测用户对 GUI Element (比如GUI Texture)和 碰撞体 的 触屏点击 。因此通常这个方法不会放进主程序的Update内,这是因为只要我们给点击相关物件的代码加入这个方法,那么当它被触摸时,就会调用OnMouseDown方法。
Unity Remote可以为电脑和手机提供接口,让游戏画面可以通过Unity的play按钮,在手机上同步呈现。读者可自行到goolg play商店下载安装。
然而,假设你为你的一些GUI button加入了这个方法,并做一些输出之后,你会发现一个问题。如果你试着同时按下两个按钮,会发现什么信息也没有出现,或者只出现其中一个按钮被按下的信息。
绝大部分的游戏都需要 多点触控 ,所以上面的方法显然还行不通。
先来了解 多点触控 的概念。看图
当玩家的手触碰到手机屏幕时,Unity便会触发 触碰事件 ,我们將手指与屏幕触碰过程中的所有资讯称为 触控信息 。Unity会为我们识别每一个触控信息所携带的ID、触控点位置、状态、点击次数等资讯。
聪明的你或许已经注意到了,每个触控点都会有一个属于它们的状态。当我们第一次触碰到屏幕时,第一笔的触控信息会被记录,并且设定状态为 Begin 。而当我们在继续触碰的情况下滑动手指,触控点的状态便更改为 Moved 。如果我们只是按住而没有滑动的动作,状态便设定为 Stationary 。当我们把手指离开屏幕,状态变为 Ended 。当然,即使我们的手指已经离开屏幕,Unity仍然会保留这笔状态一段时间,这是为了可以让那些短时间内再次触碰的动作减少资源消耗。当我们再次触碰屏幕,Unity便会再一次追踪触控信息。每次成功的触控追踪都会被记录为 触控事件 ,并且让资料中的点击次数加一。
如果玩家长时间不碰屏幕,或手机已经离开并触碰屏幕的其他地方。由于系统追踪不到原先的触控点,便会将该点的状态记录为 Canceled 。值得注意的是,在状态 Moved 中,触控信息会记录额外的位移与时间信息,供开发者作 手势判别 。这是我们下一个讨论内容,这里就先介绍到这。
上文提到的关于 触控信息 的内容,在Unity中会被存储在Touch类中,读者可前往文档详细阅读。
另外,触控点的状态则定义在TouchPhase类的结构中。这是一个Enum类别的数据。有Began, Moved, Stationary, Ended, Canceled五个状态。
小实践:多点触控
为了做到这一点,我们需要抛弃OnMouseDown这个方法,改而在游戏刷新的过程中不断检测触控的数量,根据触控的次数来作对应的操作命令
void Update(){
int touchCount = Input.touchCount; // 获取触控点个数
if (touchCount > 0){ // 若玩家有触碰到屏幕
for (int i = 0 ; i < touchCount ; i++ ){
Touch touch = Input.GetTouch(i); // 将触控点存储
if (this.getTexture.HitTest(touch.position)){
if (this.name == "Forward"){
Debug.Log("Forward");
else if (this.name == "Jump"){
Debug.Log("Jump!");
} // 若触控点有碰到按钮,则判断是按下了哪个按钮
}
接下来,你可以把这段代码挂到任何一个需要使用到触控检测的GUI element上。HitTest()是GUI element的方法,你可以将触控点的位置传入来作检测。如果触控点在GUI element的范围内,则回传true,反之回传false。
一些问题和可行的解决方案
当你试着实现多点触控时,你会发现一些事情,如果你一直按住某个按钮。比如Jump键,那么console信息栏中就会一直输出"Jump!"的信息。换句话说,按钮事件会一直处在被触发的状态。这好说,因为手指一直在按钮上,而这段代码则被写在Update中,那么每一帧都会执行这端代码,当然信息会一直输出了。这时读者可以使用上文提到的TouchPhase类进行点击状态的判断,进而减轻系统输出负担。
2 手势检测
多点触控的用处很多,手势的侦测就是其中一种。
当我们游玩神庙逃亡时,如果我们手指向上滑动,那么角色就会做出一个跳跃的动作。当然这里很可能是没有实现多点触控的,笔者只是想让读者思考手势检测的例子。
延申前文所提到的 光线投射 的概念,在结合本文的 触控检测 概念,就可以写出很美妙的手势操作判断了。笔者假设读者需要开发有多点触控功能的应用,因此延续上文的代码。
void Update(){
int touchCount = Input.touchCount;
Camera cam = Camera.main;
if (touchCount > 0){
for (int i = 0 ; i < touchCount ; i++ ){
Touch touch = Input.GetTouch(i);
if (touch.phase == TouchPhase.Bagan)){ // 如果触控点状态为按下
Ray ray = cam.ScreenPointToRay(touch.position); // 从触控点位置产生一条Ray
RaycastHit hit; // 用来检测碰撞的Ray
if (Physics.Raycast(ray, out hit))
Debug.Log(hit.transform.name); // 输出点击物体名称
}
你可能会觉得,这不就是一个【按下】的功能嘛!说好的手势判断呢!哎先别打我啊,我们来看看程式设计的概念:如果玩家的手碰到了屏幕,再做一个向上滑动的手势后,就向上跳。设计手势时,可以善用触控点的状态资讯,就是TouchPhase。
我们可以把这个概念写作这样的流程:
- 玩家触碰到屏幕 - 触控点状态:TouchPhase.Began
- 玩家向上滑动 - 触控点状态:TouchPhase.Moved
- 玩家手指离开 - 触控点状态:TouchPhase.Ended
好的,代码可以写成这样:
...
int touchCount = Input.touchCount;
Camera cam = Camera.main;
if (touchCount > 0){
for (int i = 0 ; i < touchCount ; i++ ){
Touch touch = Input.GetTouch(i);
if (touch.phase == TouchPhase.Bagan)){ // 如果触控点状态为按下
else if (touch.phase == TouchPhase.Moved)){ // 如果触控点状态为移动
else if (touch.phase == TouchPhase.Ended)){ // 如果触控点状态为离开屏幕
}
读者可以看到,现在的代码被分成了三个区块,分别处理按下、移动和离开屏幕后的对应事件。由于我们提到的例子只需要检测向上划与否,所以处理按下时,只需检查玩家是否按到游戏内的碰撞体,并记录位置,方便计算即可。
按下的处理:
Vector2 startTouchPos; // pre-defined
Camera cam = Camera.main; // pre-defined
if (touch.phase == TouchPhase.Bagan)){ // 如果触控点状态为按下
Ray ray = cam.ScreenPointToRay(Input.position);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
Debug.Log(hit.transform.name); // 输出点击物体名称
startTouchPos = touch.position;
...
移动的处理:
...
if (touch.phase == TouchPhase.Moved)){ // 如果触控点状态为按下
if ( touch.position.y - startTouchPos.y > 0)
Debug.Log("Jump!");