组件功能
让相机在某一时刻对某个点进行注释和收缩的表现。
下面给出C#代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
[ExecuteAlways]
#endif
public class UIControlShrinkCamera : MonoBehaviour
{
#region 事件区
/// <summary>
/// 无参注释开始回调,优先级比有参的高,并且只在队列开始时调用
/// </summary>
private event Action _StartSeeAction;
/// <summary>
/// 有参注释开始回调,队列每次注释都调用
/// </summary>
private event Action<GameObject> _StartSeeEveryOneAction;
/// <summary>
/// 无参注释结束回调,优先级比有参的高,并且只在队列开始时调用
/// </summary>
private event Action _SeeEndAction;
/// <summary>
/// 有参注释结束回调,队列每次注释都调用
/// </summary>
private event Action<GameObject> _SeeEndEveryOneAction;
#endregion
#region 属性区
#endregion
#region 变量区
[Space]
[Header("注释英雄时的位置向量大小")]
[Range(5,10)]
public float heroSeePos;
[SerializeField]
[Header("受作用物体")]
private Transform _targetTransform;
/// <summary>
/// 收缩曲线
/// </summary>
public AnimationCurve shrinkCurve;
private Queue<GameObject> _heroTargetList = new();
#endregion
#region 函数区
//--------------------Public 公有函数--------------------//
public void SeeHero(GameObject targetTrans)
{
if (_heroTargetList.Count == 0)
{
_heroTargetList.Enqueue(targetTrans);
_SeeHeroAnim();
}
else
{
_heroTargetList.Enqueue(targetTrans);
}
}
/// <summary>
/// 注册开始注视的监听
/// </summary>
/// <param name="starSeeAction"></param>
public void RegisterStartSeeEvent(Action starSeeAction)
{
_StartSeeAction -= starSeeAction;
_StartSeeAction += starSeeAction;
}
/// <summary>
/// 注册开始注视的监听
/// </summary>
/// <param name="starSeeEveryOneAction"></param>
public void RegisterStartSeeEveryOneEvent(Action<GameObject> starSeeEveryOneAction)
{
_StartSeeEveryOneAction -= starSeeEveryOneAction;
_StartSeeEveryOneAction += starSeeEveryOneAction;
}
/// <summary>
/// 注册注视结束的监听
/// </summary>
/// <param name="seeEndAction"></param>
public void RegisterSeeEndEvent(Action seeEndAction)
{
_SeeEndAction -= seeEndAction;
_SeeEndAction += seeEndAction;
}
/// <summary>
/// 注册注视结束的监听
/// </summary>
/// <param name="seeEndEveryOneAction"></param>
public void RegisterSeeEndEveryOneEvent(Action<GameObject> seeEndEveryOneAction)
{
_SeeEndEveryOneAction -= seeEndEveryOneAction;
_SeeEndEveryOneAction += seeEndEveryOneAction;
}
/// <summary>
/// 注销开始注视的监听
/// </summary>
/// <param name="starSeeAction"></param>
public void UnRegisterStartSeeEvent(Action starSeeAction)
{
_StartSeeAction -= starSeeAction;
}
/// <summary>
/// 注销开始注视的监听
/// </summary>
/// <param name="starSeeAction"></param>
public void UnRegisterStartSeeEveryOneEvent(Action<GameObject> starSeeAction)
{
_StartSeeEveryOneAction -= starSeeAction;
}
/// <summary>
/// 注销注视结束的监听
/// </summary>
/// <param name="seeEndEveryOneAction"></param>
public void UnRegisterSeeEndEveryOneEvent(Action<GameObject> seeEndEveryOneAction)
{
_SeeEndEveryOneAction -= seeEndEveryOneAction;
}
/// <summary>
/// 注销注视结束的监听
/// </summary>
/// <param name="seeEndAction"></param>
public void UnRegisterSeeEndEvent(Action seeEndAction)
{
_SeeEndAction -= seeEndAction;
}
//--------------------Protect 保护函数--------------------//
#if UNITY_EDITOR
/// <summary>
/// 测试使用
/// </summary>
[SerializeField]
private List<Transform> _testTarget = new List<Transform>();
private void OnValidate()
{
if (_targetTransform == null)
{
_targetTransform = transform;
}
}
#endif
//--------------------Private 私有函数--------------------//
#region SeeHero
private void _SeeHeroAnim()
{
if (_heroTargetList == null || _heroTargetList.Count <= 0)
{
return;
}
StartCoroutine(_SeeHeroAnimCo());
}
/// <summary>
/// 开启队列协程
/// </summary>
/// <returns></returns>
private IEnumerator _SeeHeroAnimCo()
{
if (_heroTargetList.Count <= 0)
{
yield break;
}
_StartSeeAction?.Invoke();
Vector3 nowPosition = _targetTransform.position;
Quaternion quaternion = _targetTransform.rotation;
while (_heroTargetList.Count > 0)
{
// Vector3 targetTrans = _heroTargetList.Peek();
GameObject role = _heroTargetList.Peek();
if (role == null)
{
continue;
}
_StartSeeEveryOneAction?.Invoke(role);
Vector3 targetTrans = role.transform.position;
targetTrans.y += 1.8f;
yield return _SeeTargetHeroAnimCo(targetTrans,_targetTransform);
_SeeEndEveryOneAction?.Invoke(role);
_heroTargetList.Dequeue();
}
_targetTransform.position = nowPosition;
_targetTransform.rotation = quaternion;
_SeeEndAction?.Invoke();
}
/// <summary>
/// 挪动注视英雄目标
/// </summary>
/// <returns></returns>
private IEnumerator _SeeTargetHeroAnimCo(Vector3 target,Transform oTrans)
{
if (oTrans == null)
{
yield break;
}
Vector3 oPos = oTrans.position;
Quaternion oQuaternion = oTrans.rotation;
//这里target不判空了,只有唯一个地方调用并且在调用处判空了
Vector3 targetPosition = target;
Vector3 targetPos = targetPosition + (oPos - targetPosition).normalized * heroSeePos;
Vector3 moveVec = targetPos - oPos;
float durTime = shrinkCurve.keys[shrinkCurve.length - 1].time;
float passTime = 0f;
_targetTransform.LookAt(target);
float endTime = Time.realtimeSinceStartup + durTime;
while (Time.realtimeSinceStartup < endTime)
{
float tempTimeScale = (Time.timeScale > 1 ? Time.timeScale : 1f);
float curWaitTime = Time.fixedUnscaledDeltaTime / tempTimeScale;
float value = shrinkCurve.Evaluate(passTime);
_targetTransform.position = oPos + moveVec * value;
passTime += curWaitTime * tempTimeScale;
yield return new WaitForSecondsRealtime(curWaitTime);
}
_targetTransform.position = oPos;
_targetTransform.rotation = oQuaternion;
yield break;
}
#endregion
#endregion
}
下面是组件效果
这里有个问题,相机在第一个和第二注视的时候会很快的跳转回原始位置,解决这个问题,在注视结束的时候做一个缓动重置位置来解决问题,下面是修改后的代码。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
[ExecuteAlways]
#endif
public class UIControlShrinkCamera : MonoBehaviour
{
#region 事件区
/// <summary>
/// 无参注释开始回调,优先级比有参的高,并且只在队列开始时调用
/// </summary>
private event Action _StartSeeAction;
/// <summary>
/// 有参注释开始回调,队列每次注释都调用
/// </summary>
private event Action<GameObject> _StartSeeEveryOneAction;
/// <summary>
/// 无参注释结束回调,优先级比有参的高,并且只在队列开始时调用
/// </summary>
private event Action _SeeEndAction;
/// <summary>
/// 有参注释结束回调,队列每次注释都调用
/// </summary>
private event Action<GameObject> _SeeEndEveryOneAction;
#endregion
#region 属性区
#endregion
#region 变量区
[Space]
[Header("注释英雄时的位置向量大小")]
[Range(5,10)]
public float heroSeePos;
[SerializeField]
[Header("受作用物体")]
private Transform _targetTransform;
[SerializeField]
[Header("重置原始位置间隔时间")]
[Range(0,1f)]
private float _resetOriginalPosIntervalTime = 0.3f;
/// <summary>
/// 收缩曲线
/// </summary>
public AnimationCurve shrinkCurve;
private Queue<GameObject> _heroTargetList = new();
#endregion
#region 函数区
//--------------------Public 公有函数--------------------//
public void SeeHero(GameObject targetTrans)
{
if (_heroTargetList.Count == 0)
{
_heroTargetList.Enqueue(targetTrans);
_SeeHeroAnim();
}
else
{
_heroTargetList.Enqueue(targetTrans);
}
}
/// <summary>
/// 注册开始注视的监听
/// </summary>
/// <param name="starSeeAction"></param>
public void RegisterStartSeeEvent(Action starSeeAction)
{
_StartSeeAction -= starSeeAction;
_StartSeeAction += starSeeAction;
}
/// <summary>
/// 注册开始注视的监听
/// </summary>
/// <param name="starSeeEveryOneAction"></param>
public void RegisterStartSeeEveryOneEvent(Action<GameObject> starSeeEveryOneAction)
{
_StartSeeEveryOneAction -= starSeeEveryOneAction;
_StartSeeEveryOneAction += starSeeEveryOneAction;
}
/// <summary>
/// 注册注视结束的监听
/// </summary>
/// <param name="seeEndAction"></param>
public void RegisterSeeEndEvent(Action seeEndAction)
{
_SeeEndAction -= seeEndAction;
_SeeEndAction += seeEndAction;
}
/// <summary>
/// 注册注视结束的监听
/// </summary>
/// <param name="seeEndEveryOneAction"></param>
public void RegisterSeeEndEveryOneEvent(Action<GameObject> seeEndEveryOneAction)
{
_SeeEndEveryOneAction -= seeEndEveryOneAction;
_SeeEndEveryOneAction += seeEndEveryOneAction;
}
/// <summary>
/// 注销开始注视的监听
/// </summary>
/// <param name="starSeeAction"></param>
public void UnRegisterStartSeeEvent(Action starSeeAction)
{
_StartSeeAction -= starSeeAction;
}
/// <summary>
/// 注销开始注视的监听
/// </summary>
/// <param name="starSeeAction"></param>
public void UnRegisterStartSeeEveryOneEvent(Action<GameObject> starSeeAction)
{
_StartSeeEveryOneAction -= starSeeAction;
}
/// <summary>
/// 注销注视结束的监听
/// </summary>
/// <param name="seeEndEveryOneAction"></param>
public void UnRegisterSeeEndEveryOneEvent(Action<GameObject> seeEndEveryOneAction)
{
_SeeEndEveryOneAction -= seeEndEveryOneAction;
}
/// <summary>
/// 注销注视结束的监听
/// </summary>
/// <param name="seeEndAction"></param>
public void UnRegisterSeeEndEvent(Action seeEndAction)
{
_SeeEndAction -= seeEndAction;
}
//--------------------Protect 保护函数--------------------//
#if UNITY_EDITOR
/// <summary>
/// 测试使用
/// </summary>
[SerializeField]
private List<Transform> _testTarget = new List<Transform>();
private void OnValidate()
{
if (_targetTransform == null)
{
_targetTransform = transform;
}
}
#endif
//--------------------Private 私有函数--------------------//
#region SeeHero
private void _SeeHeroAnim()
{
if (_heroTargetList == null || _heroTargetList.Count <= 0)
{
return;
}
StartCoroutine(_SeeHeroAnimCo());
}
/// <summary>
/// 开启队列协程
/// </summary>
/// <returns></returns>
private IEnumerator _SeeHeroAnimCo()
{
if (_heroTargetList.Count <= 0)
{
yield break;
}
_StartSeeAction?.Invoke();
Vector3 nowPosition = _targetTransform.position;
Quaternion quaternion = _targetTransform.rotation;
while (_heroTargetList.Count > 0)
{
// Vector3 targetTrans = _heroTargetList.Peek();
GameObject role = _heroTargetList.Peek();
if (role == null)
{
continue;
}
_StartSeeEveryOneAction?.Invoke(role);
Vector3 targetTrans = role.transform.position;
targetTrans.y += 1.8f;
yield return _SeeTargetHeroAnimCo(targetTrans,_targetTransform);
_SeeEndEveryOneAction?.Invoke(role);
_heroTargetList.Dequeue();
}
_targetTransform.position = nowPosition;
_targetTransform.rotation = quaternion;
_SeeEndAction?.Invoke();
}
/// <summary>
/// 挪动注视英雄目标
/// </summary>
/// <returns></returns>
private IEnumerator _SeeTargetHeroAnimCo(Vector3 target,Transform oTrans)
{
if (oTrans == null)
{
yield break;
}
Vector3 oPos = oTrans.position;
Quaternion oQuaternion = oTrans.rotation;
//这里target不判空了,只有唯一个地方调用并且在调用处判空了
Vector3 targetPosition = target;
Vector3 targetPos = targetPosition + (oPos - targetPosition).normalized * heroSeePos;
Vector3 moveVec = targetPos - oPos;
float durTime = shrinkCurve.keys[shrinkCurve.length - 1].time;
float passTime = 0f;
_targetTransform.LookAt(target);
float endTime = Time.realtimeSinceStartup + durTime;
while (Time.realtimeSinceStartup < endTime)
{
float tempTimeScale = (Time.timeScale > 1 ? Time.timeScale : 1f);
float curWaitTime = Time.fixedUnscaledDeltaTime / tempTimeScale;
float value = shrinkCurve.Evaluate(passTime);
_targetTransform.position = oPos + moveVec * value;
passTime += curWaitTime * tempTimeScale;
yield return new WaitForSecondsRealtime(curWaitTime);
}
//start 这里做一个缓动来解决突然重置的问题
Vector3 nowPos = _targetTransform.position;
Quaternion nowQuat = _targetTransform.rotation;
float t = 0;
while (t < _resetOriginalPosIntervalTime)
{
float lerpT = t / _resetOriginalPosIntervalTime;
_targetTransform.position = Vector3.Lerp(nowPos, oPos, lerpT);
_targetTransform.rotation = Quaternion.Lerp(nowQuat,oQuaternion,lerpT);
t += Time.unscaledDeltaTime;
yield return new WaitForSecondsRealtime(Time.unscaledDeltaTime);
}
//end
_targetTransform.position = oPos;
_targetTransform.rotation = oQuaternion;
yield break;
}
#endregion
#endregion
}
修改后的效果
对Inspector面板上几个参数进行一下解释
- HeroSeePos ⇒ 相机在注视目标物体时和目标物体的距离值
- TargetTransform ⇒ 受作用的物体,案例上受作用物体是相机,也可以设置其他物体进行收缩
- ResetOriginalPosIntervalTime ⇒ 重置原始位置的缓动时间
- ShrinkCurve ⇒ 物体收缩动画的曲线控制,可以看到有案例中的曲线有三段,第一段是从初始位置到目标位置,中间那段是停顿时间,最后的那段就是重置曲线,曲线可以根据自己的需要自己调整
对接口进行一下解释
外部调用收缩注视的接口只有一个 SeeHero接口
如果想在不同时机做一些特殊事情的话可以调用下面的几个接口进行注册回调
- RegisterStartSeeEvent 无参注释开始回调,优先级比有参的高,并且只在队列开始时调用
- RegisterStartSeeEveryOneEvent 有参注释开始回调,队列每次注释都调用
- RegisterSeeEndEvent 无参注释结束回调,优先级比有参的高,并且只在队列开始时调用
- RegisterSeeEndEveryOneEvent 有参注释结束回调,队列每次注释都调用
对应的反注册接口
- UnRegisterStartSeeEvent
- UnRegisterStartSeeEveryOneEvent
- UnRegisterSeeEndEvent
- UnRegisterSeeEndEveryOneEvent
如果有相同功能,希望可以帮到你。