Godot中碰撞触发动画后调用queue_free释放节点的实现方法
Godot增益道具碰撞触发收集动画实现方案
核心思路:不要在玩家脚本里跨场景硬引用道具的动画节点,把触发、播放、销毁逻辑全部收敛在道具自身脚本里,从根源上避免引用失败的问题,耦合度最低也最不容易出bug。
具体实现步骤
- 道具场景结构调整
增益道具(powerup)统一用Area2D(2D项目)/Area3D(3D项目)作为根节点,下面挂Sprite2D/Sprite3D渲染节点、CollisionShape2D/CollisionShape3D碰撞节点、AnimationPlayer动画节点,所有相关节点都放在道具自身场景下,不要散落到其他场景。
提前给动画播放器做好缩放消失的动画,命名为collect,动画最后一帧插入调用方法轨道,直接绑定根节点的queue_free()方法,动画播完会自动销毁节点,不用手写延迟计时。 - 碰撞检测配置
给玩家节点打上player分组,给道具根节点配置碰撞层/掩码,确保能检测到玩家的物理体;道具根节点开启monitoring属性,专门用来做触发检测,不需要开刚体模拟,性能更好。 - 道具脚本逻辑(直接挂在powerup根节点上,Godot 4.x示例)
extends Area2D # 拖拽场景内的AnimationPlayer到属性面板赋值,不用手写节点路径 @onready var anim_player: AnimationPlayer = $AnimationPlayer var is_collected: bool = false # 加个标记防止重复触发 func _ready(): # 连接自身的碰撞进入信号,不需要去玩家那边连 body_entered.connect(_on_trigger_collect) func _on_trigger_collect(collide_body: Node2D): # 撞上来的不是玩家、或者已经被收集过,直接跳过 if not collide_body.is_in_group("player") or is_collected: return is_collected = true # 第一时间关闭碰撞检测,避免重复触发 set_deferred("monitoring", false) # 直接播放本地存的收集动画,根本不需要玩家脚本过来调用 anim_player.play("collect") # 要加增益效果直接在这里写就行,collide_body就是撞到的玩家节点 # 比如 collide_body.apply_power_buff()、GameManager.add_score(10)
如果你一定要在玩家脚本里触发动画的兼容方案
不推荐这种写法,耦合度很高,容易因为场景结构调整报空引用,如果要写,绝对不要硬编码全局节点路径,碰撞触发时直接从碰撞到的道具节点上取动画播放器即可,示例:
# 挂在玩家节点上的脚本 extends CharacterBody2D func _ready(): body_entered.connect(_on_hit_object) func _on_hit_object(body: Node2D): # 提前给所有增益道具打"powerup"分组 if body.is_in_group("powerup"): # 从碰撞到的道具节点上找动画播放器,不要全局搜 var anim_player = body.get_node_or_null("AnimationPlayer") if anim_player: body.set_deferred("monitoring", false) anim_player.play("collect")
常见引用失败的排查点
- 报空节点错误:要么是AnimationPlayer的节点路径写的不对,要么是在节点加载进场景树之前就提前获取了节点引用,统一用
@onready修饰节点获取逻辑即可 - 动画没播完节点就消失:不要在触发播放的第一时间直接调
queue_free(),要么在动画尾帧加方法调用,要么写await anim_player.animation_finished之后再调用销毁 - 一次碰撞多次触发动画:触发第一时间关闭碰撞检测,或者加布尔值标记拦截重复调用
内容的提问来源于stack exchange,提问作者Litch 50
相关产品推荐
相关产品推荐

