如何正确传递事件作为参数?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
相关产品推荐
相关产品推荐

