美文网首页Unity分享征服Unity3dunity3D技术分享
Unity透视相机下地图边界处理-海岛奇兵4

Unity透视相机下地图边界处理-海岛奇兵4

作者: 云木unity | 来源:发表于2017-11-08 15:05 被阅读185次

    本文前篇 Unity透视相机下场景移动缩放-海岛奇兵3
    接着前篇继续写。

    主要记录以下内容:

    1. 边界处理

    2. 计算视口宽高和查看视口工具 GeWenL/CameraView

    3. Move操作优化:

      1. 触摸点跟着手指Move
      2. 在边界的移动,拆分移动的X和Y,忽略越界方向,仅保留合法的轴;
    4. Zoom缩放操作优化:在边界的向内放大(放大时,向内移动,不超出边界)

    边界处理

    地图边界示意图.png

    用屏幕四个角来检测四个边界;
    LeftOutBoundary:左边是否越界;
    RightOutBoundary:右边是否越界;
    DownOutBoundary:下边是否越界;
    UpOutBoundary:上边是否越界;

    为什么要用四个变量来记录每个边界是否越界呢?

    用于Move和Zoom缩放在边界时的操作优化。
    在已经检测越界的情况下,进行Move(有边界方向的分量)和Zoom缩放操作必定会判定为非法,从而舍弃掉这次的操作。这样的体验并不好。
    详细优化操作见3 、4节;Move、Zoom操作初篇见 Unity透视相机下场景移动缩放-海岛奇兵3

    private List<Vector2> ScreenCornerPosList = new List<Vector2> { Vector2.zero
        , new Vector2(0, Screen.height)
        , new Vector2(Screen.width, Screen.height)
        , new Vector2(Screen.width, 0) };
    
    // 检测边界 是否合法 true合法  false越界非法 
    private bool CheckBoundary()
    {
        LeftOutBoundary = false;
        RightOutBoundary = false;
        DownOutBoundary = false;
        UpOutBoundary = false;
        if (ScreenCornerPosList != null)
        {
            foreach (var screenPos in ScreenCornerPosList)
            {
                Ray ray = _cameraMain.ScreenPointToRay(screenPos);
                var hits = Physics.RaycastAll(ray, 1000);
                if (hits == null || hits.Length <= 0)
                {
                    continue;
                }
                for (var i = 0; i < hits.Length; ++i)
                {
                    var go = hits[i].collider.gameObject;
                    if (go.layer == Const.Lay_MapBorder)
                    {
                        switch (go.name)
                        {
                            case "left":
                                LeftOutBoundary = true;
                                break;
                            case "right":
                                RightOutBoundary = true;
                                break;
                            case "up":
                                UpOutBoundary = true;
                                break;
                            case "down":
                                DownOutBoundary = true;
                                break;
                            default:
                                break;
                        }
                    }   
                }
            }
        }
        return !(LeftOutBoundary || RightOutBoundary || DownOutBoundary || UpOutBoundary);
    }
    

    计算视口宽高和查看视口工具

    可以直观的看到视口离边界的距离


    绘制视口区域.png
    视口script.png

    GitHub完整CameraView.cs地址: GeWenL/CameraView
    关键部分代码 求距离相机distance的视口宽高:(这个计算会在Zoom缩放操作优化中使用)

    计算透视相机视口宽高示意图.png
    Vector3[] GetCorners(float distance)
    {
        ...
        float halfFOV = (theCamera.fieldOfView * 0.5f) * Mathf.Deg2Rad;
        float aspect = theCamera.aspect;
    
        float height = distance * Mathf.Tan(halfFOV);
        float width = height * aspect;
        ...
    }
    

    Move操作优化

    1. 触摸点跟着手指Move
      相机X移动值 = 手指滑动的距离X / 屏幕宽度 * 相机视口宽度
      相机Z移动值 = 手指滑动的距离Y / 屏幕高度 * 相机视口高度
      这样能保证,相机移动前后,手指在地图上触摸到的物体 保持不变。
      且相机在进行缩放操作之后,移动手感一致。比如地图缩小后,视口宽度、高度变大,移动距离按比例变大。

       _halfFOVTan = Mathf.Tan((_OriginalFov * 0.5f) * Mathf.Deg2Rad);
       float hight = GetCameraDis() * _halfFOVTan * 2;// 参照图片(计算透视相机视口宽高示意图)
       float width = hight * _cameraMain.aspect;
      
       _IncreMoveVector.x = -swipeVector.x / Screen.width * width;
       _IncreMoveVector.z = -swipeVector.y / Screen.height  * hight;
      
    2. 在边界的移动,拆分移动的X和Y,忽略越界方向,仅保留合法的轴;

      1. 当右边越界(RightOutBoundary),且玩家还向左滑动(_IncreMoveVector.x > 0)
        或者左边越界(LeftOutBoundary),且玩家还向右滑动(_IncreMoveVector.x < 0)

      舍弃这次玩家滑动的X分量,上下滑动的值保留;这样斜着滑动时,地图还能上下滑动,不会完全舍弃。

       if ((_IncreMoveVector.x > 0 && RightOutBoundary) || (_IncreMoveVector.x < 0 && LeftOutBoundary))
       {
           _IncreMoveVector.x = 0;
       }
      
      1. 当上边越界(UpOutBoundary),且玩家还向下滑动(_IncreMoveVector.y > 0)
        或者下边越界(DownOutBoundary),且玩家还向上滑动(_IncreMoveVector.y < 0)

      舍弃这次玩家滑动的上下分量,左右滑动的值保留;这样斜着滑动时,地图还能左右滑动,不会完全舍弃。

       if ((_IncreMoveVector.z > 0 && UpOutBoundary) || (_IncreMoveVector.z < 0 && DownOutBoundary))
       {
           _IncreMoveVector.z = 0;
       }
      

    Move操作代码如下:

    // 移动摄像机
    // swipeVector.x > 0 向右 swipeVector.x < 0 向左
    // swipeVector.y > 0 向上 swipeVector.y < 0 向下
    private void Move(Vector2 swipeVector)
    {
        if (swipeVector == Vector2.zero)
        {
            return;
        }
    
        float hight = GetCameraDis() * _halfFOVTan * 2;
        float width = hight * _cameraMain.aspect;
    
        _IncreMoveVector.x = -swipeVector.x / Screen.width * width;
        _IncreMoveVector.z = -swipeVector.y / Screen.height  * hight;
        if ((_IncreMoveVector.x > 0 && RightOutBoundary) || (_IncreMoveVector.x < 0 && LeftOutBoundary))
        {
            _IncreMoveVector.x = 0;
        }
        if ((_IncreMoveVector.z > 0 && UpOutBoundary) || (_IncreMoveVector.z < 0 && DownOutBoundary))
        {
            _IncreMoveVector.z = 0;
        }
    }
    

    Zoom缩放操作优化

    缩小是指地图缩小,等同于相机拉远,视口放大。

    1. 当左右或上下 视口同时越界,则说明地图已缩小到最小,不能再缩小。
      若此次操作是缩小,则return;
    2. 一边越界,或2条相邻的边越界,则在缩小的同时,向内移动相机。调用ZoomSetMove函数;
      计算视口放大前后,视口宽高的差异;
    ZoomSetMove.png
    // 摄像机拉近拉远 
    // deltaPinch > 0 为放大 - 由内向外
    // deltaPinch < 0为缩小  - 由外向内
    private void Zoom(float deltaPinch)
    {
        //Debug.Log("Zoom deltaPinch=" + deltaPinch);
        if (deltaPinch < 0 && ((RightOutBoundary && LeftOutBoundary) || (UpOutBoundary && DownOutBoundary)))
        {
            _IncreCameraDis = 0;
            return;
        }
        ... 
        if (_IncreCameraDis > 0 && (RightOutBoundary || LeftOutBoundary || UpOutBoundary || DownOutBoundary))
        {
            ZoomSetMove();
        }
    }
    
    private void ZoomSetMove()
    {
        _IncreMoveVector = Vector3.zero;
        float aspect = _cameraMain.aspect;
        float halfDiff = _IncreCameraDis * _halfFOVTan * 1.1f;
        if (LeftOutBoundary)
        {
            _IncreMoveVector.x = halfDiff * aspect;
        }
        if (RightOutBoundary)
        {
            _IncreMoveVector.x = -halfDiff * aspect;
        }
        if (UpOutBoundary)
        {
            _IncreMoveVector.z = -halfDiff;
        }
        if (DownOutBoundary)
        {
            _IncreMoveVector.z = halfDiff;
        }
    }
    

    下一篇继续优化Zoom缩放-聚焦触摸点

    相关文章

    1. Unity实现类似【海岛奇兵】探索场景概览1
    2. Unity实现UI信息跟随场景移动缩放-海岛奇兵2
    3. Unity透视相机下场景移动缩放-海岛奇兵3
    4. Unity Pinch手势缩放(Zoom)聚焦-海岛奇兵5
    5. Unity 海岛奇兵资源收取效果(6)

    参考

    1. Unity3D研究院之获取摄像机的视口区域http://www.xuanyusong.com/archives/3036

    相关文章

      网友评论

        本文标题:Unity透视相机下地图边界处理-海岛奇兵4

        本文链接:https://www.haomeiwen.com/subject/tkbxmxtx.html