需求:
当用户距离目标位置越近,参数值越大。
可用于控制场景亮度、动画进度、交互强度等多种效果。
一、需求背景:如何让“距离”成为设计的一部分?
在虚拟现实(VR)、增强现实(AR)乃至普通的 3D 游戏开发中,“距离”作为一个极具表现力的交互维度,常常被用于:
- 视觉反馈:靠近某个目标时逐渐亮起或高亮;
- 交互触发:靠近 NPC 时自动播放语音或弹出交互 UI;
- 动画控制:根据接近程度控制动画播放的速度或进度;
- 声音控制:实现空间音效的衰减或增强;
- 镜头拉伸:远距离拉远视角,近距离拉近细节。
虽然 Unity 本身提供了 Vector3.Distance
等简单的距离计算方法,但要在实际项目中将“距离”转化为一个 可控、可调、可视化 的值(如 [0~1] 区间),并支持动画曲线调节,这就需要一个通用的组件来简化开发工作。
因此,我设计并实现了这个通用组件 —— InverseDistanceMapper
。
二、需求分析:参数拆解与功能设定
为了让该工具既能在项目中高效复用,又具备高度灵活性与拓展性,我们将需求拆解如下:
1. 核心功能:
- 计算 XR Rig 中的 Camera 与目标位置(Transform)之间的距离;
- 距离越小,映射值越大(反距离关系);
- 输出参数值在 [0, 1] 区间;
- 支持通过
AnimationCurve
来调整映射曲线,实现非线性响应; - 支持设置最大距离与最小距离阈值,超出部分 Clamp;
- 可视化实时调试当前值。
2. 技术设计:
- 使用协程
Coroutine
按固定周期计算距离(默认 0.05s); - 提供
public float CurrentValue
供其他组件读取; - 支持默认使用
Camera.main
,也支持自定义 Camera 引用; - 使用
DisallowMultipleComponent
限制重复挂载; - 保证禁用组件时自动停止协程,释放资源。
3. 适用场景拓展:
- 场景照明渐变控制;
- UI 动画播放进度;
- 近距离震动或特效增强;
- 多人交互中基于距离的“存在感”控制;
- NPC 与玩家距离感知驱动行为变化等。
三、实现过程:InverseDistanceMapper 组件脚本设计
我们接下来将脚本分为以下几个模块进行说明:
1. 脚本结构总览:
[DisallowMultipleComponent]
public class InverseDistanceMapper : MonoBehaviour
{
public Transform targetTransform;
public Camera xrCamera;
public float minDistance = 0f;
public float maxDistance = 10f;
public AnimationCurve distanceToValueCurve;
public float updateInterval = 0.05f;
[Range(0f, 1f)]
public float currentValue = 0f;
private Coroutine updateRoutine;
...
}
说明:
targetTransform
是我们计算目标的参考点;xrCamera
是玩家视角对应的相机;minDistance
与maxDistance
构成我们的距离范围;AnimationCurve
允许我们使用 Unity 的曲线编辑器进行非线性值控制;updateInterval
控制计算的频率,防止每帧更新导致性能浪费;currentValue
是最终我们要输出的值。
2. 核心算法实现(UpdateValue):
void UpdateValue()
{
if (xrCamera == null || targetTransform == null)
{
currentValue = 0f;
return;
}
float distance = Vector3.Distance(xrCamera.transform.position, targetTransform.position);
float clamped = Mathf.Clamp(distance, minDistance, maxDistance);
float t = Mathf.InverseLerp(minDistance, maxDistance, clamped);
currentValue = distanceToValueCurve.Evaluate(t);
}
- 使用
Mathf.Clamp
限制实际距离在合法区间; - 使用
Mathf.InverseLerp
将距离映射为 [0, 1]; - 使用
AnimationCurve.Evaluate(t)
进行最终值映射。
3. 启动与协程更新逻辑:
void OnEnable()
{
if (xrCamera == null)
xrCamera = Camera.main;
if (xrCamera != null && targetTransform != null)
updateRoutine = StartCoroutine(UpdateDistanceRoutine());
}
IEnumerator UpdateDistanceRoutine()
{
WaitForSeconds wait = new WaitForSeconds(updateInterval);
while (true)
{
UpdateValue();
yield return wait;
}
}
协程中每 updateInterval
秒执行一次更新。
四、编辑器使用指南
- 将
InverseDistanceMapper
挂载在任意 GameObject 上; - 指定
TargetTransform
,可以是 NPC、按钮、物品等; - 若不设置
camera
,默认会使用Camera.main
; - 设置
minDistance
(一般为 0)和maxDistance
(如 5~10); - 调整
distanceToValueCurve
(可使用 Ease Out、SmoothStep 等); - 在运行时观察
CurrentValue
实时变化; - 通过其他组件(如动画控制器、灯光控制器)读取该值进行联动。
五、应用示例场景
1. 场景亮度随距离增强
public Light sceneLight;
public InverseDistanceMapper mapper;
void Update()
{
sceneLight.intensity = Mathf.Lerp(0.2f, 2.0f, mapper.GetValue());
}
2. 动画播放进度驱动器
public Animator targetAnimator;
public InverseDistanceMapper mapper;
void Update()
{
targetAnimator.Play("Reveal", 0, mapper.GetValue());
}
3. UI 提示渐变显示
public CanvasGroup hintCanvas;
public InverseDistanceMapper mapper;
void Update()
{
hintCanvas.alpha = mapper.GetValue();
}
4. 声音音量与距离联动
public AudioSource proximityAudio;
public InverseDistanceMapper mapper;
void Update()
{
proximityAudio.volume = mapper.GetValue();
}
六、性能与扩展建议
1. 性能优化建议:
- 控制
updateInterval
,避免每帧更新; - 多个 Mapper 可集中统一管理;
- 可选择用
Update()
替代协程,仅在性能可接受范围。
2. 扩展方向建议:
- 添加事件系统,例如当
Value > 某阈值
时触发事件; - 支持“多目标权重加权”,实现复杂距离控制;
- 支持 VR 控制器、手势、视线等多种输入源接入;
- 提供自定义 Inspector 可视化调试功能。
七、总结
InverseDistanceMapper
是一个轻量且实用的组件,它将用户与目标之间的距离映射为 0~1 区间的值,具备高度灵活性和可视化调节能力。借助 Unity 内置的 AnimationCurve
,开发者可以实现线性、缓出、弹性等多种响应模式,极大地增强交互体验。
无论你是想控制场景亮度、动画进度、声音音量,还是用于交互触发器、HUD 提示控制,这个组件都能成为你项目中的一把利器。