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

本文前篇 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容