取消延时执行之坑

博客提到启动3S延时执行,在做取消操作时存在两个坑,一是要在主线中执行取消,二是需注意参数的一致性,否则无法取消。

启动3S延时执行有一份

[self performSelector:@selector(didRuninCurrModel:) withObject:@(YES) afterDelay:3.0f];

在做取消操作时有两个坑

  • 1,要在主线中执行取消

  • 2,需要注意的是参数的一致性否则取消不掉

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModel:) object:@(YES)];
<think>嗯,用户问的是Unreal Engine蓝图中Latent动作的完成引脚为什么不执行。这个问题其实挺常见的,尤其是刚接触UE蓝图延迟功能的新手容易遇到。用户之前提到过“完成节点不执行”,现在又聚焦到Latent动作的完成引脚,说明他正在调试蓝图流程控制问题。 从用户引用的材料来看,他应该已经查过一些资料了[^1][^2][^3],特别是关于异步节点的特点[^2]和Latent Function的实现方式[^3]。不过这些引用内容比较零散,需要帮ta系统梳理。 核心问题其实有五个关键点:首先是WorldContext失效,这是最常见的原因。Latent动作需要有效的WorldContext对象来维持延迟调用链,如果这个对象中途被销毁(比如包含蓝图的Actor被Kill),完成引脚自然就不会触发。这点在引用[3]提到的Delay函数实现里也能看出端倪。 其次是执行条件被跳过的情况。比如用户可能在Branch节点后直接连了Latent节点,但没注意条件分支的变化。或者更隐蔽的——Sequence节点中某个分支提前完成并重置了执行流。 第三点是蓝图生命周期管理问题。用户可能没注意到蓝图实例本身被卸载了,或者关卡切换导致执行上下文丢失。特别是当Latent动作跨关卡时就更容易出问题。 第四类错误是循环阻塞。比如在Tick里不断触发Latent动作但没设终止条件,结果前一个动作还没完成就被新的覆盖了。引用[2]提到异步节点不能用在函数里也是类似原理——函数执行是原子的,不适合做持续状态跟踪。 最后是引擎bug的可能性。虽然概率低,但像引用[3]说的那种历史遗留的Latent Function实现方式,在某些版本确实存在内存泄漏问题。不过应该先排除前四种情况再考虑这个。 调试建议方面,除了常规的打印日志,特别要提醒用户用“Begin Destroyed”事件跟踪对象生命周期。因为Latent动作依赖的UObject如果被垃圾回收,在编辑器中往往没有明显报错,这是最的地方。 用户可能还需要注意:某些Latent节点(如Delay)在PIE模式下结束时会自动取消,但正式运行不会。所以测试时如果只在PIE调试可能会遗漏问题。 对了,他引用的材料[^1]里提到行为树,虽然当前问题集中在蓝图,但原理是相通的——都是执行流的挂起与恢复机制。这点在最后总结时可以呼应下。</think>在 Unreal Engine 蓝图中,Latent 动作(如 **Delay**、**Timeline**、**异步加载**等节点)的**完成引脚未执行**通常由以下原因导致,按发生频率排序: --- ### 一、核心原因与解决方案 #### 1. **World Context 对象被销毁**(最常见) Latent 动作依赖 `WorldContextObject`(通常是蓝图所属的 **Actor** 或 **Level Blueprint**)。若该对象在延迟期间被销毁(如 `DestroyActor`),完成引脚将静默失败。 ✅ **解决方案**: - 检查 World Context 对象是否存活: ```cpp // 在调用 Latent 动作前验证 if (!IsValid(GetWorldContextObject())) { return; // 提前终止 } ``` - 避免在即将销毁的对象上启动延迟操作。 #### 2. **执行流被中断** - **条件分支跳过完成引脚**: ```mermaid graph LR A[开始] --> B{条件} B -->|true| C[Delay 节点] B -->|false| D[其他节点] C -->|完成| E[目标操作] ``` 当条件为 `false` 时,`Delay` 节点**根本不会执行**,其完成引脚自然失效。 - **Sequence 节点提前结束**:若 Sequence 中某分支包含 Latent 动作,其他分支完成后会重置整个 Sequence[^3]。 #### 3. **蓝图实例被卸载** - 包含 Latent 动作的蓝图实例(如 **Widget Blueprint**)在延迟结束前被移除(`RemoveFromParent`),导致回调失效。 ✅ **解决方案**: - 在卸载前取消延迟操作(使用 **Cancel Latent Actions** 节点)。 - 使用 `Async Task` 替代传统 Latent 动作(更安全的生命周期管理)[^3]。 #### 4. **循环阻塞问题** 在 **Tick** 事件中重复触发 Latent 动作(如每帧调用 `Delay`),导致多个延迟任务堆叠竞争: ```mermaid graph TB Tick --> Delay1 Tick --> Delay2 Delay1 -->|完成| 操作1 Delay2 -->|完成| 操作2 // 可能被覆盖 ``` ✅ **解决方案**: - 添加状态锁防止重入: ```cpp bool bIsDelayRunning = false; // 蓝图变量 // Tick 事件 if (!bIsDelayRunning) { bIsDelayRunning = true; Start Delay(1.0f) -> OnComplete: { // 执行操作... bIsDelayRunning = false; } } ``` #### 5. **引擎 Bug 或版本问题** 特定版本存在 Latent 动作回调失效的 Bug(如 UE4.25 的部分 Hotfix 版本)。 ✅ **解决方案**: - 升级引擎版本或应用官方补丁。 - 用 **Blueprint Async Node** 重构逻辑(更稳定的异步机制)[^3]。 --- ### 二、调试技巧 1. **添加日志追踪**: - 在 Latent 动作**开始**和**完成**时打印日志: ``` [开始] Delay 已启动 [完成] Delay 结束 // 若未出现则验证中断点 ``` 2. **检查对象生命周期**: - 在 World Context 对象上添加 **OnDestroy** 事件,排查是否提前销毁。 3. **使用断点调试**: - 在蓝图节点上右键选择 **Add Breakpoint**,逐步跟踪执行流。 --- ### 三、最佳实践 ```mermaid graph LR A[启动Latent动作] --> B{验证对象有效性?} B -->|有效| C[执行延迟操作] B -->|无效| D[终止流程] C --> E[完成回调] E --> F[执行业务逻辑] ``` 1. **始终验证 World Context**: ```cpp // 安全调用 Delay 的模板 if (IsValid(GetOwner())) { UKismetSystemLibrary::Delay(GetOwner(), 1.0f, ...); } ``` 2. **优先使用 Async Task**: ```cpp // 更安全的异步封装(UE5+) UMyAsyncTask* Task = UMyAsyncTask::DoTask(GetWorld()); Task->OnComplete.AddDynamic(this, &AMyActor::HandleComplete); ``` 3. **避免在 Tick 中触发延迟操作**:改用状态机或事件驱动。 > 💡 若问题仍存在,尝试在 `Project Settings > Packaging > 取消勾选 "Use Latent Action Manager"` 强制重启引擎(临时解决方案)[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值