您当前的位置: 首页 > 

野奔在山外的猫

暂无认证

  • 2浏览

    0关注

    85博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【案例设计】Free Camera 设计与实现思路

野奔在山外的猫 发布时间:2022-05-29 10:07:19 ,浏览量:2

开发平台:Unity 2020 编程平台:Visual Studio 2020  

前言

  在游戏世界中,摄像机作为最常见、最常使用的 Component 或 GameObject 对象。经常因为项目开发类型,在原基础上进行二次开发。例如 RTS建造游戏、即时战略、模拟现实等需要第三人称摄像机(TP)。枪战对抗、恐怖解密等需要第一人称摄像机(FP)。必须时,如《坦克世界》、《战地风云》等经典游戏需要 TP、FP 两者的结合。本文主要探究 Camera Component 的基础上实现 TP 效果。

注意:Unity 在如今的版本中推广 Ciniemachine 工具包。强化了 Camera 的使用效率、呈现效果。本文不使用该工具包实现。  

思考:Transform 与 Vector 的关系与区别

  摄像机的移动 关联 Unity 编辑器中的 Transform 组件信息。该组件用于定义与管理 GameObject 在世空间的位置信息。在游戏中,寄希望于通过不同角度获取虚拟世界中不同角度下的视觉体验。对 Transform 必须有较好的理解与认知。如下示意图所示: 在这里插入图片描述   通过上视图,可以总结出以下几点:    1)世界空间是以 Vector 系组建而成。且方向固定,各轴面互垂直。    2)Transform 定义的前后左右方向在世界空间下不固定。即以所见方向为前向。  

思考:Quaternion 与 EulerAngles 的关系与区别

在这里插入图片描述

四元数(Quaternion):在 Unity 引擎中,统一使用四元数(Quaternion)实现对 GameObject 的旋转。 特点:四元数具备不受 万向锁 影响的特点。 备注:默认下面板中 Rotation 使用的是 Quaternion数据类型变量。 相关API:Unity 建议使用 *= 方式实现旋转方式。(四元数值累加与 + 无关)   欧拉角(EulerAngle):围绕固定的 X Y Z 轴进行旋转移动。 特点:各轴值变化互不影响。但同时变化多轴值出现 万向锁 情况影响。 备注:默认为面板上具体的数值信息。(非归一化 Quaternion)使用transform.eulerAngles获取关于 X Y Z 轴轴值。 相关API:transform.Rotate() - Unity 封装的基于 EulerAngles 实现的旋转方式。

◼ 关于 EulerAngles 轴值限定范围问题的分析与解决方案   值得注意的是 EulerAngles 的范围区间在 [0, 360]。即超过或低于范围内的数据将转换至范围内的相对数据值。例如 470° = 110°、-60° = 300°等情况。对 Inspector 面板有所了解的会注意到,任意的改变 Rotation 变量值均不会出现被处理至 [0, 360] 区间的范围。即是多少 = 值多少(这里值不为度数)。   有时候为限制 EulerAngles 中具体某个轴值范围易出现问题,例如 限制 [-60, 60],实际上做不到区间 [-60, 0) 的限制。于是在 Inspector 面板上虽然显示数值 -50。但实际其欧拉角大小为(- 50 + 360)= 310。在实际操作中,其度数的变化是基于X正半轴顺时针开始计算角度值。为实现负数角度的限制,使用以下示例代码逻辑:

public float ClampEulerAngles(float eulerAnglesValue)
{
	var value = eulerAnglesValue  transform.rotation *= Quaternion.Euler(new Vector3(x, y ,z));
  • Quaternion.Euler():将 Vector3 类型转换为 Quaternion 类型。
  • 在 全自由移动 下的表现,是完全基于自身方向进行。如果刻意控制其左右转向围绕水平面方向进行,则其最后需添加transform.rotation = Quaternion.Euler(new Vector3(transform.rotation.x, transform.rotation.y, 0f))进行坐标处理即可。

◼ 基于 欧拉角 的旋转

public void DoRotate()
{ 
	//transform.eulerAngles += new Vector3(x, y, z);
	transform.Rotate(new Vector3(x, y, z));
}
  • 实质上该方法即 Unity API 中transform.Rotate()方法原理。即可使用transform.Rotate(new Vector3(x, y, z))进行表示。
  • 同理于 四元数 实现左右转向围绕水平面方向进行,应使用transform.enlerAngles = new Vector3(tranform.eulerAngles.x, transform.eulerAngles.y, 0f);进行角度重置。即使传入的值为负数,在 EulerAngles 中会处理至[0, 360]值范围内。

特别的:transform.enlerAngles.x 这类具体到特定值上,在开发过程中无法直接修改该值以实现角度限制。则应追溯至上一层级进行数据类型赋值。

实践:视距缩放 Scale

  实现画面的放大与缩小效果,类似放大镜的作用。在 Unity 中这类实现方法无疑是将 Free Camera 沿 transform.forward 方向进行偏移。 当然,在一定范围内,使用 Camera.fieldOfView 属性进行值修改可达到同样的效果。其原理即是降低视野范围。   ◼ 关联参数

  • 缩放阻尼:过渡效果(在移动、旋转中 亦可考虑添加) 关联词Lerp()
  • 缩放速率:同距离下的最快移动速度。

◼ 基于 fieldOfView 的缩放

public void DoScale()
{
	var valueEnd = Input.mouseScrollDelta.y >=0f ? 20f : 60f;
	_Camera.fielfOfView = Mathf.Lerp(_Camera.fieldOfView, valueEnd, 1f);
}
  • Mathf.Lerp():插值过程,此处描述为1s内完成当前值至valueEnd值的变化。
  • 弊端:当值变化过于极端的情况下,画面视野扭曲程度较高,与实际放大缩小充满违和感。

◼ 基于 位置变化 的缩放

public void DoScale()
{
	var moveDir = transform.forward * Input.mouseScrollDelta.y * Time.deltaTime;
	_Camera.transfrom.position += moveDir;
	//_Camera.transform.Translate(moveDir);
}

其他实现层面:

  • 添加 Aim 模式,修改缩放策略。有时候期望于仅放大观察物体表面内容,则可使用 Physics.RayCasst() 物理射线检测对象(或由点击行为绑定的 GameObject 信息)。 按照 (目标 至 主摄像机 的距离)* 0.5f 进行移动效果(或使用插值),以达到无限接近目标位置。但未穿过目标。

小结

  Free Camera 的应用环境广泛,也是入门级学习的典型案例。理解与深析是认识三维虚拟世界的重要点。

  ◼ 可优化措施

  1. 使用 ScriptableObject 创建配置参数。
  2. 推荐配合 Cinemachine 工具包拓展 Free Camera 功能设计。
  3. 代码结构上,划分 主逻辑(移动 旋转 缩放)、助理(转换方法、枚举)、可配置(持久化数据)。切忌整合在同一脚本下,增加阅读难度。
  4. 扩展设计 Free Camera 的三维空间运动范围,限制其在某些区域内的移动。
关注
打赏
1659777066
查看更多评论
立即登录/注册

微信扫码登录

0.0364s