You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何通过协程实现半径值的平滑同步?

Unity OnValidate中半径平滑过渡失效的解决方法

问题背景

在Unity的OnValidate方法中调用SyncRadii时,xradius与yradius会直接跳变至changeBothRadius的数值,无法实现平滑过渡。尝试通过协程结合Mathf.Lerp插值的方案后,问题依然存在。

原代码片段

OnValidate方法

private void OnValidate()
{
    // Handle changes to changeBothRadius
    if (changeBothRadius != previousBothRadius)
    {
        SyncRadii(); // Sync radii when changeBothRadius changes
        previousBothRadius = changeBothRadius;
    }
    else if (xradius != previousXradius || yradius != previousYradius)
    {
        // Allow independent changes to xradius and yradius
        Debug.Log($"Updating radii: X = {xradius}, Y = {yradius}");
        previousXradius = xradius;
        previousYradius = yradius;
    }

    // Update LineRenderer and object positions
    if (line != null && draw)
    {
        ConfigureLineRenderer();
        CreatePoints();
        AdjustPositions(numOfObjects, transform.position, xradius, yradius); // Pass both radii
    }
}

原SyncRadii方法

void SyncRadii()
{
    xradius = changeBothRadius;
    yradius = changeBothRadius;

    previousXradius = xradius;
    previousYradius = yradius;
}

修改后的SyncRadii及协程(无效版本)

void SyncRadii()
{
    // Start the coroutine to smooth the radius change
    StopAllCoroutines(); // Stop any ongoing radius adjustments
    StartCoroutine(SmoothSyncRadii(changeBothRadius, 0.5f)); // Adjust over 0.5 seconds

    previousXradius = xradius;
    previousYradius = yradius;
}
private IEnumerator SmoothSyncRadii(float targetRadius, float duration)
{
    float elapsedTime = 0f;

    float startXRadius = xradius;
    float startYRadius = yradius;

    while (elapsedTime < duration)
    {
        elapsedTime += Time.deltaTime;
        float t = elapsedTime / duration;

        // Smoothly interpolate the radii
        xradius = Mathf.Lerp(startXRadius, targetRadius, t);
        yradius = Mathf.Lerp(startYRadius, targetRadius, t);

        yield return null; // Wait until the next frame
    }

    // Ensure the final value is set
    xradius = targetRadius;
    yradius = targetRadius;
}

问题根源

  1. 协程在编辑器非Play模式下不运行:Unity的普通协程依赖于MonoBehaviour的生命周期(如Update),但在编辑器编辑模式(未进入Play)时,这些生命周期函数不会触发,导致协程代码完全不执行。
  2. Time.deltaTime在编辑器模式下为0:即使协程能运行,Time.deltaTime在编辑模式下始终为0,插值计算无法推进,最终直接跳转到目标值。
  3. previousXradius/Yradius的即时赋值:在SyncRadii中立即将previousXradius设为当前xradius,会导致后续插值修改无法触发OnValidate的更新逻辑。

解决方案

使用编辑器专用的更新回调EditorApplication.update来模拟帧更新,实现编辑模式下的平滑插值。

修改后的代码实现

首先引入编辑器命名空间:

using UnityEditor;

添加必要的私有变量:

private float _startXRadius, _startYRadius;
private float _targetRadius;
private float _elapsedTime;
private float _interpolationDuration = 0.5f;
private float _lastEditorUpdateTime;

修改SyncRadii方法:

void SyncRadii()
{
    // 停止之前的插值回调,避免冲突
    EditorApplication.update -= UpdateSmoothRadiiInEditor;

    // 记录插值起始值与目标值
    _startXRadius = xradius;
    _startYRadius = yradius;
    _targetRadius = changeBothRadius;
    _elapsedTime = 0f;
    _lastEditorUpdateTime = EditorApplication.timeSinceStartup;

    // 注册编辑器更新回调
    EditorApplication.update += UpdateSmoothRadiiInEditor;
}

实现编辑器插值逻辑:

private void UpdateSmoothRadiiInEditor()
{
    // 计算编辑器下的"帧间隔"时间
    float deltaTime = EditorApplication.timeSinceStartup - _lastEditorUpdateTime;
    _lastEditorUpdateTime = EditorApplication.timeSinceStartup;

    _elapsedTime += deltaTime;
    float t = Mathf.Clamp01(_elapsedTime / _interpolationDuration);

    // 执行平滑插值
    xradius = Mathf.Lerp(_startXRadius, _targetRadius, t);
    yradius = Mathf.Lerp(_startYRadius, _targetRadius, t);

    // 实时更新LineRenderer与物体位置
    if (line != null && draw)
    {
        ConfigureLineRenderer();
        CreatePoints();
        AdjustPositions(numOfObjects, transform.position, xradius, yradius);
    }

    // 插值完成后清理回调并更新状态
    if (t >= 1f)
    {
        EditorApplication.update -= UpdateSmoothRadiiInEditor;
        xradius = _targetRadius;
        yradius = _targetRadius;
        previousXradius = xradius;
        previousYradius = yradius;
        previousBothRadius = changeBothRadius;
    }
}

额外注意事项

  • 如果需要在Play模式下也生效,可以在SyncRadii中判断当前模式,分别使用协程(Play模式)或编辑器回调(编辑模式)。
  • 若用户在插值过程中再次修改changeBothRadius,SyncRadii会自动停止之前的插值并启动新的,避免冲突。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:35:16