博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
各种Camera,总有一款适合你(二)
阅读量:6156 次
发布时间:2019-06-21

本文共 5996 字,大约阅读时间需要 19 分钟。

  在实际的项目开发中,一般需要程序抽象出一些在几何意义上有明确意义的参数,这样方便策划或美术在自己的机器上进行调试。

  下面是一个可变参的地下城摄像机的简单实现:

// 第三人称摄像机,平移和旋转会同时进行平滑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;    }}

 

转载地址:http://cnifa.baihongyu.com/

你可能感兴趣的文章
Runtime类
查看>>
eclipse decompiler
查看>>
记一个搜索网盘资源的网站
查看>>
jdk1.7和jdk1.8的String的getByte方法的差异
查看>>
java父子进程通信
查看>>
Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
查看>>
Olap学习笔记
查看>>
Codeforces Round #431 (Div. 1)
查看>>
如何进行数组去重
查看>>
将标题空格替换为 '_' , 并自动复制到剪切板上
查看>>
List Collections sort
查看>>
Mysql -- You can't specify target table 'address' for update in FROM clause
查看>>
使用局部标准差实现图像的局部对比度增强算法。
查看>>
2017-2018-1 20165313 《信息安全系统设计基础》第八周学习总结
查看>>
《代码敲不队》第四次作业:项目需求调研与分析
查看>>
菜鸡互啄队—— 团队合作
查看>>
HttpWebRequest的GetResponse或GetRequestStream偶尔超时 + 总结各种超时死掉的可能和相应的解决办法...
查看>>
SparseArray
查看>>
第二章
查看>>
android背景选择器selector用法汇总
查看>>