在移动平台上开发 3D 游戏或可视化应用时,良好的相机交互体验至关重要。相机不仅是用户观察世界的窗口,更是交互流畅性和沉浸感的重要保障。
本文将基于一个实战级 Unity 脚本,从原理到实现,逐步拆解移动端相机控制的关键要素。内容涵盖:
文章适用于中高级 Unity 开发者,也适合希望深入理解移动交互与相机控制机制的开发者学习参考。
在移动端相机控制中,用户的手指就是操控相机的摇杆。我们希望实现以下交互方式:
基于此目标,我们来逐步构建完整的系统。
整个控制器封装在一个脚本类 MobileCameraController
中。该类主要负责:
SmoothDamp
和 Slerp
实现平滑移动和旋转我们先来看下初始化过程:
void Start()
{
targetPosition = transform.position;
targetRotation = transform.rotation;
}
这里初始化相机的目标位置和旋转,使得后续每一帧都以此为参照进行插值。
核心逻辑位于 Update()
函数中:
void Update()
{
if (Input.touchCount == 2)
{
HandleMobileZoom(); // 双指缩放
isDragging = false;
}
else if (Input.touchCount == 1)
{
HandleSingleTouch(); // 单指拖动
}
// 平滑过渡到目标位置与角度
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime / smoothTime);
}
这里通过 Input.touchCount
判断是一个手指还是两个手指操作,从而决定是移动还是缩放。之后通过内置的 SmoothDamp
与 Slerp
实现平滑过渡。
单指拖动逻辑封装在 HandleSingleTouch()
方法中,分三个阶段处理:按下、移动、抬起。
case TouchPhase.Moved:
if (isDragging)
{
// 计算标准化后的拖动距离
Vector2 delta = touch.position - lastTouchPosition;
Vector2 normalizedDelta = delta / screenDiagonal;
// 基于当前高度动态调整移动速度
float heightFactor = Mathf.Log(transform.position.y / minZoomHeight + 1f);
float dynamicSpeed = moveSpeed * heightFactor;
// 应用相机朝向,构建移动向量
float moveX = -normalizedDelta.x * dynamicSpeed;
float moveZ = normalizedDelta.y * dynamicSpeed;
Vector3 right = transform.right;
Vector3 forward = -transform.forward;
forward.y = 0;
Vector3 horizontalMovement = right * moveX;
Vector3 forwardMovement = forward * moveZ;
targetPosition += horizontalMovement + forwardMovement;
lastTouchPosition = touch.position;
}
由于不同设备的屏幕分辨率不同,我们将手指的位移除以屏幕对角线长度进行归一化,保证在不同尺寸设备上的操作一致性。
在高视角时,用户希望更快地移动画面;在低视角时,移动应更细腻。因此通过当前相机高度对速度进行对数变换,实现非线性自适应调整。
-transform.forward
?因为 Unity 中相机的 forward
是朝前(屏幕外),而我们通常期望“手指向上滑动,画面向前推进”,因此取负方向。
我们希望双指缩放不仅改变高度,还能够渐变相机角度。这个特性对于 3D 空间交互尤为重要。
float newHeight = targetPosition.y - normalizedDelta * zoomSensitivity * 100f;
newHeight = Mathf.Clamp(newHeight, minZoomHeight, maxZoomHeight);
targetPosition = new Vector3(targetPosition.x, newHeight, targetPosition.z);
这里是通过手指之间距离变化的差值,推算出新的相机高度,并限制在设定范围内。
接着我们插值旋转:
float t = Mathf.InverseLerp(minZoomHeight, maxZoomHeight, newHeight);
targetRotation = Quaternion.Slerp(zoomInRotation, zoomOutRotation, t);
使用 InverseLerp
将当前高度转换为 0~1 的比例因子,再用它在两个预设角度之间插值,从而实现“缩小时仰角大,放大时仰角小”的自然视角过渡。
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
SmoothDamp
是 Unity 中非常实用的平滑插值函数,适用于实现类似“渐近收敛”的平移效果。相比 Lerp 它不会卡在目标点附近。
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime / smoothTime);
Slerp
(球形线性插值)则用于实现四元数之间的平滑旋转,适用于相机这种需要自然变换方向的场景。
移动端相机控制不仅关乎操作的实现,更关系到用户体验的细节打磨。从本篇内容我们可以看到,仅凭单指拖动与双指缩放这两种基础手势,通过合理设计参数、动态调整逻辑以及引入插值平滑机制,就可以构建出一个高质量、专业感十足的相机控制系统
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。