各种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的压力。

  摄像机振动脚本:

using UnityEngine;
using System.Collections;

public class ShakeCamera : MonoBehaviour
{
    public float ShakeTime = 1f;                                // 时间
    public float ShakeStrength = 1f;                            // 振幅
    public int ShakeRate = 10;                                  // 频率

    public void Shake()
    {
        StartCoroutine("ShakeCoroutine");
    }

    public void Shake(float time, float strength, int rate)
    {
        ShakeTime = time;
        ShakeStrength = strength;
        ShakeRate = rate;
        StartCoroutine("ShakeCoroutine");
    }

    IEnumerator ShakeCoroutine()
    {
        // 此处需要先用临时变量记录相关参数值,以便能够实现多次重叠shake
        float startTime = Time.time;
        float endTime = startTime + ShakeTime;
        float interval = 1f / (float)ShakeRate;
        float strength = ShakeStrength;
        float timeCount = 0f;

        while (Time.time < endTime)
        {
            timeCount += Time.deltaTime;
            if (timeCount > interval)
            {
                // 获取随机位置
                Vector3 offset = Random.insideUnitSphere * strength;
                transform.position += offset;
                timeCount -= interval;
            }
            yield return null;
        }
    }

}
时间: 2024-11-03 16:32:59

各种Camera,总有一款适合你(二)的相关文章

[Asp.net]常见数据导入Excel,Excel数据导入数据库解决方案,总有一款适合你!

引言 项目中常用到将数据导入Excel,将Excel中的数据导入数据库的功能,曾经也查找过相关的内容,将曾经用过的方案总结一下. 方案一 NPOI NPOI 是 POI 项目的 .NET 版本.POI是一个开源的Java读写Excel.WORD等微软OLE2组件文档的项目.使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 WORD/EXCEL 文档进行读写.NPOI是构建在POI 3.x版本之上的,它可以在没有安装Office的情况下对Word/Excel文档进行读写操作

【吐血整理】国外产品经理课程推荐,总有一款适合你!

关于产品经理的发展史,虽然可以追溯到1927年美国P&G公司首次提出Product Management的制度,但"产品经理"这个职业的发展和普及不过才短短几年的时间,尤其对于很多中小型企业来说,才刚刚度过了职责模糊期.再加上现在互联网资本市场的推动,直接催化了互联网中的浮躁和泡沫现象,而且间接影响着产品经理这一职业.以及准备择业或深耕的人们. 目前有很多线上/线下的PM培训课程和机构,但到底实际情况怎么样?是否适合自己目前阶段的需求?学习后的成效到底如何?相信每一个准备转岗或

10款流行的Markdown编辑器,总有一款适合你

摘要:作为一个开源人,如果你不会使用Markdown语法,那你就OUT了!Markdown 是 2004 年由 John Gruberis 设计和开发的纯文本格式的语法,非常的简单实用. 作为一个开源人,如果你不会使用Markdown语法,那你就OUT了!Markdown 是 2004 年由 John Gruberis 设计和开发的纯文本格式的语法,非常的简单实用. CSDN CODE平台的 帮助文档.项目README文件.文档项目甚至包括ISSUES和项目评论等都是支持Markdown语法的!

最流行的自动化测试工具,总有一款适合你

1)Selenium Selenium 可能是网页应用中最流行的开源自动化测试框架.起源于 2000 年,10 多年来不断地完善,Selenium 成为许多 Web 自动化测试人员的选择,尤其是那些有高级编程和脚本技能的人.Selenium 也成为了其他开源自动化测试工具比如 Katalon Studio,Watir,Protractor 和 Robot Framework 的核心框架. Selenium 支持多系统环境(Windows,Mac,Linux)以及多种浏览器(Chrome,Fire

销售话术,一说就能“套”人,总有一款适合你

销售话术,遵从人的心理,套路总是"坏坏"的,结果总是"爽爽"的. 对于卖家来说,用好了话术,销售可给客户带来"美好"感受,让顾客快速下单,下次还来. 对于买家来说,能识别话术,购物则会更加理性,并且可能不会买错. 其实,人在做决策时的非理性程度,往往大到你无法想象,买卖双方的博弈,就是一次理性与非理性的相互颠覆. 好,废话少说,直接来干货. 加价:平滑过渡法 问题:原材料上涨,产品需要提价保持利润率.如何提价抚慰老客户? 错误姿势:直接加价,将提

硬核推荐15个最火的Spring实战开源项目!总有一款适合你!

哔哩哔哩在线演示视频: https://www.bilibili.com/video/av92191212 Guide哥注:下面这些推荐的项目几乎都和 Spring Boot 有关,毕竟这年头没有理由再搞 SSM/SSH 这些东西了. 商城系统 Guide哥注:下面的商城系统大多比较复杂比如 mall ,如果没有 Java 基础和 Spring Boot 都还没有摸熟的话不推荐过度研究下面几个项目或者使用这些项目当作毕业设计. mall :mall 项目是一套电商系统,包括前台商城系统及后台管理

美国高防独立服务器大搜罗,总有一款适合你

如今互联网处于爆炸式发展阶段,网站服务群体的多样化,功能的臃肿化,对服务器的内存和性能要求也越来越高.同时网站安全问题层出不穷,一言不合就遭到竞争对手攻击,而高防服务器在一定程度上可以对这种攻击起到抵御作用,那么市面上都有哪些口碑不错的美国高防主机呢?  1.Hostspace美国高防服务器 Hostspace位于美国机房位于美国加利弗利亚洲洛杉矶市西区第七大街600号,地处中美海缆的登录点附近,随着北美市场的拓展及资源点的广布,它即将更名成为LA机房,与位于华盛顿特区的WT机房成为Hostsp

四款?linux?操作系统总有一款适合你

转为使用Linux可谓是开启了一扇新工具和新技术的大门,它使你对计算环境的体验更为轻松.但是根据你所选择的Linux操作系统的不同,有时你甚至是浏览桌面环境也可能会充满了挑战,因此在客观上还是多少存在着一个学习曲线.本文旨在让你这样的新旅程更容易上手些. 发行版与桌面 发行版的桌面选项是吸引你成为它们的用户的主要方式之一. 你也许会问:"稍等,你是说桌面的选项?"是的,Linux提供了各种各样的桌面环境,或称为图形类的shell,它们从非常"酷炫"到极为清洁和简约.

四款linux操作系统总有一款适合你

发行版与桌面 发行版的桌面选项是吸引你成为它们的用户的主要方式之一. 你也许会问:"稍等,你是说桌面的选项?"是的,Linux提供了各种各样的桌面环境,或称为图形类的shell,它们从非常"酷炫"到极为清洁和简约.不同于那些在通常情况下只能够提供单一图形化shell的操作系统(如果并非是唯一选择的话),Linux允许你安装一个或多个不同的选项来满足你的口味. 几乎所有以桌面为核心的发行版都会以此作为它们的"主打"选项,它们中的一些会被配置得在外观