各种Camera,总有一款适合你(二)
在实际的项目开发中,一般需要程序抽象出一些在几何意义上有明确意义的参数,这样方便策划或美术在自己的机器上进行调试。
下面是一个可变参的地下城摄像机的简单实现:
// 第三人称摄像机,平移和旋转会同时进行平滑
public class ThirdPersonalCamera : MonoBehaviour
{
/// Camera Control Params
public GameObject Target = null;
public float Distance = 10f;
public float RotateX = 0f; // pitch 俯仰
public float RotateY = 0f; // yaw 偏航
public float SmoothTime = 0.01f; // 平滑时间,默认为1s
public float ScrollWheelSpeed = 1000f; // 中轴调整速度
public Vector3 TargetOffset = Vector3.zero; // 目标偏移量 private Vector3 Velocity = Vector3.zero; // 平滑初速度
private Vector3 Offset = Vector3.zero; // 根据上面三个变量计算出
private const float MinDistance = 5f; // 镜头最近距离
private const float MaxDistance = 100f; // 镜头最远距离 void Start()
{
if (Target == null) return;
} public void Shake()
{
StartCoroutine("ShakeCoroutine");
} void LateUpdate()
{
if (Target == null) return; UpdateDistance(); float radX = Mathf.Deg2Rad * RotateX;
float radY = Mathf.Deg2Rad * RotateY; Offset.x = Distance * Mathf.Cos(radX) * Mathf.Cos(radY);
Offset.z = Distance * Mathf.Cos(radX) * Mathf.Sin(radY);
Offset.y = Distance * Mathf.Sin(radX); Vector3 targetPos = Target.transform.position + Offset + TargetOffset;
transform.position = Vector3.SmoothDamp(transform.position, targetPos, ref Velocity, SmoothTime); transform.LookAt(Target.transform.position + TargetOffset);
} private void UpdateDistance()
{
float horizontal = Input.GetAxis("Mouse ScrollWheel") * ScrollWheelSpeed * Time.deltaTime;
Distance -= horizontal;
} }
可以看到抽象出的控制参数主要包括:摄像机分别绕X轴和Y轴的旋转、摄像机距离角色的距离、以及用来控制平滑时间的参数。
注意,上面这段代码的实现中,position和rotation是同时进行平滑的,下面来看另外一种实现:
using UnityEngine;
using System.Collections; [ExecuteInEditMode]
public class MyDungeonCamera : MonoBehaviour
{
/// [摄像机控制参数]
public GameObject Target = null;
public float RotateX = 0f; // pitch 俯仰
public float RotateY = 0f; // yaw 偏航
public float Distance = 10f; // 摄像机远近
public float MoveSmoothTime = 0.3f; // 位置平滑时间,默认为1s
public float RotateSmoothTime = 0.3f; // 旋转平滑时间
public float ScrollWheelSpeed = 1000f; // 中轴调整速度
public Vector3 TargetOffset = Vector3.zero; // 目标偏移量 private Vector3 Velocity = Vector3.zero; // 平滑初速度
private Vector3 Offset = Vector3.zero; // 根据上面三个变量计算出
private const float MinDistance = 5f; // 镜头最近距离
private const float MaxDistance = 100f; // 镜头最远距离
private Quaternion tmpRotation = Quaternion.identity;
private Vector3 tmpPosition = Vector3.zero; private float RotateDamping
{
get
{
if (RotateSmoothTime <= 0f) RotateSmoothTime = 0.001f;
return 1f / RotateSmoothTime;
}
} void LateUpdate()
{
if (Target == null) return; tmpRotation = transform.rotation;
tmpPosition = transform.position; UpdateDistance();
UpdateRotation();
UpdatePosition(); transform.rotation = tmpRotation;
transform.position = tmpPosition;
} private void UpdateRotation()
{
if (!NeedRotate()) return; Quaternion wantedRotation = Quaternion.Euler(RotateX, RotateY, 0f);
// 旋转采用球形插值
tmpRotation = Quaternion.Slerp(tmpRotation, wantedRotation, Time.deltaTime * RotateDamping);
} private void UpdatePosition()
{
// 如果有旋转插值,则位置根据旋转变换;否则,位置自己进行插值过渡
if (!NeedRotate())
{
Offset = Quaternion.Euler(RotateX, RotateY, 0f) * Vector3.forward * Distance;
Vector3 wantedPos = Target.transform.position - Offset + TargetOffset;
// 位置采用平滑阻尼过渡
tmpPosition = Vector3.SmoothDamp(tmpPosition, wantedPos, ref Velocity, MoveSmoothTime);
}
else
{
Offset = tmpRotation * Vector3.forward * Distance;
tmpPosition = Target.transform.position - Offset + TargetOffset;
}
} private void UpdateDistance()
{
float horizontal = Input.GetAxis("Mouse ScrollWheel") * ScrollWheelSpeed * Time.deltaTime;
Distance -= horizontal;
Distance = Mathf.Clamp(Distance, MinDistance, MaxDistance);
} private bool NeedRotate()
{
Vector3 eulerAngles = transform.rotation.eulerAngles;
return !(FloatEqual(eulerAngles.x, RotateX) && FloatEqual(eulerAngles.y, RotateY));
} public static bool FloatEqual(float value1, float value2)
{
float ret = value1 - value2;
return ret > -0.0005f && ret < 0.0005f;
} }
这个实现中,有几点需要注意的:
(1)rotation使用四元素球形插值,这样保证每次旋转的角速度是恒定的,不过两次旋转各自的角速度不相等,这里可以改进;
(2)position使用Vector3.SmoothDamp进行平滑阻尼过渡效果会比较好;
(3)浮点数相等判断,不要直接用等号哟;
(4)当同时有rotation和position时,以rotation为主;
(5)是否需要旋转的判断,只有需要的时候才走旋转逻辑,这样可以减轻Update的压力。
摄像机振动脚本:
public class ShakeCamera : MonoBehaviour
{
public float ShakeTime = 1f;
public float ShakeStrength = 0.2f;
private Vector3 ShakeOffset = Vector3.zero;
private Vector3 preShakeOffset = Vector3.zero; void LateUpdate()
{
transform.position = transform.position - preShakeOffset + ShakeOffset;
} public void Shake()
{
StopCoroutine("ShakeCoroutine");
StartCoroutine("ShakeCoroutine");
} public void Shake(float time)
{
ShakeTime = time;
Shake();
} IEnumerator ShakeCoroutine()
{
float endTime = Time.time + ShakeTime;
while (Time.time < endTime)
{
ShakeOffset = Random.insideUnitSphere * ShakeStrength;
yield return null;
}
ShakeOffset = Vector3.zero;
} }
各种Camera,总有一款适合你(二)的更多相关文章
- 各种Camera,总有一款适合你(一)
根据游戏类型的不一样,会需要各种各样的摄像机,下面将分享三种典型的摄像机类型:(1)地下城摄像机:(2)第三人称跟随摄像机:(3)鼠标控制旋转摄像机.将这些控制脚本拖动到场景的MainCamera上即 ...
- 10款流行的Markdown编辑器,总有一款适合你
摘要:作为一个开源人,如果你不会使用Markdown语法,那你就OUT了!Markdown 是 2004 年由 John Gruberis 设计和开发的纯文本格式的语法,非常的简单实用. 作为一个开源 ...
- .NET5都来了,你还不知道怎么部署到linux?最全部署方案,总有一款适合你
随着2020进入4季度,.NET5正式版也已经与大家见面了.不过,尽管 .NET Core发布已经有四五年的时间,但到目前为止,依旧有很多.NET开发者在坚守者.NET4,原因不尽相同,但最大的问题可 ...
- 这么多TiDB负载均衡方案总有一款适合你
[是否原创]是 [首发渠道]TiDB 社区 前言 分布式关系型数据库TiDB是一种计算和存储分离的架构,每一层都可以独立地进行水平扩展,这样就可以做到有的放矢,对症下药. 从TiDB整体架构图可以看到 ...
- [Asp.net]常见word,excel,ppt,pdf在线预览方案,有图有真相,总有一款适合你!
引言 之前项目需要,查找了office文档在线预览的解决方案,顺便记录一下,方便以后查询. 方案一 直接在浏览器中打开Office文档在页面上的链接.会弹出如下窗口: 优点:主流浏览器都支持. 缺点: ...
- 四大VDI客户端 总有一款适合你
[TechTarget中国原创] 交付虚拟桌面时IT管理员必须要考虑到用户如何访问虚拟桌面,因为这会影响用户体验以及VDI部署最终的成败. IT可以转向简便的HTML5客户端,HTML 5客户端功能丰 ...
- 把 Console 部署成 Windows 服务,四种方式总有一款适合你!
一:背景 1. 讲故事 上周有一个项目交付,因为是医院级项目需要在客户的局域网独立部署. 程序: netcore 2.0,操作系统: windows server 2012,坑爹的事情就来了, net ...
- [Asp.net]常见数据导入Excel,Excel数据导入数据库解决方案,总有一款适合你!
引言 项目中常用到将数据导入Excel,将Excel中的数据导入数据库的功能,曾经也查找过相关的内容,将曾经用过的方案总结一下. 方案一 NPOI NPOI 是 POI 项目的 .NET 版本.POI ...
- 40个容易上瘾的HTML5网页游戏,总有一款适合你
我记得姐姐家的孩子在刚刚才学会走路,说话还不能完整的时候就已经能自己用小手点出小游戏的网站来一个人自娱自乐.我一直在想这一代跟着计算机一起茁壮成长的孩子会不会也和美国那一代人一样,出现9岁的黑客和计算 ...
随机推荐
- spring源码 — 一、IoC容器初始化
IoC容器初始化 注意:本次的spring源码是基于3.1.1.release版本 容器:具有获取Bean功能--这是最基本功能,也是BeanFactory接口定义的主要行为,在添加了对于资源的支持之 ...
- 通过ReadWriteReentrantLock源代码分析AbstractQueuedSynchronizer共享模式
1.特点 ReentrantLock能够实现共享资源的互斥访问,但是它在某些条件下效率比较低下.比如,多个线程要查询(或者说读取)某列车的余票数,如果使用ReentrantLock,那么多个线程的查询 ...
- Chkdsk scan needed on volume
After we extended the volume in storage array, in Failover cluster, it shows the volume is of 30.0 T ...
- 针对不同的Cookie做页面缓存
有时我们需要为PC浏览器及移动浏览器生成不同的页面,为了提高性能,不能每次请求都去判断User-Agent,通常用一个 Cookie 标记一下客户端是否是移动客户端,这样只需要读取这个 Cookie ...
- XML 和 List 互转类
XML 和 List 互转类 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...
- WPF窗体的命令绑定
方法一:使用代码 <WpfUI:View.CommandBindings> <CommandBinding Command="Help" CanExecute=& ...
- 每日英语:A Whiff Of 'Welcome Home'
Buying real estate involves weighing a lot of factors: location, price . . . smell? Some condo devel ...
- 出现Assertion failure in -[***** layoutSublayersOfLayer:]
在自定义的view中使用了[self layoutIfNeeded]方法,在iOS8\9上都没有错误,但是在iOS7上出现了页面错乱,解决方案就是在自定义的view里面添加如下代码: + (void) ...
- 【Cocos2d-x 3.X 资源及脚本解密】
加密就不用说了,看上一篇2.X加密的方式,怎么弄都可以.的保证解密规则就行: 现在重点说3.X解密: 在新的3.X引擎中官方整合了大部分获取资源的方法,最终合成一个getdata: 可以从源码,和堆栈 ...
- LoadRunner场景参数文件部分参数说明(我在某银行的整理)
由于场景中脚本繁多,同时设置60个脚本的“运行时设置”会提示个数限制信息,这时可以考虑通过场景的参数文件配置来批量解决这些事情,主要是提高工作效率. 选中自己保存的controller场景,鼠标右键点 ...