- 文档
- 教程
- 游戏开发
【免费下载链接】godot-docs
Godot Engine official documentation
VisibleOnScreenNotifier2D是 Godot Engine 官方文档(本仓库 godot-docs)中定义的 2D 节点类,用于检测一个矩形区域是否出现在屏幕或视口内,并在状态变化时发出screen_entered/screen_exited信号。本文以 classes/class_visibleonscreennotifier2d.rst 为核心,结合仓库内 2D/3D 官方教程与相关类的源码文档,讲解该节点的属性、信号、方法、底层原理与实战用法;读完后你将掌握用它实现「离屏对象自动回收」「按可见性控制游戏逻辑」等典型方案。
一、类概览与继承体系
VisibleOnScreenNotifier2D表示 2D 空间中的一个矩形区域:当该区域的任意部分进入屏幕(或视口)时,节点发出screen_entered信号;当没有任何部分留在屏幕上时,发出screen_exited信号。如果你希望某个节点在区域可见时被自动启用,官方推荐使用它的子类VisibleOnScreenEnabler2D(见 classes/class_visibleonscreenenabler2d.rst)。
其继承链为:
VisibleOnScreenNotifier2D └─ Node2D └─ CanvasItem └─ Node └─ Object被继承者:
VisibleOnScreenEnabler2D(继承自 VisibleOnScreenNotifier2D)与 3D 版本对应:仓库中的 classes/class_visibleonscreennotifier3d.rst 定义了VisibleOnScreenNotifier3D(继承自VisualInstance3D),其继承体系与 2D 版本平行,属同一「屏幕可见性检测」节点家族。
二、核心工作原理:渲染剔除驱动的可见性判定
官方文档明确说明了一个关键机制:
VisibleOnScreenNotifier2D使用渲染剔除(render culling)代码来判断它是否在屏幕上,因此除非
CanvasItem.visible设置为true,否则它不会正常工作。
这意味着:
- 该节点的可见性判定不是通过独立的几何查询,而是复用渲染管线的视锥剔除结果;
- 将节点的
visible属性设为false(或隐藏其父节点)会同时禁用屏幕可见性检测,即使矩形区域实际上仍在屏幕范围内,也不会触发screen_entered; - 与之对比,
show_rect属性只影响编辑器中的可视化显示,不会影响剔除检测(详见下文)。
仓库中 classes/class_renderingserver.rst 展示了渲染服务器层面的相关能力,例如instance_set_ignore_culling(忽略剔除强制绘制)、viewport_set_use_occlusion_culling(视口遮挡剔除开关)等,可以看到剔除是渲染服务器对实例统一管理的行为——2D 的屏幕可见性通知正是依附于这一套渲染实例管理机制实现的。
3D 版本的文档(classes/class_visibleonscreennotifier3d.rst)额外提醒:3D 版本默认采用一种近似启发式判定,「不把墙壁等遮挡物考虑在内,除非启用了遮挡剔除」。2D 版本文档未作此说明,因此 2D 场景中也不应依赖该节点进行墙体遮挡判断。
三、核心属性:rect 与 show_rect
| 类型 | 属性 | 默认值 | 说明 |
|---|---|---|---|
Rect2 | rect | Rect2(-10, -10, 20, 20) | VisibleOnScreenNotifier2D 的包围矩形 |
bool | show_rect | true | 是否在编辑器中显示矩形区域 |
3.1 rect:检测区域的大小与位置
rect是检测区域本体,类型为 Godot 内置Rect2。根据 classes/class_rect2.rst 的定义,Rect2是一个二维轴对齐包围盒,由position(Vector2)与size(Vector2)两个分量构成,常用于快速重叠测试(intersects()等)。注意:Rect2不支持负的size值,负尺寸下大部分方法无法正确工作。
默认值Rect2(-10, -10, 20, 20)表示:以节点原点为中心,向四个方向各扩展 10 像素,构成一个 20×20 的方形检测区域。实战中通常需要根据精灵或碰撞体的实际尺寸来调整,例如:
# 在 GDScript 中设置检测区域:以自身位置为中心,宽 64、高 48 $VisibleOnScreenNotifier2D.rect = Rect2(-32, -24, 64, 48) # 或直接使用 set_rect / get_rect 访问器(对应文档中的 set_get 接口) $VisibleOnScreenNotifier2D.set_rect(Rect2(-32, -24, 64, 48)) var r: Rect2 = $VisibleOnScreenNotifier2D.get_rect()C# 对应写法:
var notifier = GetNode<VisibleOnScreenNotifier2D>("VisibleOnScreenNotifier2D"); notifier.Rect = new Rect2(-32, -24, 64, 48);属性访问器说明:文档中
rect同时提供了set_rect(value)与get_rect()两个方法;show_rect则对应set_show_rect(value)与is_showing_rect()。
3.2 show_rect:编辑器中的可视化辅助
show_rect默认为true,作用是在编辑器中以半透明品红色(translucent magenta)填充的方式显示rect所定义的区域,方便你直观地调整检测范围。文档特别强调:改变show_rect不会影响屏幕剔除检测——它与修改节点visible属性有本质区别。
上图展示了官方教程中为 Mob 场景添加可见性通知节点后编辑器内出现的品红色盒子,2D 版本在编辑器中同样以半透明品红矩形呈现检测区域。
四、信号:screen_entered 与 screen_exited
该节点只定义两个信号:
| 信号 | 触发时机 |
|---|---|
screen_entered() | VisibleOnScreenNotifier2D 进入屏幕时发出 |
screen_exited() | VisibleOnScreenNotifier2D 完全离开屏幕时发出 |
语义要点:
screen_entered只要区域任意部分可见即触发;screen_exited要等区域没有任何部分可见时才触发(即整个矩形完全离开屏幕或视口)。
这两种信号是生命周期类逻辑的天然钩子,最常见的用法是把离屏对象销毁掉。在官方 2D 入门教程 getting_started/first_2d_game/04.creating_the_enemy.rst 中,敌人(Mob)场景将VisibleOnScreenNotifier2D的screen_exited()信号连接到 Mob 根节点,实现「离开屏幕即自我删除」:
func _on_visible_on_screen_notifier_2d_screen_exited(): queue_free()C# 版本(在编辑器的信号连接窗口中指定 PascalCase 函数名):
private void OnVisibleOnScreenNotifier2DScreenExited() { QueueFree(); }queue_free()会在当前帧结束时释放该节点——这正是文档教程中描述的「mobs delete themselves when they leave the screen」方案。
五、方法:is_on_screen 与一帧延迟特性
该节点只有一个公开方法:
func is_on_screen() -> bool # const,无副作用若包围矩形当前在屏幕上则返回true。文档给出了一个极易踩坑的注意事项:
节点被加入场景树后,需要一帧的时间才能完成可见性判定,因此在实例化之后、绘制帧(draw pass)之前调用
is_on_screen()会始终返回false。
这意味着:
- 不要在
_ready()中立即调用is_on_screen()并依赖其结果来决定初始化逻辑; - 若需要在节点加入后马上获知可见状态,更稳妥的方式是等待
screen_entered信号,或延迟一帧(await get_tree().process_frame)后再查询; - 3D 版本 classes/class_visibleonscreennotifier3d.rst 文档也有完全相同的「一帧延迟」提醒,说明这是整个节点家族的共同行为。
典型用法示例——轮询检测当前是否可见并驱动逻辑:
func _process(_delta: float) -> void: if $VisibleOnScreenNotifier2D.is_on_screen(): # 区域内可见时执行逻辑(例如播放音效、结算得分) pass六、实战:敌人离屏自动销毁的完整流程
综合官方 2D 教程 getting_started/first_2d_game/04.creating_the_enemy.rst,标准接入步骤如下:
- 在场景树中为需要跟踪的对象(如
RigidBody2D根节点Mob)添加子节点VisibleOnScreenNotifier2D; - 在编辑器中调整
rect属性(拖动品红矩形边缘),使其完整覆盖对象的可视外形——通常应比对象略大,避免因动画摆动导致过早触发screen_exited; - 在信号面板双击
screen_exited(),将其连接到目标节点; - 在生成的回调中调用
queue_free()。
该教程还补充了对象池(object pooling)的背景知识:当反复实例化/销毁对象时,可用「预创建对象数组并循环复用」的池化技术规避创建销毁开销;而使用 GDScript 时通常不需要对象池,因为 GDScript 采用引用计数内存管理,不存在 GC 语言(如 C#)的停顿问题。
七、延伸:VisibleOnScreenEnabler2D 自动启用目标节点
如果不想自己处理信号,可直接使用子类VisibleOnScreenEnabler2D。根据 classes/class_visibleonscreenenabler2d.rst,它包含一个矩形区域和一个目标节点:区域可见时,目标节点通过其Node.process_mode属性被自动启用;否则自动禁用。典型场景是「玩家靠近时才激活敌人」。
| 属性 | 默认值 | 说明 |
|---|---|---|
enable_mode(EnableMode枚举) | 0 | 决定目标节点如何被启用,对应Node.ProcessMode;禁用时统一使用PROCESS_MODE_DISABLED |
enable_node_path(NodePath) | NodePath("..") | 目标节点路径(相对于 Enabler 自身) |
EnableMode枚举的三个取值:
ENABLE_MODE_INHERIT = 0:对应Node.PROCESS_MODE_INHERIT;ENABLE_MODE_ALWAYS = 1:对应Node.PROCESS_MODE_ALWAYS;ENABLE_MODE_WHEN_PAUSED = 2:对应Node.PROCESS_MODE_WHEN_PAUSED。
enable_node_path的目标节点会被缓存:仅在设置该属性时(若 Enabler 已在场景树中)以及每次 Enabler 进入场景树时赋值。路径为空则不影响任何节点;路径无效会报错。从源码结构看,这种缓存设计可以避免每帧解析 NodePath 的开销,但也意味着运行时动态更换目标需要重新设置属性才能生效。
八、与 3D 版本及性能优化的关系
- 2D/3D 分工:2D 场景使用
VisibleOnScreenNotifier2D,3D 场景使用VisibleOnScreenNotifier3D(基于AABB(-1, -1, -1, 2, 2, 2)包围盒与is_on_screen()),两者 API 高度对称,方便跨维度迁移经验。 - 性能优化:官方性能指南 tutorials/performance/optimizing_3d_performance.rst 提到,动画/顶点动画(蒙皮、变形)成本高昂,可通过
VisibleOnScreenEnabler3D与VisibleOnScreenNotifier3D来「降低远处或被遮挡网格的动画频率,或直接暂停玩家不太可能注意到的动画」。这一思路同样适用于 2D:用VisibleOnScreenNotifier2D暂停屏幕外对象的处理逻辑,从而减少每帧的 CPU 开销。 - 大世界考量:同一性能文档指出,大型世界与小型游戏有不同的优化考量,屏幕可见性通知正是「只处理玩家能看到的内容」这类优化手段的基础设施。
九、Godot 4 迁移中的命名变化
如果你从 Godot 3 迁移而来,需要注意 tutorials/migrating/upgrading_to_godot_4.rst 中的重命名对照表:
| Godot 3 旧名 | Godot 4 新名 |
|---|---|
VisibilityNotifier2D | VisibleOnScreenNotifier2D |
VisibilityNotifier3D | VisibleOnScreenNotifier3D |
VisibilityEnabler | VisibleOnScreenEnabler3D |
迁移时若遇到VisibilityNotifier2D相关的旧脚本,直接替换为VisibleOnScreenNotifier2D即可;同样,3D 中旧的ProximityGroup也可用VisibleOnScreenNotifier3D作为替代方案。
小结:何时使用、何时慎用
推荐使用的场景:
- 离屏对象回收(连接
screen_exited→queue_free); - 按可见性切换逻辑(配合
is_on_screen()或screen_entered/screen_exited); - 需要「可见即启用」的自动化行为(直接选用
VisibleOnScreenEnabler2D)。
需要谨慎的地方:
- 判定完全依赖渲染剔除,
CanvasItem.visible为false时功能失效(见 classes/class_visibleonscreennotifier2d.rst 的 Note); - 节点加入场景树后首帧内
is_on_screen()恒为false,不能依赖即时结果; - 它判定的是「屏幕可见性」而非「物理碰撞」或「遮挡关系」,不要用它替代
Camera2D视口判断之外的自定义可见性逻辑。
- 文档
- 教程
- 游戏开发
【免费下载链接】godot-docs
Godot Engine official documentation
相关推荐
PixiJS Culler Plugin 视锥剔除实战:跳过屏幕外对象的渲染
PixiJS Culler Plugin 视锥剔除实战:跳过屏幕外对象的渲染 PixiJS 的 CullerPlugin 用于在渲染前自动跳过视口(viewpo
前端图形学Quasar v-intersection 指令实战指南:基于 Intersection Observer 的可见性检测与按需渲染
Quasar v intersection 指令实战指南:基于 Intersection Observer 的可见性检测与按需渲染 导读 : v interse
前端UI组件跨平台树链剖分:把树上的路径查询压到 O(log²n),顺手支持换根
树链剖分:把树上的路径查询压到 O log²n ,顺手支持换根 树上任意两点之间查路径权值和,暴力每次把路径走一遍是 O n ,10^5 个点 10^5 次询问
文档知识库教育教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考