ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

UE4/UE5触摸屏手势交互实战:单指拖拽与双指缩放旋转方案

UE4/UE5触摸屏手势交互实战:单指拖拽与双指缩放旋转方案 简介这份资源面向使用UE4/UE5开发触屏交互的开发者聚焦触摸屏上的移动、旋转、缩放三类手势事件处理。内容围绕触摸开始、移动、结束等事件类型展开讲解如何通过UUserWidget重写触摸回调、注册触摸处理函数并在蓝图中借助TouchInputComponent管理多点触摸利用双指距离与角度变化实现平移、旋转与缩放同时涉及防抖动与输入刷新率等优化思路。资源包共349个文件以288个uasset资产、6个umap关卡、34个ini配置为主另含少量png、bin、json、log及工程文件压缩包约610.21MB可直接导入引擎查看与复用。目前已有6432人学习下载适合希望快速掌握触屏手势交互、对照工程结构查漏补缺的中级开发者参考。1. 触摸屏上的 UE4/UE5 手势交互从单指拖拽到双指缩放一套能落地的输入方案在展厅大屏、车载中控、工业 HMI 这类触摸屏项目里用 UE4 或 UE5 做交互时最常被问到的一件事就是怎么让模型跟着手指移动、旋转、缩放很多人第一反应是去 Enhanced Input 里找现成的触摸事件结果发现单指能拖、双指就乱旋转和缩放互相打架。这个标题讲的正是这套东西——在触摸屏上把移动、旋转、缩放三个手势拆开、识别、映射到 Actor 变换上。它适合做数字孪生、展馆互动、触控一体机的开发者尤其是那些已经会用蓝图但被多点触控坑过的人。下面我按自己踩过的顺序把原理、蓝图节点、参数和翻车点讲清楚。2. 触摸输入在 UE 里到底是怎么进来的从 Finger Index 到手势判定2.1 触摸事件和鼠标事件不是一回事UE 的输入系统里触摸Touch和鼠标Mouse走的是两套分发逻辑。鼠标只有一个指针触摸可以有十个 Finger Index。你在 Project Settings → Input 里看到的Touch 1、Touch 2这些 Action Mapping本质上是把某个手指的按下/抬起绑定成事件但它不告诉你手指在屏幕上的坐标变化趋势。真正做手势得用InputTouch系列事件或者Get Input Touch State节点去轮询。常见做法是在 PlayerController 或 Pawn 的蓝图里用InputTouch事件拿到FingerIndex和Location然后自己维护一个手指状态数组。为什么不用现成的Touch 1映射因为映射只能告诉你“第一根手指按下了”但双指缩放需要同时知道两根手指的实时坐标映射做不到。提示UE5 的 Enhanced Input 对触摸的支持在 5.1 之后才比较完整但很多触摸屏项目仍然跑在 UE4.27 上所以下面给的方案是两版通用的。2.2 手指索引和屏幕坐标的获取先看一段蓝图逻辑对应的伪代码方便理解数据流。实际在蓝图里是用节点连的但写成代码更好说清参数// 在 PlayerController 的 Tick 或 InputTouch 事件中 TArrayFTouchState ActiveTouches; void OnTouchStart(int32 FingerIndex, FVector2D ScreenPos) { FTouchState State; State.Index FingerIndex; State.StartPos ScreenPos; State.LastPos ScreenPos; State.Delta FVector2D::ZeroVector; ActiveTouches.Add(State); } void OnTouchMove(int32 FingerIndex, FVector2D ScreenPos) { for (auto T : ActiveTouches) { if (T.Index FingerIndex) { T.Delta ScreenPos - T.LastPos; // 本帧位移 T.LastPos ScreenPos; break; } } }这段逻辑的关键参数是Delta它代表这一帧手指移动了多少像素。移动、旋转、缩放全都基于这个 Delta 和两根手指之间的距离变化来算。StartPos用来判断是点击还是拖拽一般位移超过 10 像素才认为是拖拽否则算点击。2.3 单指、双指、三指的分工在触摸屏交互里我一般这样分配手指数手势映射操作单指拖拽平移 Actor双指同向拖拽平移更稳双指捏合/张开缩放改 Scale双指旋转旋转改 Yaw三指预留重置视角这里有个坑双指同时做缩放和旋转时两个计算会互相干扰。常见做法是给缩放和旋转各设一个阈值比如距离变化超过 5% 才触发缩放角度变化超过 2 度才触发旋转避免手指轻微抖动就乱转。3. 把屏幕位移映射到世界坐标移动和旋转的换算逻辑3.1 屏幕 Delta 转世界位移屏幕坐标是二维的世界坐标是三维的。单指拖拽时你希望物体在平行于屏幕的平面上移动。最直接的做法是用Deproject Screen to World节点把手指的屏幕坐标转成世界空间的一条射线然后和物体所在平面求交。// 蓝图里对应Deproject Screen to World - Line Plane Intersection FVector WorldOrigin, WorldDirection; UGameplayStatics::DeprojectScreenToWorld(PlayerController, ScreenPos, WorldOrigin, WorldDirection); // 假设物体在 ZObjectZ 的平面上移动 float t (ObjectZ - WorldOrigin.Z) / WorldDirection.Z; FVector WorldPoint WorldOrigin WorldDirection * t;参数说明ObjectZ是物体当前的世界 Z 坐标。如果物体不在水平面上移动比如在墙面上那就得换成用物体法线做平面。t是射线参数如果WorldDirection.Z接近 0说明射线平行于平面这时候要跳过这一帧否则会算出无穷大。移动量就是WorldPoint - LastWorldPoint然后把这个增量加到 Actor 的 Location 上。注意不要直接 Set Location而是用AddActorWorldOffset这样不会破坏物体的旋转。3.2 双指旋转的角度计算双指旋转的核心是算两根手指连线在屏幕上的角度变化。用Atan2求角度FVector2D Dir TouchB.LastPos - TouchA.LastPos; float CurrentAngle FMath::Atan2(Dir.Y, Dir.X); // 弧度 float DeltaAngle CurrentAngle - LastAngle; // 转成角度 float DeltaDegrees FMath::RadiansToDegrees(DeltaAngle);这里有个玄学问题Atan2的返回值在 -180 到 180 之间当角度跨过 ±180 边界时会跳变。血泪经验是如果DeltaAngle的绝对值大于 180 度要手动修正比如减去 360 或加上 360。否则物体会突然转一大圈。修正后把DeltaDegrees加到 Actor 的 Yaw 上。如果物体需要绕其他轴转就改对应的 Rotator 分量。3.3 双指缩放的比例因子缩放最简单用两根手指当前距离除以上一帧距离float CurrentDist FVector2D::Distance(TouchA.LastPos, TouchB.LastPos); float LastDist FVector2D::Distance(TouchA.PrevPos, TouchB.PrevPos); float ScaleFactor CurrentDist / LastDist;然后NewScale OldScale * ScaleFactor。但要注意缩放应该以两根手指的中点为锚点而不是物体原点。如果直接改 Scale物体会以自身原点缩放手指中点如果不在物体中心看起来就会“跑偏”。解决办法是同时调整 Location让中点对应的世界点保持不动。这个计算稍微复杂但效果差别很大。注意缩放因子要 Clamp比如限制在 0.5 到 2.0 之间防止用户一下子把物体缩到看不见或者撑爆场景。4. 蓝图落地一套可复用的触摸手势组件4.1 创建 ActorComponent 管理触摸状态不要把所有逻辑塞进 Level Blueprint 或 Pawn 里。我一般建一个TouchGestureComponent挂在需要交互的 Actor 上。组件里维护ActiveTouches数组在TickComponent里处理手势判定。// TouchGestureComponent.h 关键成员 UPROPERTY() TArrayFTouchState ActiveTouches; UPROPERTY(EditAnywhere, CategoryGesture) float DragThreshold 10.0f; // 像素 UPROPERTY(EditAnywhere, CategoryGesture) float RotateThreshold 2.0f; // 度 UPROPERTY(EditAnywhere, CategoryGesture) float ScaleThreshold 0.05f; // 比例这些阈值暴露到 Details 面板不同项目可以调。展厅大屏手指动作大阈值可以设高一点手机屏幕小阈值要低。4.2 手势判定的状态机在TickComponent里根据ActiveTouches.Num()分支0 个手指重置所有 Last 状态1 个手指进入拖拽模式算世界位移2 个手指算距离和角度分别和阈值比较决定是否缩放/旋转void UTouchGestureComponent::TickComponent(float DeltaTime, ...) { if (ActiveTouches.Num() 1) { HandleSingleDrag(); } else if (ActiveTouches.Num() 2) { HandlePinchAndRotate(); } }HandleSingleDrag里用 Deproject 算世界点HandlePinchAndRotate里算距离和角度。每个函数只做一件事方便单独调试。4.3 把变换应用到 Actor算出来的位移、角度、缩放最后统一应用到 Owner ActorOwner-AddActorWorldOffset(WorldDelta); Owner-AddActorWorldRotation(FRotator(0, DeltaDegrees, 0)); Owner-SetActorScale3D(Owner-GetActorScale3D() * ScaleFactor);注意AddActorWorldRotation接受的是 Rotator不是角度值。如果只绕 Yaw 转就构造FRotator(0, DeltaDegrees, 0)。缩放用SetActorScale3D直接设但记得乘上当前 Scale否则会重置。提示如果 Actor 有物理模拟直接改 Transform 会和物理打架。触摸交互的物体一般关掉 Simulate Physics或者用 Kinematic 模式。5. 避坑与排查触摸屏手势最常见的 5 个翻车现场5.1 双指缩放时物体跳动现象两根手指捏合物体不是平滑缩放而是一跳一跳的。原因LastDist没有每帧更新或者更新时机不对。如果只在 TouchMove 事件里更新而 TouchMove 的触发频率和 Tick 不一致就会累积误差。解决在 Tick 里统一更新LastDist和LastAngle不要依赖事件触发。每帧算完手势后把当前值存为 Last。5.2 旋转方向反了现象手指顺时针转物体逆时针转。原因屏幕坐标的 Y 轴向下世界坐标的 Y 轴可能向前Atan2的符号和旋转方向不匹配。解决在DeltaDegrees前面加负号或者交换Atan2的参数顺序。这个没有统一答案和项目坐标系有关试一次就知道了。5.3 单指拖拽时物体飞出屏幕现象手指快速滑动物体瞬间消失。原因Deproject算出的世界点如果射线和平面夹角很小t会非常大导致位移巨大。解决给WorldDelta加一个最大长度限制比如WorldDelta.GetClampedToMaxSize(100.0f)。另外检查WorldDirection.Z的绝对值小于 0.1 就跳过这一帧。5.4 触摸事件在 UI 上被拦截现象手指点在按钮上物体也跟着动。原因UE 的 UI 会消费触摸事件但如果你在 PlayerController 里直接读Get Input Touch State它不管 UI 是否消费。解决用InputTouch事件而不是轮询或者在轮询前判断IsOverGameObject。更稳妥的做法是把触摸交互做成 UI 的一部分用WidgetInteraction组件。5.5 多指触摸时 Finger Index 复用现象三根手指同时按抬起一根后再按索引乱了。原因UE 的 Finger Index 是复用的抬起后可能分配给新手指。解决不要用 Finger Index 作为唯一标识用StartPos或者自己生成一个 ID。在 TouchStart 时分配 IDTouchEnd 时移除。6. 进阶用触摸手势驱动 3D UI 和相机轨道6.1 触摸手势控制相机环绕上面讲的是移动物体但很多场景是物体不动、相机动。把同样的手势逻辑映射到 SpringArm 上单指拖拽改 SpringArm 的 Yaw/Pitch双指缩放改 SpringArm 的 TargetArmLength。注意 Pitch 要 Clamp 在 -80 到 80 度之间防止翻转。// 相机环绕 SpringArm-AddRelativeRotation(FRotator(0, DeltaDegrees, 0)); SpringArm-TargetArmLength FMath::Clamp( SpringArm-TargetArmLength * ScaleFactor, 100.0f, 1000.0f);6.2 手势和 3D UI 的交互UE5 的 3D UIWidget Component在触摸屏上有个模糊问题尤其是缩放后文字发虚。这是因为 Widget Component 的渲染分辨率没跟上。解决办法是在缩放时动态调整WidgetComponent-SetDrawSize或者用Retainer Box提高渲染质量。这个坑在展厅项目里很常见调一次能省半天返工。6.3 验证手势是否跟手最后说一个我自己的习惯在屏幕上画调试线。用Draw Debug Line把两根手指的连线画出来再把计算出的旋转中心点画个球。这样一眼就能看出手势识别对不对。别小看这个很多“玄学”问题都是因为手指坐标没取对。我一般会在项目里留一个bDebugTouch开关打包时关掉编辑器里打开。这套方案我在几个触摸屏项目里跑过从 55 寸到 86 寸都稳。核心就一句话别依赖现成映射自己维护手指状态每帧算 Delta阈值给够。希望帮到你。本文还有配套的精品资源点击获取
返回列表