Unity中Cinemachine的基础功能介绍可详见之前写的博客:

https://www.cnblogs.com/koshio0219/p/11820654.html

本篇的重点是讨论,在给定规则地图的长宽和中心点坐标的情况下,如何动态生成一个透视摄像机的碰撞盒子以限定摄像机的视野永远不会超出地图的边界。

例如,下面这种规则地图:(或者其他用程序生成的单位块地图)

在输入一些参数后:

可以自动创建形如:

这样的摄像机运动范围,且输出的范围能够适配到屏幕的分辨率,考虑到相机绕某一轴向的旋转等问题。

其实基本都是纯粹的数学运算,开始之前,必须先弄清楚透视摄像机的一些基本原理,它的视窗大小和屏幕分辨率之间到底是什么关系:

1.FOV:这是透视摄像机区别于正交摄像机最重要的一个特性——视口大小,它表示的是当前摄像机视野范围的开口角度,也因该角度大小的不同,使得透视摄像机的近裁剪平面和远裁剪平面大小不一,从而产生三维空间中近大远小的特点。

2.Aspect:当前摄像机的宽高比。为什么要设置这样一个东西呢?理由就是屏幕有不同的分辨率,而相机映照出来的画面最终是要在屏幕当中显示的,当我们的屏幕分辨率发生变化时,相机的视口面积也会对应的发生变化,这时,仅仅只有一个FOV没办法满足不同类型的屏幕分辨率,于是就需要额外设置相机的宽高比来对最终呈现的摄像机视口大小进行辅助调整。

在Unity中,是以视口的高为基准进行计算的,也就是说,Unity中的透视摄像机的Fov角度其实是按照屏幕分辩率的高度进行对应的,而宽度对应的Fov则随着Aspect的变化而变化,不是面板设置的Fov大小。

试比较下面两张图,分别是摄像机的宽和高的Fov:

设置的Fov为40度,当前的屏幕分辨率为2960*1440:

很显然,只有高度对应的Fov为面板中显示的值,而宽度对应的Fov明显大于40度。实际宽的的Fov应该是82度左右(40*2960/1440)。

知道了上面这些后我们才能更愉快的进行接下来的计算,不然只会计算出许多错误也搞不清是什么原因。

在Cinemachine中,一般会设置一个跟随目标,且跟踪该目标的距离是一个常量,可以从面板中取得:

我们先分析摄像机的左右运动范围是如何计算的:(本例中的摄像机只在X轴向上存在旋转值,一般斜向的摄像机也只需要旋转一个轴即可,左右看上去一般追求对称性)

观察上图,假设现在摄像机位于空中的P点,已知AB为地图的边缘围墙高度,BC为角色的高度,CP为跟踪的摄像机到角色的距离,现在我们需要求出摄像机所在的X轴向的坐标,关键就是要求出AD的距离。

我们还知道一个数据就是摄像机的Fov,但是由于该Fov并非高度对应的值,所以我们先要进行一次转换,以得到摄像机宽度视口的Fov角度。以下均为弧度计算:

     //计算的角度均为弧度值,传入纵向的(高)Fov的一半得到横向的(宽)Fov的一半
public float GetHorizontalFovHalf(float vhfov, float aspect)
{
return Mathf.Atan(Mathf.Tan(vhfov) * aspect);
}

上面已经讲过原理了这里就不在进行过多叙述了,简单来说就是利用摄像机的深度值进行了一次转换,因为无论是纵向还是横向的Fov,它们的深度值都是相同的,读者可以自行画图或脑补一下。

通过上面的方法我们就可以求得∠DPA的大小了,它正好就是横向Fov的一半,那个∠α的大小就可以轻易求出,现在问题的关键就是要求出边AP的长度,AP的长度得出的话,就可以利用∠α余弦求得AD,DP等。

利用正弦定理可以非常快速的解决上面的问题,当然你也可以设未知数利用勾股定律解一元二次方程,但当你写程序的时候你可能会有想吐的冲动:

     //计算轴向偏移值
private float GetSizeOffse(float fbangel, float distance, float wh, float followy)
{
//直角弧度值
var rightangel = * Mathf.Deg2Rad;
//∠PAC
var disangel = fbangel + rightangel;
//求出正弦定理的比值
var sin = distance / Mathf.Sin(disangel);
//求∠APC的正弦值
var angelo = (wh - followy) / sin;
//三角形内角和求∠ACP
var angel = rightangel * - Mathf.Asin(angelo) - disangel;
//计算AP利用α余弦返回AD
return sin * angel * Mathf.Cos(fbangel);
}

fbangel即为上图中的∠α,distance即为上图中的CP,wh即为上图中的AB,followy即为上图中的CB。

X轴向的偏移计算完毕后,Z轴的偏移也是类似的,只不过需要考虑旋转值而已,接下来就是摄像机的高度(注意摄像机的高度是一个变量),这个很容易计算。下面给出生成摄像机运动区域的参考:

     //计算并生成透视摄像机的运动区域
public void GenZone()
{
Camera = Camera.main; //计算从地图中心到边缘的向量
var toedge = WidthHeight * UnitLength * .5f;
//左后
var lb = CenterPoint - toedge;
//右前
var rf = CenterPoint + toedge;
//墙高
var wh = WallHeight; zone = new GameObject("CameraZone"); var box = zone.AddComponent<BoxCollider>();
var cvc = GetComponent<CinemachineVirtualCamera>();
var cft = cvc.GetCinemachineComponent<CinemachineFramingTransposer>(); var cvcs = cvc.m_Lens;
//摄像机跟踪目标的高度
var followy = cvc.m_Follow.position.y;
//跟踪距离
var distance = cft.m_CameraDistance;
//屏幕高对应的Fov的一半(真实Fov)
var hfov = cvcs.FieldOfView * .5f * Mathf.Deg2Rad;
//摄像机视口宽高比
var aspect = Camera.aspect;
//摄像机轴向旋转值
var rotation = Camera.transform.eulerAngles.x * Mathf.Deg2Rad;
var rightangel = * Mathf.Deg2Rad;
//屏幕宽对应的Fov的一半(转化后的Fov)
var whfov = GetHorizontalFovHalf(hfov, aspect); //摄像机当前高度
var height = Mathf.Sin(rotation) * distance + followy; //计算左右偏移(对称)
var lrangel = rightangel - whfov;
var widthh = GetSizeOffse(lrangel, distance, wh, followy);
var left = lb.x + widthh;
var right = rf.x - widthh;
var sizex = Mathf.Abs(left - right); //计算前后偏移(带旋转值,非对称)
var fangel = rotation - hfov;
var front = rf.y - GetSizeOffse(fangel, distance, wh, followy); var bangel = rotation + hfov;
var back = lb.y - GetSizeOffse(bangel, distance, wh, followy); var sizez = Mathf.Abs(front - back); //设置摄像机运动范围的大小,因为在XZ平面上,盒子的高度可以为一个常量
box.size = new Vector3(sizex, , sizez);
zone.transform.position = new Vector3((left + right) * .5f, height, (front + back) * .5f); CC.m_BoundingVolume = zone.GetComponent<BoxCollider>();
}

生成该盒子后,只需要将它赋值给CinemachineConfiner的BoundingVolume属性即可:

为了更方便的进行测试和调试,可以写一个Editor脚本在编辑器模式下生成:

 using UnityEditor;
using UnityEngine; [CustomEditor(typeof(CameraZoneCtrl))]
public class CameraZoneEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
CameraZoneCtrl ctrl = (CameraZoneCtrl)target;
if (GUILayout.Button("创建摄像机范围"))
{
ctrl.GenZone();
}
}
}

Unity 基于Cinemachine计算透视摄像机在地图中的移动范围的更多相关文章

  1. Unity 通过NGUI 完成单摄像机 制作地图

    本次思想主要是通过 Ngui的Scroll View 主要是UIPanel的Clipping属性的Alipha Clip 调节窗口大小,遮蔽地图试地图实现在屏幕的部分显示.此方法的好处是不用担心sha ...

  2. unity 基于scrollRect实现翻页显示

    unity 基于scrollRect实现翻页显示,并定为到某一页,而不是某一页的中间方法(第二个脚本采用实际位置计算,并在update里实现平滑过渡): 组场景时,经常需要获取鼠标(或者点击)开始结束 ...

  3. Unity 3d 实现物体跟随摄像机视野运动

    https://blog.csdn.net/qq_31411825/article/details/61623857 Unity 3d 实现物体跟随摄像机视野运动Created by miccall ...

  4. 基于ArcEngine与C#的鹰眼地图实现

    鹰眼图是对全局地图的一种概略表达,具有与全局地图的空间参考和空间范围.为了更好起到空间提示和导航作用,有些还具备全局地图中重要地理要素,如主要河流.道路等的概略表达.通过两个axMapControl控 ...

  5. Spark 介绍(基于内存计算的大数据并行计算框架)

    Spark 介绍(基于内存计算的大数据并行计算框架)  Hadoop与Spark 行业广泛使用Hadoop来分析他们的数据集.原因是Hadoop框架基于一个简单的编程模型(MapReduce),它支持 ...

  6. 跨平台移动开发_PhoneGap 使用Geolocation基于所在地理位置坐标调用百度地图API

    使用Geolocation基于所在地理位置坐标调用百度地图API 效果图 示例代码 <!DOCTYPE html> <html> <head> <title& ...

  7. 10-THREE.JS perspective透视摄像机和orthographic正交摄像机区别

    <!DOCTYPE html> <html> <head> <title></title> <script src="htt ...

  8. 一元建站-基于函数计算 + wordpress 构建 serverless 网站

    前言 本文旨在通过 快速部署一个 wordpress 网站到阿里云函数计算平台 这个示例来展示 serverless web 新的开发模式, 包括 FUN 工具一键初始化 NAS, 同步网站到 NAS ...

  9. 基于函数计算 + TensorFlow 的 Serverless AI 推理

    前言概述 本文介绍了使用函数计算部署深度学习 AI 推理的最佳实践, 其中包括使用 FUN 工具一键部署安装第三方依赖.一键部署.本地调试以及压测评估, 全方位展现函数计算的开发敏捷特性.自动弹性伸缩 ...

随机推荐

  1. 2018-8-10-WPF-调试-获得追踪输出

    title author date CreateTime categories WPF 调试 获得追踪输出 lindexi 2018-08-10 19:16:51 +0800 2018-05-16 1 ...

  2. no_expand优化案例

    bond 来看一个烂语句: select a.*,b.dn from temp_allcrmuser a, phs_smc_user b  where a.USERNUMBER=b.dn  and ( ...

  3. python模块之hashlib模块

    hashlib模块:提供摘要算法 格式: hashlib格式: obj = hashlib.算法(md5,sha....) obj.update(摘要内容:bytes类型) result = obj. ...

  4. vue-learning:25 - component - 概念-定义-注册-使用-命名

    概念 Vue遵循Web Component规范,提供了自己的组件系统.组件是一段独立的代码,代表页面中某个功能块,拥有自己的数据.JS.样式,以及标签.组件的独立性是指形成自己独立的作用域,不会对其它 ...

  5. ASP.NET Core 连接 GitLab 与 MatterMost 打造 devops 工具

    在现代化开发工具链里面就包含了自动化的通讯工具,而日志写代码我是推到 Gitlab 平台上,我今天听了郭锐大佬的分享之后,感觉我现在的团队的自动化做的远远不够.我在他的课程上学到的最重要一句话就是做工 ...

  6. Trendalyzer is an information visualization software

    Trendalyzer is an information visualization software for animation of statistics that was initially ...

  7. 学习linux命令,看这篇2w多字的linux命令详解

    用心分享,共同成长 没有什么比每天进步一点点更重要了 本文已收录到我的github:https://github.com/midou-tech/articles/tree/master/docs/li ...

  8. JVM内存结构探秘及编码实战

    了解JVM内存结构的目的 在Java的开发过程中,因为有JVM自动内存管理机制,不再需要像在C.C++开发那样手动释放对象的内存空间,不容易出现内存泄漏和内存溢出的问题.但是,正是由于把内存管理的权利 ...

  9. WWDC2018 之 高性能 Auto Layout

    1. 关于 Auto Layout 的历史渊源 上世纪 90 年代,名叫 Cassowary的布局算法,通过将布局问题抽象成线性不等式,并分解成多个位置间的约束,解决了用户界面的布局问题. Apple ...

  10. 一天入门 Python 的一些心得

    1. 前言 好久没写文了.最近在搞一些好玩的技术用到了 Python .我原以为要花些时日,谁知道第一天入门之后便没有再刻意地去学习它了.这里就写写其中的一些关键点吧.如果我去学一门语言不是因为它火了 ...