Unity中如何实现GameObject在透视与正交相机间无缝切换显示?
问题
开发类跳棋棋盘游戏时遇到以下场景与问题:
- 主相机为透视模式,移除棋子需拖拽至屏幕底部的已移除棋子栈区域,随后动画移动并旋转至栈内正确位置
- 棋子栈位于独立层,仅正交相机可见(透视模式下显示效果差)
- 切换棋子所在层时,屏幕位置会跳变(甚至移出屏幕),视觉旋转也会大幅改变
- 需求:切换层时棋子视觉位置与旋转完全不变,之后无缝动画至栈内目标位置与旋转
解决方案
核心思路是:将棋子在透视相机下的视口坐标转换为正交相机下的世界坐标,同时将棋子相对于透视相机的局部旋转映射到正交相机视角下,确保视觉上无断层。
修改ChangeOver()方法后的完整代码如下:
public class Stack : MonoBehaviour { [SerializeField] private Camera perspectiveCamera, orthogonalCamera; [SerializeField] private Vector3 startPosition; [SerializeField] private Vector3 startRotation; [SerializeField] private float duration; public void AddToStack(Transform piece) { piece.transform.SetParent(transform); // 切换层并保持视觉位置/旋转不变 ChangeOver(piece); StartCoroutine(LerpBetweenPositionsAndRotations(piece)); } private void ChangeOver(Transform piece) { // --- 位置转换:保持屏幕位置不变 --- // 将棋子世界坐标转为透视相机的视口坐标(不受屏幕分辨率影响) Vector3 viewportPos = perspectiveCamera.WorldToViewportPoint(piece.position); // 获取棋子栈到正交相机的深度,确保棋子与栈处于同一平面 float targetDepth = orthogonalCamera.WorldToViewportPoint(transform.position).z; // 将视口坐标转为正交相机下的世界坐标,保证屏幕位置一致 Vector3 newWorldPos = orthogonalCamera.ViewportToWorldPoint( new Vector3(viewportPos.x, viewportPos.y, targetDepth) ); piece.position = newWorldPos; // --- 旋转转换:保持视觉旋转不变 --- // 计算棋子相对于透视相机的局部旋转(剥离相机自身旋转影响) Quaternion relativeRotToPerspective = Quaternion.Inverse(perspectiveCamera.transform.rotation) * piece.rotation; // 将该相对旋转映射到正交相机视角,得到新的世界旋转 piece.rotation = orthogonalCamera.transform.rotation * relativeRotToPerspective; // 记录动画起始的局部位置与旋转 startPosition = piece.localPosition; startRotation = piece.localRotation.eulerAngles; // 切换棋子到栈所在层(仅正交相机可见) piece.gameObject.layer = gameObject.layer; } private IEnumerator LerpBetweenPositionsAndRotations(Transform piece) { float elapsedTime = 0f; // 提前计算目标值,避免循环内重复计算 Vector3 targetLocalPos = new Vector3(transform.childCount - 1, 0f, 0f); Quaternion targetLocalRot = Quaternion.Euler(0f, 0f, 0f); while (elapsedTime < duration) { float t = elapsedTime / duration; piece.localPosition = Vector3.Lerp(startPosition, targetLocalPos, t); piece.localRotation = Quaternion.Lerp(Quaternion.Euler(startRotation), targetLocalRot, t); elapsedTime += Time.deltaTime; yield return null; } // 确保最终状态完全匹配目标值 piece.localPosition = targetLocalPos; piece.localRotation = targetLocalRot; } }
关键逻辑说明
位置转换:
- 使用视口坐标而非屏幕坐标,适配任意分辨率
- 对齐棋子与栈的深度,避免正交相机下的位置偏移
- 通过视口坐标的跨相机转换,保证屏幕视觉位置完全一致
旋转转换:
- 先计算棋子相对于透视相机的局部旋转,剔除相机自身旋转的干扰
- 将该相对旋转映射到正交相机视角,确保视觉朝向无变化
动画优化:
- 提前计算目标位置与旋转,减少循环内的性能开销
- 使用
while循环更精准地控制动画时长
内容的提问来源于stack exchange,提问作者Eddie Domínguez
相关产品推荐
相关产品推荐

