提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
主要是用在 模型部件的标签使用。

提示:以下是本篇文章正文内容,下面案例可供参考
一、代码
代码挂载在标签UI上。
功能(可以在编辑器面板调整):
1.永远朝向相机
相机随着相机变化,一直朝向相机
2.位置跟随物体
标签一直跟随物体
3.尺寸控制
无论相机远近怎么变换,标签大小随之变化。

using UnityEngine;
using UnityEngine.Rendering;
[RequireComponent(typeof(RectTransform))] // 如果是UI
public class RobustWorldSpaceUIFacer : MonoBehaviour
{
[Header("核心目标")]
[Tooltip("要朝向的相机。如果为空,则使用Main Camera。")]
public Camera targetCamera;
[Header("朝向控制")]
[Tooltip("启用后,UI将始终旋转以面向相机。")]
public bool faceCamera = true;
[Tooltip("旋转平滑时间(秒)。0为立即旋转。")]
[Range(0f, 2f)]
public float rotationSmoothTime = 0.15f;
[Tooltip("自定义的向上轴方向(世界空间)。用于防止翻转。")]
public Vector3 worldUp = Vector3.up;
[Header("位置跟随")]
[Tooltip("UI要跟随的目标Transform。如果为空,则UI在世界中静止。")]
public Transform followTarget;
[Tooltip("相对于跟随目标的偏移量。")]
public Vector3 targetOffset = new Vector3(0, 2f, 0); // 例如,在目标头顶2米
[Tooltip("位置跟随的平滑时间(秒)。0为立即移动。")]
[Range(0f, 2f)]
public float positionSmoothTime = 0.1f;
[Header("尺寸控制")]
[Tooltip("启用后,将尝试根据与相机的距离调整缩放,以保持视觉大小一致。")]
public bool scaleWithDistance = false;
[Tooltip("参考距离。在此距离下,UI的缩放为1。")]
public float referenceDistance = 5f;
[Tooltip("缩放平滑时间(秒)。")]
[Range(0f, 2f)]
public float scaleSmoothTime = 0.2f;
// 用于平滑计算的私有变量
private Quaternion _targetRotation;
private Vector3 _rotationSmoothVelocity; // 用于Quaternion.Slerp的模拟速度(这里简化处理,实际用插值系数)
private Vector3 _positionSmoothVelocity;
private float _currentScale = 1f;
private float _scaleSmoothVelocity;
private Transform _myTransform;
private RectTransform _rectTransform; // 如果是UI Canvas,可能需要获取
// ... [所有SerializedField参数同上] ...
[Header("性能")]
public bool updateOnlyWhenVisible = true;
public float maxUpdateDistance = 100f;
private float _sqrMaxUpdateDistance;
private Transform _transform;
private Camera _cam;
private Renderer _renderer; // 可选,用于可见性检测
private Quaternion _targetRot;
private Vector3 _posSmoothVel;
private float _scaleSmoothVel;
private float _currentScaleFactor = 1f;
void Awake()
{
_transform = transform;
_renderer = GetComponent<Renderer>(); // 可能为null,如果是Canvas
_sqrMaxUpdateDistance = maxUpdateDistance * maxUpdateDistance;
}
void Start()
{
if (targetCamera == null) targetCamera = Camera.main;
_cam = targetCamera;
if (_cam == null) Debug.LogError("No camera assigned and no Main Camera found!", this);
_targetRot = _transform.rotation;
_currentScaleFactor = _transform.localScale.x;
}
void LateUpdate()
{
if (_cam == null) return;
// 1. 距离裁剪
float sqrDist = (_transform.position - _cam.transform.position).sqrMagnitude;
if (sqrDist > _sqrMaxUpdateDistance) return;
// 2. 可见性裁剪 (粗略)
if (updateOnlyWhenVisible && _renderer != null && !_renderer.isVisible) return;
// 3. 位置跟随
if (followTarget != null)
{
Vector3 desiredPos = followTarget.TransformPoint(targetOffset); // 等价于 position + TransformDirection
_transform.position = positionSmoothTime > 0 ?
Vector3.SmoothDamp(_transform.position, desiredPos, ref _posSmoothVel, positionSmoothTime) :
desiredPos;
}
// 4. 计算并应用旋转
if (faceCamera)
{
CalculateStableRotation();
ApplyRotation();
}
// 5. 计算并应用缩放
if (scaleWithDistance)
{
float desiredScale = Mathf.Clamp(Vector3.Distance(_transform.position, _cam.transform.position) / referenceDistance, 0.1f, 10f);
_currentScaleFactor = scaleSmoothTime > 0 ?
Mathf.SmoothDamp(_currentScaleFactor, desiredScale, ref _scaleSmoothVel, scaleSmoothTime) :
desiredScale;
_transform.localScale = Vector3.one * _currentScaleFactor;
}
}
void CalculateStableRotation()
{
Vector3 toCamVector = _cam.transform.position - _transform.position;
if (toCamVector.sqrMagnitude < 0.001f) return;
Vector3 desiredForward = -toCamVector.normalized;
Vector3 desiredUp = worldUp;
// 防翻转处理
if (Mathf.Abs(Vector3.Dot(desiredForward, desiredUp)) > 0.9999f)
{
desiredUp = _cam.transform.right;
}
_targetRot = Quaternion.LookRotation(desiredForward, desiredUp);
}
void ApplyRotation()
{
if (rotationSmoothTime > 0)
{
float t = Mathf.Min(1, Time.deltaTime / rotationSmoothTime);
_transform.rotation = Quaternion.Slerp(_transform.rotation, _targetRot, t);
}
else
{
_transform.rotation = _targetRot;
}
}
// 在编辑器中可视化跟随偏移
void OnDrawGizmosSelected()
{
if (followTarget != null)
{
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(followTarget.TransformPoint(targetOffset), 0.1f);
Gizmos.DrawLine(followTarget.position, followTarget.TransformPoint(targetOffset));
}
}
}
总结
好记性不如烂笔头
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_37524903/article/details/170722010




