惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Simon Willison's Weblog
Simon Willison's Weblog
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
Last Week in AI
Last Week in AI
月光博客
月光博客
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
Martin Fowler
Martin Fowler
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Check Point Blog
U
Unit 42
The Register - Security
The Register - Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
D
DataBreaches.Net
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
H
Hacker News: Front Page
Recent Announcements
Recent Announcements
C
CXSECURITY Database RSS Feed - CXSecurity.com
Latest news
Latest news
小众软件
小众软件
P
Palo Alto Networks Blog
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
S
Secure Thoughts
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
O
OpenAI News
S
Securelist
云风的 BLOG
云风的 BLOG
H
Help Net Security
T
Troy Hunt's Blog

碎言

不懂就别瞎掰掰:【程序员的常识】写的什么玩意? AI 辅助编程下的程序设计与代码编写 使用 Next.js 和 Tailwind CSS 构建可编辑和删除的 ToDo 待办事项应用 探索编程新境界:MarsCode 助你一臂之力 使用 Next.js 和 Tailwind CSS 搭建静态图片展示站点并部署到 Vercel AI 辅助编程:免费工具的优缺点及官网一览 GitHub push更新总是失败,写个python脚本解决 把博客从GitHub迁移到到了vercel ComfyUI 和 Flux.1 安装与使用教程 Flux.1 入门必知:硬件、环境、模型 Git项目的子文件夹中的内容无法同步到远程仓库的解决方法 blender流体Fluid使用中没有流体、流体穿模等一些问题的解决方法 博客聚合网站:积薪,竟然关闭了! blender日常使用中的一些技巧 虽然只有我自己在用,但还是更新了碎言博客的源代码, 与其在迷茫中困惑,不如在努力中前进 差点忘了我还有一个博客... 秧歌、博客和AI 终于熬到了新手上路 好久没有更新博客了。。。 老妈大腿骨骨折,最近一直在医院护理 Hello, September! Ubuntu开机自动启动Docker容器运行WordPress Docker 简单快速安装部署WordPress Docker下安装MySQL 加碘盐能防核辐射的话,还怕什么核战争? shields.io 一个简洁、一致、清晰的徽章 Ubuntu下使用root登录ssh的设置 GitHub Actions 构建、部署 Next.js项目 我又用回了"IE"--edge ubuntu-22-live-server 设置固定IP Next项目部署GitHub pages的一些坑 整理了一个下午的收藏夹...... 短暂而忙碌的暑假即将谢幕 GitHub Actions 是干什么用的? 放弃Next.js重构博客:经验、教训与未来技术探索 关于Google LCP 问题:超过了2.5秒 验证已通过 仅用了几个小时就使用next.js重构博客的所有功能 关于使用Next.js重构博客构思的记录 使用Tailwind css和JavaScript实现一个简单的返回顶部功能 罗技K380 F1-F12 功能键锁定 PHILIPS猛禽SPK8404机械键盘手感真是超赞! 局域网的DNS设置192.168.0.1与8.8.8.8,114.114.114.114有什么区别? 史诗结合的浪漫:《长安三万里》 聊 聊 友 情 链 接 1767个中文博客的数据洞察:中文博主都喜欢干啥? python requests 设置读取超时时间 八月回顾:博客之路,一路前行 Docker基础学习笔记和常用命令汇总 vim常用命令整理 Ubuntu 22 安装Docker过程记录 使用python结合feedparser模块编写自己的私人RSS订阅程序 博客大数据分析:38880条博文揭示博主最爱写什么? Windows下国产杀毒软件还有选择吗?火绒安全可以一试。 export exports module.exports 这三个命令的区别是什么如何使用? 时代的变迁:从56K拨号上网到1G光纤的回忆 理工计算机专业准大学生如何选配笔记本? 使用JavaScript编写自己的私人RSS订阅程序 如何使用CSS为博客标题设置彩虹颜色? 最终还是决定使用传统的网线来解决千兆速率问题。 1300M USB双频2.4+5G无线网卡的一些常识和使用技巧 Python Gui新框架:NiceGui之初体验 pycharm 正在关闭项目(closing project)无法关闭退出程序的解决方法! 速度与优雅:博客性能优化之旅 久了,博客主题就会一直做减法 七月的焦虑 在react中使用复制插件clipboard.js react中如把HTML代码转成字符串进行渲染? react useEffect()中使用lodash库中的防抖debounce react useEffect() 为什么在开发模式的时候会执行两次? 七年之痒!别了我曾深爱的MacBookPro 前端React+Material UI开发环境的快速搭建 一键更新Github部署的静态博客 编程新手学习过程中如何更好的提问? Typed.js 一款有意思的网页打字机效果库 解决错误 ERROR [WinError 10049] 在其上下文中,该请求的地址无效。 Stable Diffusion 相关教程与模型的记录 Stable Diffusion ControlNet openpose 报错无法使用 在react或vue中使用localStorage存储数据 使用React+Vite+TailwindCSS+daisyUI打造一个简单的ToDos 碎言静态博客 聊聊Python中的装饰器 npm更换源与nrm安装与配置 pip临时修改下载源 Vue快速学习笔记(9)Vuetify配置及简单使用 Vue快速学习笔记(8)webpack4.X配置Vue开发打包环境 Vue快速学习笔记(5)使用路由vue-router(1) Vue快速学习笔记(4)Vue组件中的数据传递 Vue快速学习笔记(3)Vue的组件 Vue快速学习笔记(2)Vue中的监听与计算属性 Vue快速学习笔记(1)Vue实例基础 基于Django构建的小学生英文单词背写检测考试系统。 如何在Python程序中使用配置文件.ini(ConfigParser) 写在我的博客PV10万时:聊聊编程与画画 python3函数学习笔记简单总结 PythonINI配置文件读写的简单方法 Python open()函数文件打开、读、写操作总结 Python os.path模块中一些常用方法的整理总结 Python 采集数据三步曲之[Requests + re.py模块进行数据采集] Python 采集数据三步曲之[Requests开源协议的HTTP 库]
Godot 4 信号(Signal)的使用入门
J.sky · 2025-12-30 · via 碎言

信号(Signal)是Godot引擎中最强大、最重要的机制之一,它实现了观察者模式,让节点之间可以松耦合地通信。本教程将通过实际案例,带你全面掌握Godot中的信号使用方法。

什么是信号?

信号是一种事件通知机制。当一个节点发生某些事情时(比如按钮被点击、分数改变、动画播放完成等),它可以发出一个信号,其他节点可以”监听”这个信号,并在信号发出时执行相应的代码。

信号的核心优势:

  • 解耦:节点之间不需要直接引用,降低代码耦合度
  • 灵活:一个信号可以被多个节点监听
  • 清晰:代码结构更清晰,易于维护

一、内置信号的使用

Godot的许多节点都内置了信号,比如Button节点就有pressed信号。

1.1 场景结构

我们的main.tscn场景包含:

  • 一个Button节点(按钮)
  • 一个Label节点(标签),绑定label.gd脚本

signal

1.2 在编辑器中连接信号

signal

选中button后,在节点面板中设置信号

这表示:当Buttonpressed信号发出时,调用Label节点的_on_button_pressed方法。

1.3 编写响应函数

label.gd中,我们定义了响应函数:

extends Label

func _on_button_pressed() -> void:
    # 这里就是按钮点后会执行代码的地方
    self.text = "按钮被点击啦!!!!!"

运行效果:

  • 点击按钮
  • Button发出pressed信号
  • Label_on_button_pressed函数被调用
  • 标签文本更新为”按钮被点击啦!!!!!“

signal

二、自定义信号

除了内置信号,我们还可以创建自己的信号,用于传递自定义事件。

2.1 声明自定义信号

score_manager.gd中,我们声明了一个带参数的信号:

extends Node

# 声明一个带参数的自定义信号,参数类型是整数 (int)
signal score_updated(new_score: int)

var current_score = 0

2.2 发出信号

当分数改变时,我们发出信号并传递新的分数值:

func increase_score(points: int):
    current_score += points
    # 当分数改变时,发出信号,并附带新的分数值
    emit_signal("score_updated", current_score)

这里使用了emit_signal()函数,第一个参数是信号名称,后面是传递给信号的参数。

注意:在Godot 4中,你也可以直接使用信号名称来发出信号:

score_updated.emit(current_score)

2.3 连接自定义信号

score_label.gd中,我们连接到这个信号:

extends Label
@onready var score_manager: Node2D = $".." #连接到场景中的根节点ScoreManager

func _ready():
    # 在 _ready 函数中连接信号
    # 当 score_manager 发出 score_updated 信号时,执行 _on_score_manager_score_updated
    score_manager.score_updated.connect(_on_score_manager_score_updated)

# 这是我们响应信号的函数
func _on_score_manager_score_updated(new_score: int):
    # 更新 Label 的文本,显示新的分数
    self.text = "Score: " + str(new_score)

关键点解析:

  • @onready:延迟初始化,确保节点树已经构建完成
  • connect():连接信号的方法
  • 响应函数的参数必须与信号声明的参数一致

2.4 测试信号

score_manager.gd_ready函数中,我们测试信号:

func _ready() -> void:
    increase_score(10) # ScoreLabel 显示 Score: 10
    await get_tree().create_timer(2.0).timeout  # 等待2秒
    increase_score(5)  # ScoreLabel 显示 Score: 15
    await get_tree().create_timer(2.0).timeout  # 等待2秒
    increase_score(5)  # ScoreLabel 显示 Score: 20

运行效果:

  • 场景加载后,分数立即变为10
  • 等待2秒后,分数变为15
  • 再等待2秒后,分数变为20

三、信号的两种连接方式

3.1 在编辑器中连接(可视化方式)

步骤:

  1. 选择要发出信号的节点(如Button)
  2. 在右侧”节点”面板中,找到”信号”选项卡
  3. 双击要连接的信号(如pressed)
  4. 选择接收信号的节点和方法
  5. Godot会自动生成响应函数的框架代码

优点:

  • 可视化操作,直观易懂
  • 不需要写连接代码
  • 适合简单的信号连接

3.2 在代码中连接(编程方式)

score_manager.score_updated.connect(_on_score_manager_score_updated)

优点:

  • 更灵活,可以在运行时动态连接
  • 可以传递额外的参数
  • 适合复杂的信号连接

四、信号的高级用法

4.1 断开信号连接

如果不再需要监听某个信号,可以断开连接:

score_manager.score_updated.disconnect(_on_score_manager_score_updated)

4.2 一次性信号

使用call_deferred可以让信号只触发一次:

button.pressed.connect(_on_button_pressed, CONNECT_ONE_SHOT)

4.3 信号带多个参数

信号可以传递多个参数:

signal player_died(player_name: String, score: int, cause: String)

emit_signal("player_died", "Player1", 100, "Enemy")

4.4 检查信号是否已连接

if score_manager.score_updated.is_connected(_on_score_manager_score_updated):
    print("信号已连接")

五、最佳实践

5.1 命名规范

  • 信号名称使用动词形式:score_updatedplayer_diedgame_over
  • 响应函数命名:_on_节点名_信号名,如_on_button_pressed

5.2 什么时候使用信号?

适合使用信号的场景:

  • UI交互(按钮点击、输入框变化)
  • 游戏状态变化(分数更新、生命值改变)
  • 动画事件(动画播放完成、帧事件)
  • 碰撞检测(进入/退出碰撞区域)

不适合使用信号的场景:

  • 需要返回值的操作
  • 高频调用的每帧逻辑(使用_process_physics_process

5.3 性能考虑

信号连接本身开销很小,但要注意:

  • 避免在_process中频繁连接/断开信号
  • 大量信号监听者可能影响性能

六、总结

通过本教程,我们学习了:

  1. 内置信号:使用Godot预定义的信号(如Button的pressed)
  2. 自定义信号:创建和发出自己的信号
  3. 信号连接:在编辑器和代码中连接信号
  4. 信号参数:传递数据给信号监听者
  5. 高级用法:断开连接、一次性信号、多参数信号

信号是Godot开发中的核心概念,掌握它将让你的代码更加模块化、可维护。


项目文件说明

本教程项目包含以下文件:

  • main.tscn:演示内置信号(Button pressed)
  • label.gd:响应按钮点击信号
  • score_manager.tscn:演示自定义信号
  • score_manager.gd:定义和发出自定义信号
  • score_label.gd:监听并响应自定义信号

祝你在Godot开发之路上越走越远! 🚀