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

如何正确传递事件作为参数?Unity触发事件关联缩放停止实现疑问

关于聚焦缩放与Trigger事件联动的方案见解

嘿,你的这个思路方向是合理的——通过调用者的OnTriggerExit来终止聚焦缩放流程,本质上是事件驱动的生命周期管理,很贴合Unity里的触发场景。不过原方案的实现细节还有优化空间,我来拆解下可行的思路和注意点:

原方案的潜在问题

你当前的代码里OnStopFocusing委托只是被注释了赋值,没有实际关联停止缩放的逻辑;另外如果直接让调用者传递事件,容易造成调用者和聚焦逻辑的耦合过高——比如调用者需要知道聚焦内部的停止细节,不符合单一职责原则。

更优雅的实现方式

1. 完善委托回调逻辑

首先把OnStopFocusing委托真正用起来,在聚焦流程里保存这个回调,同时在FocusOnTarget的结束逻辑(或者手动停止时)触发它,还要记得恢复角色移动:

// 先定义你的委托
public delegate void OnStopFocusing();

// 新增私有变量保存当前的停止回调与聚焦状态
private OnStopFocusing _currentStopCallback;
private bool _isFocusing;

public void FocusTargetZoom(Transform target, float focusTime, float zoomFactorPercentage, OnStopFocusing onStopFocusing = null, bool lockMovement = true) {
    // 如果正在聚焦,先终止之前的流程
    if (_isFocusing) {
        StopFocusZoom();
    }

    _isFocusing = true;
    _currentStopCallback = onStopFocusing;

    if (lockMovement) {
        Character.Instance.LockMovement();
    }

    zoomFactor = initFOV * zoomFactorPercentage / 100f;
    FocusOnTarget(target, focusTime);
}

// 封装停止缩放的核心逻辑
private void StopFocusZoom() {
    if (!_isFocusing) return;

    _isFocusing = false;
    // 恢复默认视场角(假设initFOV是默认值)
    Camera.main.fieldOfView = initFOV;
    // 解锁角色移动
    Character.Instance.UnlockMovement();
    // 触发外部传入的停止回调
    _currentStopCallback?.Invoke();
    // 清空回调引用避免内存泄漏
    _currentStopCallback = null;
}

2. 让调用者在OnTriggerExit中触发停止

调用者(带Trigger的对象)只需要在合适的时机调用停止方法即可,不用关心内部细节,实现解耦:

// 调用者的脚本示例
public class TriggerFocus : MonoBehaviour {
    public CameraFocus cameraFocus; // 你的聚焦脚本引用

    private void OnTriggerEnter(Collider other) {
        if (other.CompareTag("Player")) {
            // 调用聚焦方法,可传入自定义的停止回调(比如播放音效、UI提示)
            cameraFocus.FocusTargetZoom(transform, 1f, 50f, () => {
                Debug.Log("聚焦已停止");
            });
        }
    }

    private void OnTriggerExit(Collider other) {
        if (other.CompareTag("Player")) {
            // 触发停止缩放逻辑
            cameraFocus.StopFocusZoom();
        }
    }
}

3. 可选:用CancellationToken实现更安全的协程终止

如果你的FocusOnTarget是用协程实现的,可以结合CancellationToken来更安全地终止协程,避免无效逻辑继续执行:

private CancellationTokenSource _cts;

public void FocusTargetZoom(Transform target, float focusTime, float zoomFactorPercentage, OnStopFocusing onStopFocusing = null, bool lockMovement = true) {
    if (_isFocusing) {
        StopFocusZoom();
    }

    _isFocusing = true;
    _currentStopCallback = onStopFocusing;
    _cts = new CancellationTokenSource();

    if (lockMovement) {
        Character.Instance.LockMovement();
    }

    zoomFactor = initFOV * zoomFactorPercentage / 100f;
    StartCoroutine(FocusOnTargetCoroutine(target, focusTime, _cts.Token));
}

private IEnumerator FocusOnTargetCoroutine(Transform target, float focusTime, CancellationToken token) {
    // 你的渐进式缩放逻辑示例
    float elapsedTime = 0f;
    float startFOV = Camera.main.fieldOfView;
    while (elapsedTime < focusTime && !token.IsCancellationRequested) {
        elapsedTime += Time.deltaTime;
        float t = elapsedTime / focusTime;
        Camera.main.fieldOfView = Mathf.Lerp(startFOV, zoomFactor, t);
        yield return null;
    }

    // 如果是主动取消,执行停止逻辑
    if (token.IsCancellationRequested) {
        StopFocusZoom();
    }
}

private void StopFocusZoom() {
    if (!_isFocusing) return;

    // 终止并清理CancellationToken
    _cts?.Cancel();
    _cts?.Dispose();
    _cts = null;

    _isFocusing = false;
    Camera.main.fieldOfView = initFOV;
    Character.Instance.UnlockMovement();
    _currentStopCallback?.Invoke();
    _currentStopCallback = null;
}

关键注意点

  • 状态同步:一定要用_isFocusing标记当前是否在聚焦,避免重复调用或无效停止操作。
  • 资源清理:如果使用协程或CancellationToken,记得在停止时释放资源,防止内存泄漏。
  • 解耦设计:调用者只需要知道“启动聚焦”和“停止聚焦”两个方法,不用关心内部的缩放、锁移动逻辑,后续修改聚焦逻辑时,调用者不需要改动代码。

这个方案是完全可行的,只要把委托回调和状态管理做好,就能稳定实现你想要的“进入Trigger启动聚焦,退出Trigger停止聚焦”的流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 13:42:52