今天不学机器学习,让我们一起搞(zuo)事(you)情(xi)
现在主流的手机就是苹果和安卓,支持的是多点触控,就是可以同时触摸好几个手指头(一般不超过5个),所以我们只需要将每个手指正在做的动作记录下来,并执行相应的响应即可。
让我们看一下Unity中的Touch类,看看它能给我们提供哪些输入信息。
// 摘要:
// Structure describing the status of a finger touching the screen.
// 描述手指触摸屏幕状态的结构。
public struct Touch {
// 摘要:
// Value of 0 radians indicates that the stylus is parallel to the surface,
// pi/2 indicates that it is perpendicular.
// 0弧度的值表示触笔与表面平行,
// pi/2表示它是垂直的。
public float altitudeAngle { get; } // 高度角
//
// 摘要:
// Value of 0 radians indicates that the stylus is pointed along the x-axis
// of the device.
// 0弧度的值表示笔尖沿设备的x轴方向
public float azimuthAngle { get; } // 方位角
//
// 摘要:
// The position delta since last change.
// 自上次变化以来的位置。
public Vector2 deltaPosition { get; } // 增量位置
//
// 摘要:
// Amount of time that has passed since the last recorded change in Touch values.
// 自上次记录的触摸值更改以来已经过的时间。
public float deltaTime { get; } // 时间增量
//
// 摘要:
// The unique index for the touch.
// 独一无二的触摸id。
public int fingerId { get; } // 触摸id,用于识别是哪个指头
//
// 摘要:
// The maximum possible pressure value for a platform. If Input.touchPressureSupported
// returns false, the value of this property will always be 1.0f.
// 平台的最大可能压力值(3D Touch)。
// 如果Input.touchPressureSupported(判断设备平台是否支持3D Touch)返回false,此属性的值始终为1.0f。
public float maximumPossiblePressure { get; } // 最大可能压力值
//
// 摘要:
// Describes the phase of the touch.
// 描述触摸的时期(状态)。
public TouchPhase phase { get; } // 时期(状态)
//
// 摘要:
// The position of the touch in pixel coordinates.
// 触摸在像素坐标中的位置。
public Vector2 position { get; } // 坐标
//
// 摘要:
// The current amount of pressure being applied to a touch. 1.0f is considered
// to be the pressure of an average touch. If Input.touchPressureSupported returns
// false, the value of this property will always be 1.0f.
// 目前施加在触摸上的压力。1.0度被认为是平均接触的压力。
// 如果输入touchPressureSupported返回false,此属性的值始终为1.0f。
public float pressure { get; } // 压力
//
// 摘要:
// An estimated value of the radius of a touch. Add radiusVariance to get the
// maximum touch size, subtract it to get the minimum touch size.
// 一个触摸半径的估计值。添加radius方差来得到最大触摸尺寸,减去它得到最小触摸尺寸。
public float radius { get; } // 半径
//
// 摘要:
// The amount that the radius varies by for a touch.
// 触点半径的变化量。
public float radiusVariance { get; } // 半径变化
//
// 摘要:
// The raw position used for the touch.
// 触摸的原始位置。
public Vector2 rawPosition { get; } // 开始坐标
//
// 摘要:
// Number of taps.
// 点击次数
public int tapCount { get; } // 点击次数
//
// 摘要:
// A value that indicates whether a touch was of Direct, Indirect (or remote),
// or Stylus type.
// 一个表示触摸是否直接、间接(或远程)的值,或手写笔类型。
public TouchType type { get; } // 触摸类型(分为三种,手指头直接按, 程序内部按(触摸精灵什么的), 触摸笔按)
}
好了,知道这些以后,我们就可以将这些信息利用,制作出自己的输入管理器。
我们需要在每隔一段时间的来判断用户的触摸情况,比如把手指按上去,移动,放开等等操作。
void FixedUpdate () {
Touch[] t = Input.touches;
bool flag = false;
foreach(Touch to in t) {
flag = true;
switch (to.phase) {
case TouchPhase.Began: // 刚开始触摸(刚把手指放下去)
beganEvent(to);
break;
case TouchPhase.Canceled:
// 取消触摸
// 这个情况的话,就是系统取消了触摸,非法触摸
// 比如超过5个手指头的情况,或者就是整个手掌拍上去
canceledEvent(to);
break;
case TouchPhase.Ended: // 结束触摸(已经把手指抬起来)
endedEvent(to);
break;
case TouchPhase.Moved: // 移动的
movedEvent(to);
break;
case TouchPhase.Stationary: // 静止的
stationaryEvent(to);
break;
}
}
if (flag) {
// 判断拉伸状态
if (hasUp && hasDown) {
if (upPos.y < downPos.y) {
shrinkEvent();
} else {
stretchEvent();
}
} else if (hasLeft && hasRight) {
if (leftPos.x < rightPos.x) {
shrinkEvent();
} else {
stretchEvent();
}
}
hasUp = false;
hasDown = false;
hasLeft = false;
hasRight = false;
}
}
摩擦摩擦,在光滑的屏幕上摩擦,就会发生滑动事件,我们只需要检测上下左右拉缩即可。
protected virtual void movedEvent(Touch to) {
touchList.Remove(to.fingerId);
float angle = Mathf.Atan2((to.position.y - to.rawPosition.y), (to.position.x - to.rawPosition.x));
if (Mathf.Abs(angle - Mathf.PI / 2.0f) <= shakeAngle) {
hasUp = true;
upPos = to.rawPosition;
upEvent(to, angle);
} else if (Mathf.Abs(angle + Mathf.PI / 2.0f) <= shakeAngle) {
hasDown = true;
downPos = to.rawPosition;
downEvent(to, angle);
}
if (Mathf.Abs(angle - Mathf.PI) <= shakeAngle) {
hasLeft = true;
leftPos = to.rawPosition;
leftEvent(to, angle);
} else if (Mathf.Abs(angle) <= shakeAngle) {
hasRight = true;
rightPos = to.rawPosition;
rightEvent(to, angle);
}
}
我们需要判断长按事件,就是手指按下去,一直按下去的事件。
这里直接将按的时间传进去即可,开发的时候,根据需求自行判断静止时间。
还可以跟滑动组合,比如长按出来选项,滑动出来箭头,在根据touch的当前坐标进行选择。
protected virtual void stationaryEvent(Touch to) {
if (!touchList.ContainsKey(to.fingerId)) {
touchList.Add(to.fingerId, Time.fixedDeltaTime);
} else {
touchList[to.fingerId] += Time.fixedDeltaTime;
}
stationaryEvent(to, touchList[to.fingerId]);
}
其他的就很简单了,主要逻辑还是要根据游戏的不同,实现的功能不同,这里主要统一处理一下各个触控的事件即可。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class InputManager : MonoBehaviour {
private float shakeAngle = Mathf.PI / 2.0f * 0.25f; // 抖动幅度
protected bool hasUp; // 是否向上移动过
private Vector2 upPos; // 向上移动的起始坐标
protected bool hasDown;// 是否向下移动过
private Vector2 downPos; // 向下移动的起始坐标
protected bool hasLeft; // 是否向左移动过
private Vector2 leftPos; // 向左移动的起始坐标
protected bool hasRight; // 是否向右移动过
private Vector2 rightPos; // 向右移动的起始坐标
Dictionary<int, float> touchList = new Dictionary<int, float>();
/*********************************
* 一级事件(会调用二级事件)
* 即直接触发时执行的事件
* 注意重写的时候要先执行原函数
**********************************/
// 触摸开始事件
protected virtual void beganEvent(Touch to) {
touchList.Remove(to.fingerId);
startEvent(to);
}
// 取消触摸事件
protected virtual void canceledEvent(Touch to) {
touchList.Remove(to.fingerId);
endEvent(to);
}
// 结束触摸事件
protected virtual void endedEvent(Touch to) {
touchList.Remove(to.fingerId);
endEvent(to);
}
// 移动触摸事件
protected virtual void movedEvent(Touch to) {
touchList.Remove(to.fingerId);
float angle = Mathf.Atan2((to.position.y - to.rawPosition.y), (to.position.x - to.rawPosition.x));
if (Mathf.Abs(angle - Mathf.PI / 2.0f) <= shakeAngle) {
hasUp = true;
upPos = to.rawPosition;
upEvent(to, angle);
} else if (Mathf.Abs(angle + Mathf.PI / 2.0f) <= shakeAngle) {
hasDown = true;
downPos = to.rawPosition;
downEvent(to, angle);
}
if (Mathf.Abs(angle - Mathf.PI) <= shakeAngle) {
hasLeft = true;
leftPos = to.rawPosition;
leftEvent(to, angle);
} else if (Mathf.Abs(angle) <= shakeAngle) {
hasRight = true;
rightPos = to.rawPosition;
rightEvent(to, angle);
}
}
// 触摸静止事件
protected virtual void stationaryEvent(Touch to) {
if (!touchList.ContainsKey(to.fingerId)) {
touchList.Add(to.fingerId, Time.fixedDeltaTime);
} else {
touchList[to.fingerId] += Time.fixedDeltaTime;
}
stationaryEvent(to, touchList[to.fingerId]);
}
/*********************************
* 会调用二级事件
* 建议非必要,直接重写二级事件即可
**********************************/
public virtual void upEvent(Touch to, float angle) {
}
public virtual void downEvent(Touch to, float angle) {
}
public virtual void leftEvent(Touch to, float angle) {
}
public virtual void rightEvent(Touch to, float angle) {
}
// 拉伸事件
public virtual void stretchEvent() {
}
// 缩小事件
public virtual void shrinkEvent() {
}
// 触摸静止事件
protected virtual void stationaryEvent(Touch to, float time) {
}
// 触摸开始事件
protected virtual void startEvent(Touch to) {
}
// 触摸结束事件
protected virtual void endEvent(Touch to) {
}
// 触控监听
void FixedUpdate () {
Touch[] t = Input.touches;
bool flag = false;
foreach(Touch to in t) {
flag = true;
switch (to.phase) {
case TouchPhase.Began: // 刚开始触摸(刚把手指放下去)
beganEvent(to);
break;
case TouchPhase.Canceled:
// 取消触摸
// 这个情况的话,就是系统取消了触摸,非法触摸
// 比如超过5个手指头的情况,或者就是整个手掌拍上去
canceledEvent(to);
break;
case TouchPhase.Ended: // 结束触摸(已经把手指抬起来)
endedEvent(to);
break;
case TouchPhase.Moved: // 移动的
movedEvent(to);
break;
case TouchPhase.Stationary: // 静止的
stationaryEvent(to);
break;
}
}
if (flag) {
// 判断拉伸状态
if (hasUp && hasDown) {
if (upPos.y < downPos.y) {
shrinkEvent();
} else {
stretchEvent();
}
} else if (hasLeft && hasRight) {
if (leftPos.x < rightPos.x) {
shrinkEvent();
} else {
stretchEvent();
}
}
hasUp = false;
hasDown = false;
hasLeft = false;
hasRight = false;
}
}
}