Unity 基于Cinemachine计算透视摄像机在地图中的移动范围
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计算透视摄像机在地图中的移动范围的更多相关文章
- Unity 通过NGUI 完成单摄像机 制作地图
本次思想主要是通过 Ngui的Scroll View 主要是UIPanel的Clipping属性的Alipha Clip 调节窗口大小,遮蔽地图试地图实现在屏幕的部分显示.此方法的好处是不用担心sha ...
- unity 基于scrollRect实现翻页显示
unity 基于scrollRect实现翻页显示,并定为到某一页,而不是某一页的中间方法(第二个脚本采用实际位置计算,并在update里实现平滑过渡): 组场景时,经常需要获取鼠标(或者点击)开始结束 ...
- Unity 3d 实现物体跟随摄像机视野运动
https://blog.csdn.net/qq_31411825/article/details/61623857 Unity 3d 实现物体跟随摄像机视野运动Created by miccall ...
- 基于ArcEngine与C#的鹰眼地图实现
鹰眼图是对全局地图的一种概略表达,具有与全局地图的空间参考和空间范围.为了更好起到空间提示和导航作用,有些还具备全局地图中重要地理要素,如主要河流.道路等的概略表达.通过两个axMapControl控 ...
- Spark 介绍(基于内存计算的大数据并行计算框架)
Spark 介绍(基于内存计算的大数据并行计算框架) Hadoop与Spark 行业广泛使用Hadoop来分析他们的数据集.原因是Hadoop框架基于一个简单的编程模型(MapReduce),它支持 ...
- 跨平台移动开发_PhoneGap 使用Geolocation基于所在地理位置坐标调用百度地图API
使用Geolocation基于所在地理位置坐标调用百度地图API 效果图 示例代码 <!DOCTYPE html> <html> <head> <title& ...
- 10-THREE.JS perspective透视摄像机和orthographic正交摄像机区别
<!DOCTYPE html> <html> <head> <title></title> <script src="htt ...
- 一元建站-基于函数计算 + wordpress 构建 serverless 网站
前言 本文旨在通过 快速部署一个 wordpress 网站到阿里云函数计算平台 这个示例来展示 serverless web 新的开发模式, 包括 FUN 工具一键初始化 NAS, 同步网站到 NAS ...
- 基于函数计算 + TensorFlow 的 Serverless AI 推理
前言概述 本文介绍了使用函数计算部署深度学习 AI 推理的最佳实践, 其中包括使用 FUN 工具一键部署安装第三方依赖.一键部署.本地调试以及压测评估, 全方位展现函数计算的开发敏捷特性.自动弹性伸缩 ...
随机推荐
- Vue 父组件往子组件传递方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【js】vue 2.5.1 源码学习 (十一) 模板编译compileToFunctions渲染函数
大体思路(九) 本节内容: 1. compileToFunctions定位 1. compileToFunctions定位 ==> createCompiler = createCompiler ...
- 2019-9-3-win10-uwp-收集-DUMP-文件
title author date CreateTime categories win10 uwp 收集 DUMP 文件 lindexi 2019-09-03 17:48:44 +0800 2018- ...
- ZR993
ZR993 首先,这种和平方有关的,首先应当考虑根号做法 这道题目,我们可以直接暴力\(\log_{10}w + 10\)判断一个数是否能够由原数变化的到 直接\(O(\sqrt{n})\)枚举所有的 ...
- 台州学院第十二届校赛记录(B,C,E,H,I,J,L)
传送门:点我 题目很棒,感谢出题验题的大佬们. 细节坑不少,是好事. 还是很菜,继续加油! B: 桃子的生日 时间限制(普通/Java):1000MS/3000MS 内存限制:65536KBy ...
- 【35.86%】【POJ 1962】Corporative Network
Time Limit: 3000MS Memory Limit: 30000K Total Submissions: 3943 Accepted: 1414 Description A very bi ...
- ideaic快捷键
Intellij IDEA中有很多快捷键让人爱不释手,stackoverflow上也有一些有趣的讨论.每个人都有自己的最爱,想排出个理想的榜单还真是困难.以前也整理过Intellij的快捷键,这次就按 ...
- IdentityServer4 Clients
原文地址 Clients 的定义 Client是指那些从 identityserver获取 token的应用 通常需要为client定义下面通用的设置 唯一的client id secret, 如果需 ...
- 这群程序员疯了!他们想成为IT界最会带货的男人
随着网红主播越来越火,通过直播带货种草的形式也成了今年双12的热点. 不过,网红主播带货早已见怪不怪,但你们见过程序员直播带货吗!? 近日,趁着阿里云双12年末采购节,阿里云邀请了一波程序员GG来为大 ...
- Oracle单引号拼接和替换
1.oracle拼接一个单引号: 正常写法:''''|| 由于单引号存在转义,第一个和最后一个是指定你要使用的字符,第二个’是单引号的转义字符,所以需要第三个‘才是真正你要拼接的那个. 也可以用 ch ...