很多刚开始学 Godot 3D 的开发者会陷入一个矛盾状态能搭出地形能写好角色控制器也能拖一版简单的敌人 AI但做一个完整可玩的游戏时却卡在了非常靠前的一步——游戏启动后玩家到底怎么开始有人直接让 3D 场景成为启动场景打开游戏就进入世界。这听起来很“极客”但对真实玩家来说非常不友好。没有标题、没有菜单、没有退出按钮、没有操作提示玩家的第一反应不是“这游戏好酷”而是“这是不是出 bug 了”。而“游戏引导”这个问题更隐蔽。很多人到项目后期才想起要告诉玩家怎么操作于是往 UI 上堆解释文字结果和场景切换、暂停状态、输入事件混在一起越改越乱。这篇教程就解决这两件事用 Godot 的思路做一个完整的游戏主页并实现一套可控制、可跳过的游戏引导流程。全程基于 Godot 的场景树和信号机制来组织不搞硬编码也不会让游戏逻辑散落在按键回调里。读完你可以直接套进自己的 3D 项目把“能跑的 Demo”变成“能交给玩家玩的游戏”。1. 这篇文章真正要解决的问题新手自己做 Godot 3D 项目通常最先做出的是角色移动、跳跃、碰撞和摄像机跟随。这些都是“游戏内体验”的核心部分做得越投入越容易忘记一个基本事实游戏不是从角色出生开始的而是从启动画面和主页开始的。缺少主页的游戏在实现上只缺几行代码但在体验上是缺失的。玩家打开游戏看到满屏地形和默认摄像机角度往往不知道该干什么。更现实的问题是已发布的独立游戏或课堂设计作业都需要一个能看懂的主页用于承担开始游戏、继续游戏、打开设置、退出程序这些基本入口。游戏引导的问题更加隐蔽。很多 3D 项目的操作并非直觉化的例如“WASD 移动”“鼠标拖拽旋转视角”“Shift 冲刺”。开发者自己熟练到不需要提示但新玩家打开第一分钟会明显不知所措。引导如果做得太晚场景切换和暂停逻辑都已经写完再往里面塞控制说明就容易产生冲突。这篇文章要解决的是这两个问题在 Godot 中的典型实现方式。全文围绕三个核心点展开第一如何用 Control 节点搭建游戏主页第二如何在 Godot 场景树中切换场景并传递“是否已完成引导”的标记第三如何让引导信息以非侵入的方式叠加到 3D 游戏场景上并且能随时跳过。适合阅读这篇教程的读者是已经掌握 Godot 基础操作、能创建节点并编写简单 GDScript 的开发者。你不需要会复杂的 UI 框架知识但最好能理解“场景”和“节点”的最基本概念。这里先给一个技术判断Godot 中的界面本质上是普通场景主页和引导页并不需要使用插件或额外的 UI 框架。只要理解 CanvasLayer 的层级作用以及 Control 节点的锚点布局就能在纯编辑器环境下完成比预期更漂亮的界面。2. 基础概念场景、CanvasLayer、Control 与信号开始写代码之前先把四个概念理清楚。它们单独看不难但组合起来经常让新手混乱。2.1 场景即一切UI 也是场景在 Godot 中任何东西都可以是场景。3D 世界是场景主角是场景主页是场景一个弹出式引导窗口也可以是场景。这意味着你不必等游戏场景全部搭好再画页面而是随时新建一个 .tscn 文件在这个文件里组合 Label、Button 等 UI 节点保存然后通过代码加载。这种做法最大的好处是复用和隔离。主页和引导页之间不会互相干扰每个 .tscn 文件只负责自己的职责。一个干净的项目结构下一个场景对应一个功能查找和修改都方便。2.2 CanvasLayerUI 与 3D 世界的分层规则3D 场景中的节点是空间中的物体受摄像机位置和远近影响。但按钮、标题、文字这些信息不能跟着摄像机跑否则玩家移动视角时菜单就会飘走。Godot 提供了 CanvasLayer 节点。这个节点像一个独立的画布层挂在其下的所有 Control 节点都会直接渲染到屏幕上不受 3D 摄像机影响也不参与 3D 空间坐标计算。刚才那句可以换个方式理解3D 世界里的一切是舞台上的演员CanvasLayer 是舞台上方悬挂的屏幕字幕无论舞台怎么演字幕都会固定在观众视野里。游戏主页、暂停菜单、血量条、操作引导都是用 CanvasLayer 挂 Control 节点来实现的。2.3 Control 节点体系与锚点布局Control 是所有 UI 节点的基类。Button、Label、Panel、ColorRect、VBoxContainer 都继承自 Control。Control 最核心的能力是锚点布局。每个 Control 节点都有四个锚点值对应它相对父节点的位置比例。把锚点设为“全屏铺满”子节点就会自动适应窗口大小变化。这也意味着你的游戏主页在 16:9 和 21:9 显示器上都能保持布局合理。有一种常见误解是 Control 节点也必须放在 3D 空间里。其实只要把 UI 挂到 CanvasLayer 下它就脱离 3D 空间了。另一种误解是 VBoxContainer 这类容器节点必须配合代码手动排列实际上拖拽节点就能自动完成自动布局。2.4 信号节点之间的“广播消息”按钮被点击后需要告诉游戏“启动开始”。在 Godot 中这种节点间的通信依赖信号。信号可以理解为节点发出的一条广播被按下时发一条“pressed”消息任何对该消息感兴趣的脚本都可以提前连接到这条消息上收到后执行对应函数。start_button.pressed.connect(_on_start_pressed)这一句就是把按钮的 pressed 信号连接到当前脚本的 _on_start_pressed 函数。点击按钮后pressed 被触发_on_start_pressed 自动执行。信号机制的好处是让节点之间解耦。按钮不需要知道自己要跳转到哪个场景按钮只负责发出“我被按了”这个消息。真正决定跳转到哪里的是连接方脚本。这样改游戏逻辑时不需要去翻按钮内部实现。2.5 场景切换与资源预加载Godot 4 中常用两种场景切换方式。第一种是直接用路径加载并切换get_tree().change_scene_to_file(res://scenes/game.tscn)第二种是先把场景缓存成 PackedScene再实例化切换export var game_scene: PackedScene var packed: Node game_scene.instantiate() get_tree().root.add_child(packed) get_tree().current_scene.queue_free()第二种方式适合需要控制切换时机、做淡入淡出动画的场景。本教程使用第一种方式做最简单的主流程因为代码可读性更高新手不迷茫。3. 环境准备与项目结构本教程基于 Godot 4.x 版本。Godot 3.x 的部分 API 名称有差异例如场景切换方法在 3.x 中名为 change_scene在 4.x 中则必须写成 change_scene_to_file。所以下载时优先选择 Godot 4.2 及以上稳定版文中代码全部采用 Godot 4 语法。创建一个新项目项目模板选择“3D 场景”或“空项目”都可以。建议创建完成后在项目目录里预先把场景分类存放后续找文件时不需要在一堆未命名场景里翻。推荐目录结构如下res:// ├── scenes/ │ ├── main_menu/ │ │ └── main_menu.tscn │ ├── game/ │ │ └── game.tscn │ └── ui/ │ └── tutorial_overlay.tscn ├── scripts/ │ ├── main_menu.gd │ ├── game.gd │ ├── tutorial_overlay.gd │ └── game_manager.gd └── assets/ ├── fonts/ └── textures/这个结构并不强制但对后续维护非常重要。一个典型的反面案例是所有脚本放在根目录所有场景散落在 res:// 下。初级项目还能应付一旦功能增加想找到一个按钮对应的脚本都变得困难。你在实际项目中完全可以按团队习惯调整目录名但“场景”、“脚本”、“资源”分离这条原则建议保留。准备工作就绪后进入核心实现。4. 游戏主页的实现游戏主页的目标非常明确启动时显示一个可识别的标题界面玩家通过点击按钮进入游戏或退出。4.1 场景树设计新建场景 main_menu.tscn根节点选择 Control。接着按下面创建层次MainMenu (Control - 根节点) ├── Background (ColorRect) ├── TitleContainer (VBoxContainer) │ ├── Title (Label) │ └── Subtitle (Label) ├── ButtonContainer (VBoxContainer) │ ├── StartButton (Button) │ ├── SettingsButton (Button) │ └── QuitButton (Button)根节点选择 Control 而不是 CanvasLayer 的原因很简单MainMenu 场景本身就会被设置为启动场景它是整个窗口的第一个界面。Control 作为根节点时默认铺满全屏。如果在根节点加 CanvasLayer 也没有问题但 CanvasLayer 本身不会自动处理锚点还是需要挂一个 Control 来控制布局。直接用 Control 更直观。Background 节点使用 ColorRect铺满全屏。如果游戏有美术资源可以把 ColorRect 换成 TextureRect 并拖入背景图。这里用一个深色背景作为示例设置 Color 为#1a1a2e。TitleContainer 使用 VBoxContainer作用是垂直排列标题和副标题。给它设置锚点为“顶部居中”并增加合适的偏移让标题距离上边界约 15% 高度。ButtonContainer 同样使用 VBoxContainer锚点设为“中心”用于排列三个按钮。这种纯节点搭建的好处是 UI 布局能通过编辑器实时预览不需要运行程序就能知道按钮在哪里。4.2 设置中文显示Godot 默认字体对中文的支持不完整经常出现方块或空白。项目中需要准备一个支持中文的字体文件例如开源免费的思源黑体或阿里巴巴普惠体。如果你的项目没有现成字体先用系统字体临时替代但发布时要记得打包自定义字体。把字体文件放入 assets/fonts 目录后选中根节点 MainMenu在检查器中建立一个 Theme 资源。在 Theme 中添加 DefaultFont 和 DefaultFontSize设置字号为 28。这样所有 Label 和 Button 默认都会使用这个字体不需要逐节点设置。如果不理解 Theme 的具体细节可以简单理解为“全局统一样式表”。尝试一次后你会发现比单独设置每个节点的样式高效得多。4.3 按钮文本与场景连接在各按钮的 Text 属性中填写“开始游戏”“设置”“退出游戏”。接下来为根节点附加脚本 main_menu.gd。这个脚本负责三类事情在 _ready 中连接按钮信号。定义处理函数。执行场景切换和退出。# 文件路径scripts/main_menu.gd extends Control export var game_scene: PackedScene func _ready() - void: var start_button: Button $ButtonContainer/StartButton var settings_button: Button $ButtonContainer/SettingsButton var quit_button: Button $ButtonContainer/QuitButton start_button.pressed.connect(_on_start_pressed) settings_button.pressed.connect(_on_settings_pressed) quit_button.pressed.connect(_on_quit_pressed) func _on_start_pressed() - void: if game_scene: get_tree().change_scene_to_packed(game_scene) else: get_tree().change_scene_to_file(res://scenes/game/game.tscn) func _on_settings_pressed() - void: # 设置功能可后续扩展当前作为占位 print(设置按钮被点击等待后续功能实现) func _on_quit_pressed() - void: get_tree().quit()这里把 game_scene 定义为 export它会在编辑器中以可拖拽的槽位出现。选中 MainMenu 根节点在检查器中把 game_scene 槽位拖入 game.tscn 场景资源。这样切换时直接使用已经打包好的场景资源比运行时反复加载文件路径更高效。这里的设置按钮没有实际功能只是预留入口。在教程性质的游戏主页中这是合理的。如果项目里有音量设置或画面设置再接相应逻辑即可。4.4 设置启动场景与运行验证点击菜单栏“项目 - 项目设置”在“运行”标签页中找到“主场景”将 main_menu.tscn 设置为主场景。按 F5 运行项目预期效果是一个深色背景窗口窗口中央有三个按钮。点击“开始游戏”后跳转到 game 场景点击“退出游戏”则关闭窗口。到这一步游戏主页已经完成。接下来处理引导流程。5. 游戏引导的两种实现方案引导功能的目标是告诉玩家“怎么操作这个游戏”。在 Godot 中实现方式有很多这里介绍两种最常见且适合 3D 项目的方案并说明选择依据。5.1 方案一独立引导场景将引导做成独立的场景 tutorial.tscn在主页点击“开始游戏”后先切换到引导场景引导场景显示操作说明和“取消”按钮。玩家看完后点击“开始”再进入真正的游戏场景。这种方案适合引导内容较多的情况比如包含背景介绍、故事说明、多页操作教学。它把引导和游戏空间完全隔离逻辑最简单适合新手理解。缺点是玩家进入游戏前被强制看一页或多页说明如果游戏需要频繁测试每次都要多点击几次才能进入。5.2 方案二叠层覆盖式引导游戏场景启动后在 3D 世界上方叠加一个 UI 层展示操作说明。玩家点击“开始游玩”后引导层消失游戏正式接受输入。这种方案更贴近商业游戏中的“简洁提示模式”不会打断游戏节奏。玩家可以边看提示边体会场景点击按钮后立即进入游戏。本教程选择第二种方案因为它的技术覆盖更全需要处理输入遮蔽、控制显示时机、叠加层脱离、与全局状态协同。学会这个独立引导场景的方案自然也能写。5.3 为什么不在 Game 场景里直接放置说明文字有人会问引导说明直接用游戏场景里的 3D 文本节点或者 Label 显示不就行了吗可以但维护起来不方便。直接放在 Game 场景里的 UI 会和游戏逻辑节点混在一起暂停、隐藏、反复显示都不好控制。更关键的是它无法复用——如果以后有多个关卡每个关卡都要复制一份同样的 UI。这正好印证了 2.2 节的分层观念引导是 UI 层的职责不应该混进 3D 场景节点树。6. 实现叠层式游戏引导整个引导流程由两个部分协作完成tutorial_overlay.tscn引导容器场景负责渲染提示内容和“开始游玩”按钮。game.gd游戏主场景脚本判断当前玩家是否已经看过引导决定是否显示 overlay。6.1 创建引导界面场景新建场景 tutorial_overlay.tscn根节点选择 CanvasLayer。节点层次如下TutorialOverlay (CanvasLayer) └── RootControl (Control - 全屏锚点) └── PanelContainer (居中) └── VBoxContainer ├── TitleLabel (Label) ├── TipLabel (Label) └── ConfirmButton (Button)RootControl 是 UI 容器设置为全屏锚点。PanelContainer 负责让面板居中显示。TitleLabel 显示“如何操作”TipLabel 显示具体的操作按键说明。ConfirmButton 让玩家确认已了解。为根节点 tutorial_overlay.tscn 编写脚本# 文件路径scripts/tutorial_overlay.gd extends CanvasLayer signal confirmed onready var confirm_button: Button $RootControl/PanelContainer/VBoxContainer/ConfirmButton func _ready() - void: confirm_button.pressed.connect(_on_confirm_pressed) func show_tutorial(tips: String) - void: $RootControl/PanelContainer/VBoxContainer/TipLabel.text tips visible true func hide_tutorial() - void: visible false func _on_confirm_pressed() - void: visible false confirmed.emit()visible 属性控制整个 CanvasLayer 的显示状态。隐藏时其下所有节点都不再渲染也不接收输入事件。这就是引导层可以整体消失的原因。这里设计了两个公开方法show_tutorial 接收一段新的提示文本并显示hide_tutorial 隐藏自身。外部场景只需要调用这两个方法就可以控制引导显示而无需关心内部节点如何排列。6.2 创建 3D 游戏主场景为了让引导不悬浮在空场景上我们准备一个最简 3D 游戏场景 game.tscn。根节点为 Node3D挂载以下内容Game (Node3D) ├── DirectionalLight3D (主光源) ├── WorldEnvironment (环境) ├── Ground (StaticBody3D) │ └── MeshInstance3D (BoxMesh用于地面) ├── Player (CharacterBody3D) │ ├── CollisionShape3D │ └── MeshInstance3D (CapsuleMesh表示玩家) └── TutorialOverlay (实例化自 tutorial_overlay.tscn)这里的 3D 内容只是一个占位演示。实际项目中你的战斗系统、场景资产、敌人逻辑都会挂在这个 Game 根节点下引导覆盖层的实现方式不变。为 Game 根节点编写脚本 game.gd# 文件路径scripts/game.gd extends Node3D onready var tutorial_overlay: CanvasLayer $TutorialOverlay const TUTORIAL_TEXT : WASD 移动\n鼠标拖拽视角\nShift 冲刺\nEsc 打开暂停菜单 func _ready() - void: # 清空 3D 中的默认摄像机输入等待引导层确认后才能操作 if not _has_seen_tutorial(): tutorial_overlay.show_tutorial(TUTORIAL_TEXT) get_tree().paused true else: tutorial_overlay.hide_tutorial() get_tree().paused false func _has_seen_tutorial() - bool: return GameManager.has_seen_tutorial通过 get_tree().paused 暂停游戏树是 Godot 中控制全局暂停的常见方式。当暂停值为 true 时所有节点的默认处理都会停止包括 3D 物理、输入处理。这正好符合“引导未确认前游戏世界不运行”的预期。不过需要特别注意的是CanvasLayer 有一个 Ignore Pause 属性。如果你的引导层需要在没有确认前仍然接收按钮点击必须给 TutorialOverlay 设置process_mode PROCESS_MODE_ALWAYS或者在检查器中将“Process Mode”改为 Always。Godot 4 中 CanvasLayer 默认不处理暂停的行为如果不改这个设置暂停后按钮也会失效引导层无法被点击。一个更稳妥的写法是直接在脚本里设置 process_mode# 在 tutorial_overlay.gd 的 _ready 中增加一行 process_mode Node.PROCESS_MODE_ALWAYS6.3 全局状态管理GameManager上面引用了 GameManager.has_seen_tutorial这个变量需要一个全局可见的单例来保存。Godot 提供了 Autoload 机制。创建一个脚本 game_manager.gd# 文件路径scripts/game_manager.gd extends Node var has_seen_tutorial: bool false var player_score: int 0 var current_level: String func mark_tutorial_seen() - void: has_seen_tutorial true在项目设置 - 全局类Autoload中添加 game_manager.gd 为单例命名为 GameManager。这样项目中任何脚本都能直接访问 GameManager.has_seen_tutorial而不需要通过场景路径查找节点。这个单例非常适合保存跨场景的数据是否看过引导、当前关卡、玩家分数、音量设置等。6.4 引导确认后恢复游戏玩家点击“开始游玩”后引导隐藏同时需要把暂停状态改回 false。任务落在 tutorial_overlay.gd 上报的 confirmed 信号上。在 game.gd 中连接该信号func _ready() - void: tutorial_overlay.confirmed.connect(_on_tutorial_confirmed) if not GameManager.has_seen_tutorial: tutorial_overlay.show_tutorial(TUTORIAL_TEXT) get_tree().paused true else: tutorial_overlay.hide_tutorial() get_tree().paused false func _on_tutorial_confirmed() - void: GameManager.mark_tutorial_seen() get_tree().paused false确认引导后标记已看过引导。第二次进入这个关卡时_has_seen_tutorial 返回 true引导层不会显示游戏直接运行。这套流程的价值在于状态在全局共享。以后无论是重新开始本关卡、切换其他关卡还是从主菜单再次进入都不必重复看引导。6.5 从游戏返回主页游戏总会结束玩家需要一条回到主页的路径。在 game.gd 中加一个简单的暂停菜单入口这里用 Esc 键触发# 文件路径scripts/game.gd 中新增部分 func _unhandled_input(event: InputEvent) - void: if event.is_action_pressed(ui_cancel): get_tree().change_scene_to_file(res://scenes/main_menu/main_menu.tscn)在实际项目中这样直接退出会丢失进度。更常见的做法是弹出“暂停面板”提供“继续游戏”“保存”“回到主页”三个选项。但这里保留最简链路让流程闭环即可。这里真正值得注意的细节是使用_unhandled_input而不是_input。因为 UI 按钮已经处理过点击事件不应该再让底部 3D 游戏逻辑收到被 UI 消费的操作。如果你的游戏里同时存在鼠标点击射击和按钮点击这个区分就极其重要。7. 运行结果与效果验证下面把整个流程串起来验证一遍。按 F5 启动项目依次检查场景一游戏主页。窗口应显示深色背景和三个按钮。点击“开始游戏”后切换到 3D 场景。场景二首次进入 3D 场景。画面中央出现引导面板显示操作说明文字。3D 世界被暂停地面、光源、角色都不动。此时如果玩家尝试移动角色不会有任何反应。这是正确状态因为 get_tree().paused true。点击“开始游玩”按钮引导面板消失角色可以移动游戏恢复运行。场景三从主页再次进入游戏。返回主页后再次点击“开始游戏”这次引导面板不再出现游戏直接运行。场景四退出。主页点击“退出游戏”按钮窗口关闭。验证代码的关键观察点有三个引导层是否在确认前关闭了 3D 输入。按钮点击后引导层是否用 confirmed 信号通知游戏恢复。再次进入游戏时GameManager.has_seen_tutorial 是否为 true。如果其中有任何一步不符合预期优先按下面的排查表检查。8. 常见问题与排查思路问题现象可能原因排查方式解决方案按钮点击没有反应信号未连接或路径错误打印按钮路径是否有节点检查按钮 pressed 信号是否 connect 到函数使用 $ 节点路径逐级确认或把按钮声明为 onready 变量统一连接引导确认后游戏仍在暂停未在 confirmed 回调中设置 paused false在 _on_tutorial_confirmed 中打印当前 pause 状态在确认函数里调用 get_tree().paused false引导层按钮无法点击CanvasLayer 在暂停模式下不处理输入检查 process_mode 是否为 ALWAYS在 tutorial_overlay.gd 的 _ready 中设置 process_mode Node.PROCESS_MODE_ALWAYS中文文字显示为方块未配置中文字体检查 Label 和 Button 使用的字体为根节点创建 Theme设置 DefaultFont 为支持中文的字体场景切换后灯光或环境异常场景资源未正确配置检查 Game 场景中的 DirectionalLight3D 和 WorldEnvironment 是否存在确保 Game 场景根节点包含必要的 3D 环境节点再次进入游戏仍然显示引导GameManager 未设置 has_seen_tutorial true查看 GameManager 是否成功添加到 Autoload在项目设置 - Autoload 中添加 game_manager.gd并在 confirmed 中调用 mark_tutorial_seen引导面板位置错乱锚点设置不正确检查 RootControl 锚点是否为全屏将 RootControl 的四向锚点拉至全屏并使用 Container 来管理居中按钮样式在不同分辨率下拉伸变形按钮和容器未配置最小尺寸查看按钮的 Custom Minimum Size 是否设置为按钮设置合理的 Custom Minimum Size例如 220 x 48表格中有几个问题对应日常开发出现频率最高的三个误区。第一个误区是信号连接失效。常见原因是把func _on_start_pressed()写成了func on_start_pressed()漏掉下划线。Godot 4 使用 connect 连接时不会报编译错误而是运行时报错或静默失败新手排查时不易发现。第二个误区是忽略 process_mode。Godot 4 中节点是否受暂停影响由 process_mode 属性控制。默认情况下暂停会停止所有节点但 UI 层的按钮如果也停止处理玩家就无法确认引导游戏卡死。第三个误区是中文显示问题。中国开发者做示例项目时很多人略过字体设置结果所有界面文字都是方块。解决方式并不复杂准备一个开源中文字体在 Godot 的 Theme 中设置即可。这个习惯建议从第一个项目就养成。9. 最佳实践与工程建议把主页和引导跑通只是第一步如果要长期维护这个 Godot 项目以下建议值得纳入日常开发习惯。9.1 用 Autoload 管理跨场景状态不要用全局文件里的静态变量来传递“是否已看过引导”这种信息。Godot 中最自然的做法是创建一个 Autoload 单例例如 GameManager。它适合存放全局状态当前关卡、音量、帧率设置、玩家分数。这个单例的生命周期贯穿整个游戏过程不随场景切换而销毁。代码访问方式简单直接不需要像查找节点那样维护长路径。9.2 场景遵循“一场景一职责”把主页、引导、游戏逻辑分别拆成独立场景。新手容易犯的错是把主页的所有按钮都放在游戏场景里想着“反正开始也是在场景里”。这在一两个按钮时确实能跑但功能一旦增多游戏场景会成为一锅粥。正确的拆分是一个场景只负责一个核心功能。MainMenu 负责入口Game 负责游戏世界TutorialOverlay 负责提示展示。节点之间的通信通过信号完成。这样修改引导界面时不需要担心改坏主页。9.3 暂停与 UI 结合时小心 process_mode你已经在前面的实现中看到了这个坑游戏暂停后UI 默认也停止处理。在需要“暂停但允许 UI 操作”的场景里务必给对应 UI 节点设置 PROCESS_MODE_ALWAYS。同样的思路可以扩展到暂停菜单、背包界面、设置面板。只要这些界面需要在暂停状态出现并接收点击都适用Node.PROCESS_MODE_ALWAYS。9.4 引导内容要短、要能跳过实际操作说明不要写成长篇大论。每一条动作描述独立成行例如WASD 移动 鼠标拖拽视角 Shift 冲刺同时引导必须提供“跳过”或“以后不再提示”的入口。许多商业游戏首次启动时也会让玩家选择“新游戏”或“继续游戏”原因之一就是要让老玩家跳过新手引导。你可以在引导面板中增加一个“不再显示”复选框确认后直接在 GameManager 中标记 seen。9.5 输入事件使用动作映射本教程的返回主页写的是ui_cancel。在实际项目中建议在项目设置中为自定义操作统一命名例如move_forward、sprint然后绑定具体按键。这样做的好处是以后如果要支持手柄或自定义键位不需要修改脚本逻辑。9.6 引导状态考虑存档如果你的游戏有存档系统has_seen_tutorial不应该只在运行期内有效。更合理的做法是把它写入用户存档文件。这样玩家重新打开游戏后已完成的引导不会再次出现。平时开发时先用内存变量做存档系统后再迁移过去即可。10. 跳转到游戏场景后还可以继续做什么到这里Godot 3D 项目中“从启动到游戏”的基础链路已经打通主页 → 引导 → 3D 游戏场景 → 返回主页。整个过程没有使用第三方插件也没有引入复杂架构完全基于 Godot 4 的节点、信号、CanvasLayer 和 Autoload 机制。下一步实践方向也很明确。你可以先在当前项目里把主页按钮扩充到四个“开始游戏”“继续游戏”“设置”“退出”。其中“继续游戏”需要记住玩家上次关卡这是对 GameManager 数据管理的又一次练习。然后尝试做暂停菜单把返回主页的硬切换改为软菜单。暂停菜单里同时涉及 process_mode、输入事件、按钮信号和本教程的引导流程高度相似。真正需要记住的一句话是在 Godot 中界面就是场景流程靠信号跨场景状态交给 Autoload。按这个思路写出来的项目结构是清晰的功能是解耦的后面加新内容不会牵一发动全身。把代码敲完跑通再加上你自己的游戏素材一个体面的游戏启动流程就完成了。