一、什么是触屏输入?
手机游戏没有鼠标和键盘,玩家唯一的输入设备就是手指。
Unity 把每一次手指触碰屏幕,封装成一个叫做 Touch 的结构体。
Touch 记录了三个核心信息:
- position 触点位置(手指在屏幕哪里)
- phase 触摸阶段(手指现在处于什么状态)
- deltaPosition 移动量(这一帧手指移动了多少)
可以把 Touch 理解为"手指的档案",每根手指有自己的一份档案,随时更新。
二、两个最常用的 API
在 Update() 里,我们用这两个 API 获取触摸信息:
Input.touchCount // 当前屏幕上有几根手指
Input.GetTouch(index) // 获取第 index 根手指的 Touch 数据
touchCount 就像数人头------你得先知道"有没有人",再去拿数据。
GetTouch(0) 中的 0 是索引,代表第一根手指(和数组一样,从0开始)。
三、TouchPhase------触摸的五个阶段
这是触屏输入里最重要的概念。
每根手指在屏幕上的生命周期分为五个阶段:
| 阶段 | 英文枚举 | 触发时机 |
|---|---|---|
| 按下 | TouchPhase.Began |
手指刚放上去的那一帧 |
| 移动 | TouchPhase.Moved |
手指在移动的每一帧 |
| 静止 | TouchPhase.Stationary |
手指按着但没动 |
| 抬起 | TouchPhase.Ended |
手指离开屏幕的那一帧 |
| 中断 | TouchPhase.Canceled |
来电话等系统打断 |
类比:就像开门的过程------推门(Began)→ 门在动(Moved)→ 门停住(Stationary)→ 门关上(Ended)
四、Touch 结构体详解
Touch touch = Input.GetTouch(0);
touch.fingerId // 手指的唯一编号(区分哪根手指)
touch.position // 屏幕像素坐标,Vector2,左下角是(0,0)
touch.deltaPosition // 本帧相对上一帧的位移,Vector2
touch.phase // 当前所处阶段(见上表)
touch.tapCount // 连续点击次数(单击=1,双击=2)
position 的坐标系是这样的:
(0, 1080)------------------------(1920, 1080)
屏幕中心
(0, 0)---------------------------(1920, 0)
注意:触摸坐标原点在左下角,和 Unity UI 的锚点方向一致。
五、脚本实现------点击检测
那么我们手机触屏的基本知识就已经学完啦,接下来我们来运用知识进行实现脚本吧!
目标:手指点下屏幕时,打印触点的屏幕坐标。
using UnityEngine;
public class TapDemo : MonoBehaviour
{
void Update()
{
// 第一步:先判断有没有手指在屏幕上
if (Input.touchCount > 0)
{
// 第二步:取第一根手指的数据
Touch touch = Input.GetTouch(0);
// 第三步:只在"按下的那一帧"响应
if (touch.phase == TouchPhase.Began)
{
Debug.Log("点击位置:" + touch.position);
}
}
}
}
为什么要判断 TouchPhase.Began?
因为手指放在屏幕上不动,Update() 每帧都会执行。如果不判断 Began,只要手指还按着,就会每帧都打印------这不是我们想要的"点击"效果。
Began 只在放下的那一帧为 true,之后变成 Stationary 或 Moved,所以只会触发一次。
六、脚本实现------点击位置移动
6.1 实现思路拆解
在动手写代码之前,先把这个功能拆解成几个小问题:
- 手指点击屏幕 → 获取屏幕坐标(2D像素坐标)
- 屏幕坐标 → 转换成世界坐标(3D空间位置)
- 物体当前位置 → 平滑移动到目标位置
最关键的一步是第2步------屏幕坐标和世界坐标不是一回事:
| 坐标类型 | 单位 | 示例 |
|---|---|---|
| 屏幕坐标(touch.position) | 像素 | (540, 960) |
| 世界坐标(物体 Transform) | Unity单位 | (2.3f, 0, -1.5f) |
Unity 提供了转换函数:Camera.main.ScreenToWorldPoint(),帮我们完成这个换算。
6.2 ScreenToWorldPoint------屏幕转世界坐标
Camera.main.ScreenToWorldPoint(Vector3 position)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
| position.x | float | 触点的屏幕 x 坐标 |
| position.y | float | 触点的屏幕 y 坐标 |
| position.z | float | 距相机的深度,必须手动指定 |
为什么需要 z?因为相机把 3D 世界"拍扁"成了 2D 屏幕,丢失了深度信息。z 就是告诉 Unity:"我要的那个点,在相机前方多远的平面上?"
z 值怎么填?
把物体当前离相机的距离赋给 z,这样转换出来的世界坐标就和物体在同一个深度平面上。
// 先算出物体离相机有多远
float distToCamera = Vector3.Distance(transform.position, Camera.main.transform.position);
// 再传给 ScreenToWorldPoint
Vector3 worldPos = Camera.main.ScreenToWorldPoint(
new Vector3(touch.position.x, touch.position.y, distToCamera)
);
6.3 平滑移动------Vector3.MoveTowards
移动到目标点有两种常见方式:
| 方式 | 函数 | 效果 |
|---|---|---|
| 匀速移动 | Vector3.MoveTowards |
全程速度一致,到达即停 |
| 缓动移动 | Vector3.Lerp |
越接近目标越慢,丝滑感 |
在这里我使用 MoveTowards,更适合"点哪走哪"的手游操控感:
transform.position = Vector3.MoveTowards(
transform.position, // 当前位置
targetPos, // 目标位置
speed * Time.deltaTime // 每帧移动距离
);
transform.position:物体当前所在位置targetPos:我们想让它去的位置(点击处)speed * Time.deltaTime:每帧最多走多远(乘以 deltaTime 保证帧率无关)
6.4 脚本总结
在这里我将展示完整的代码给大家以供参考:
using UnityEngine;
public class ClickMoveDemo : MonoBehaviour
{
public float speed = 5f; // 移动速度
private Vector3 targetPos; // 目标位置
void Start()
{
// 初始目标位置就是自身位置(防止一开始就乱跑)
targetPos = transform.position;
}
void Update()
{
HandleInput(); // 第一步:处理输入,更新目标点
MoveToTarget(); // 第二步:每帧向目标点靠近
}
// ── 输入处理 ──────────────────────────────
void HandleInput()
{
#if UNITY_EDITOR
// 编辑器里用鼠标左键模拟点击
if (Input.GetMouseButtonDown(0))
{
SetTargetFromScreen(Input.mousePosition);
}
#else
// 真机上用触摸
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
SetTargetFromScreen(Input.GetTouch(0).position);
}
#endif
}
// ── 屏幕坐标 → 世界坐标 → 更新目标点 ─────
void SetTargetFromScreen(Vector2 screenPos)
{
// 用物体到相机的距离作为深度值
float distToCamera = Vector3.Distance(
transform.position,
Camera.main.transform.position
);
// 屏幕坐标转世界坐标
Vector3 worldPos = Camera.main.ScreenToWorldPoint(
new Vector3(screenPos.x, screenPos.y, distToCamera)
);
// 保持 y 轴不变(物体在地面上,不要飞起来或陷地里)
worldPos.y = transform.position.y;
targetPos = worldPos;
Debug.Log("新目标点:" + targetPos);
}
// ── 每帧向目标位置移动 ────────────────────
void MoveToTarget()
{
transform.position = Vector3.MoveTowards(
transform.position,
targetPos,
speed * Time.deltaTime
);
}
}
七、在编辑器里怎么测试?
真机触屏没法在 PC 编辑器里直接测,用下面这段代码可以一套脚本,编辑器用鼠标,手机用触摸,自动切换:
void Update()
{
#if UNITY_EDITOR
// 编辑器中用鼠标模拟
if (Input.GetMouseButtonDown(0))
{
Debug.Log("【编辑器】点击:" + Input.mousePosition);
}
#else
// 真机上用触摸
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
Debug.Log("【真机】点击:" + Input.GetTouch(0).position);
}
#endif
}
#if UNITY_EDITOR 是预编译指令 ,Unity 打包到手机时,UNITY_EDITOR 那段代码会被自动剔除,只保留触摸部分。
八、总结
下面我将总结本章的关键函数为表给大家
| 功能 | 关键代码 |
|---|---|
| 检测有无触摸 | Input.touchCount > 0 |
| 点击瞬间 | touch.phase == TouchPhase.Began |
| 抬起瞬间 | touch.phase == TouchPhase.Ended |
| 滑动方向 | Ended时 (终点 - 起点) |
| 坐标转世界坐标 | Camera.main.ScreenToWorldPoint() |
**今天的教学就到这里!**接下来我将连续更新90天的Untiy教程从基础到一个网络部分,有兴趣的朋友们可以收藏关注,谢谢!如果有疑问,评论区见。
成果展示
由于不是真机测试这个博主用的是鼠标模拟屏幕点击,忘记的可以回看Unity学习90天-第2天-认识键盘 / 鼠标输入(PC)并实现WASD 移动,鼠标控制物体转向https://blog.csdn.net/qq_57101277/article/details/160084017?spm=1001.2014.3001.5501
