☰
Godot 4游戏入口流程实战:主菜单、新手引导与暂停系统
2026/10/3 15:42:12 网站建设 项目流程

Godot 3D 的完整流程走到这里,基本就差两块拼图:玩家进入游戏看到的第一个画面(主页),以及教会玩家基础操作的引导流程。这篇是系列第 30 节,专门把“游戏引导”和“游戏主页”放在同一套工程里解决。

本文基于 Godot 4.x 和 GDScript 2.0 编写,不引入任何第三方插件。你可以从这套例子里看到“主菜单 → 教学关卡 → 正式关卡 → 暂停返回主页”的最小闭环是怎么组织的。文中的所有脚本都可以直接复制到你的项目里改着用,不需要额外安装资源包。

核心特点先摆出来:

  • 主页使用 Control 节点 + 信号(Signal)组织,按钮点击、场景切换都在脚本里显式连接;
  • 新手引导用 3D 场景中的 Area3D 区域触发,配合 CanvasLayer 上的提示面板展示引导文案;
  • 引导完成状态写入本地存档(ConfigFile),二次进游戏直接跳过引导;
  • 暂停菜单监听ui_cancel输入,支持恢复游戏和返回主页。

这篇文章会带你把项目初始化、输入映射、主菜单、全局状态、教学关卡、暂停菜单全部跑通。读完你就能在自己的 Godot 3D 工程里接入一套可扩展的入口与引导流程。

1. 核心能力速览

能力项说明
适用引擎Godot 4.x,GDScript 2.0
项目类型3D 游戏入口流程
核心功能主菜单、新手引导、本地存档、暂停菜单
运行方式Godot 编辑器 F5 直接运行,或导出桌面 / 移动端
外部依赖无第三方插件,全部使用引擎内置节点
预计代码量5 个 GDScript 脚本,核心逻辑约 250 行
技术难度入门到初级,适合学习场景切换与 UI 交互
扩展点替换正式美术资源、增加多语言、接入手柄输入、扩展引导分支

这套方案的优势是结构简单:主页场景只管入口,全局单例只管存档,教学关卡只管引导,互相之间通过场景切换和信号协作。后面要加设置菜单、成就系统、剧情演出,都能顺着这套结构继续扩展。

2. 适用场景与使用边界

这套“引导 + 主页”方案最适合以下场景:

  • 独立游戏原型,需要快速搭出“点开始 → 学操作 → 进关卡”的流程;
  • 3D 教学项目,用来演示 Godot 的场景切换、Area3D 区域检测、CanvasLayer UI 分层;
  • 中小型游戏的主菜单,需要一个干净、稳定、随时能换皮肤的入口界面。

它能解决的问题也很明确:玩家进入游戏的第一个画面;玩家对移动、跳跃等操作一无所知时的引导提示;引导完成后的状态保存;游戏进行中按 Esc 暂停并返回主页。

不过要注意边界:

  • 这套引导是线性流程,一次只推进一个步骤。如果要做多分支、多条件并行的复杂引导,需要改成“任务系统”,本文的 TutorialDirector 只是最小实现。
  • 本地存档只适合保存本机进度。如果游戏有排行榜、云存档需求,要走服务端方案。
  • 引导文案、字体、模型、图标素材都要确认授权。Godot 4 默认字体对中文支持有限,需要在项目里导入开源中文字体(例如思源黑体、文泉驿)并设置给 UI 控件。

3. 环境准备与项目初始化

开始写脚本之前,先准备环境。

3.1 安装 Godot 4.x

从 Godot 官网下载 Godot 4.x 稳定版,标准版即可,不需要 .NET 版(本文不用 C#)。下载后直接解压运行,项目管理器会弹出。

3.2 创建 3D 项目

打开项目管理器,点击“新建”,项目名称填GameDemo,渲染器选择Forward+(桌面平台默认),路径任选一个本地目录。创建完成后,Godot 会自动生成一个包含Node3D根节点的场景。

我在工程里习惯按这个目录结构组织资源:

res:// ├── scenes/ │ ├── main_menu.tscn │ ├── tutorial_level.tscn │ └── main_level.tscn ├── scripts/ │ ├── game_state.gd │ ├── main_menu.gd │ ├── player_controller.gd │ ├── tutorial_overlay.gd │ ├── tutorial_director.gd │ └── pause_menu.gd └── assets/ ├── fonts/ └── models/

在 Godot 编辑器的文件系统面板里建好这些目录,后续场景文件一律放scenes/,脚本放scripts/。

3.3 配置输入映射

Godot 4 的输入映射在“项目设置 → 输入映射”中配置。我们需要给玩家控制器添加五个动作:WASD 移动和空格跳跃。ui_cancel(Esc)是引擎内置动作,不需要自己添加。

动作名称绑定按键作用
move_leftA向左移动
move_rightD向右移动
move_forwardW向前移动
move_backS向后移动
jump空格跳跃

配置方法:

  1. 打开“项目 → 项目设置”;
  2. 切到“输入映射”标签;
  3. 在左侧输入框输入move_left,点“添加”;
  4. 展开刚添加的动作,点“+”添加按键,弹出窗口中按下 A 键;
  5. 按同样方法添加move_right、move_forward、move_back、jump。

3.4 设置 Autoload 单例

全局状态GameState需要用 Autoload 注册,这样任何场景都能访问它,而不用手动传递引用。

操作路径:“项目 → 项目设置 → 全局 → AutoLoad”。

点击路径输入框,填入:

res://scripts/game_state.gd

点击“添加”,节点名称保持默认的GameState即可。这个脚本接下来会在第 5 节创建。

4. 游戏主页场景制作

主页(主菜单)是一个 Control 场景,包含标题、开始按钮、设置按钮、退出按钮。这个场景只负责一个事情:告诉玩家“游戏入口在这,点这个继续”。

4.1 创建 MainMenu 场景

在scenes/目录下新建场景,根节点类型选Control,命名为MainMenu,保存为main_menu.tscn。

场景树结构如下:

MainMenu (Control) ├── CanvasLayer │ └── Control (全屏) │ ├── ColorRect (背景) │ └── CenterContainer │ └── VBoxContainer │ ├── Label (游戏标题) │ ├── StartButton │ ├── SettingsButton │ └── QuitButton

按下面的步骤在编辑器里搭:

  1. 在根节点MainMenu下添加CanvasLayer;
  2. 在CanvasLayer下添加Control,把它的锚点铺满全屏(Anchors Preset 选 Full Rect);
  3. 在Control下添加ColorRect做背景,同样铺满全屏,颜色选深色;
  4. 添加CenterContainer,铺满全屏;
  5. 在CenterContainer下添加VBoxContainer,设置 Separation 为 20;
  6. 在VBoxContainer下依次添加Label和三个Button。

标题 Label 的文本填你的游戏名,字号在检查器里调到 48 左右。按钮文本分别设为“开始游戏”“设置”“退出”。

4.2 主菜单脚本

给根节点MainMenu挂脚本scripts/main_menu.gd。这个脚本的作用是连接三个按钮的信号,并决定“开始游戏”是进入教学关卡还是直接进入正式关卡。

extends Control @onready var start_button: Button = %StartButton @onready var settings_button: Button = %SettingsButton @onready var quit_button: Button = %QuitButton func _ready() -> void: start_button.pressed.connect(_on_start_pressed) settings_button.pressed.connect(_on_settings_pressed) quit_button.pressed.connect(_on_quit_pressed) if GameState.has_seen_tutorial(): start_button.text = "继续游戏" func _on_start_pressed() -> void: if GameState.has_seen_tutorial(): get_tree().change_scene_to_file("res://scenes/main_level.tscn") else: get_tree().change_scene_to_file("res://scenes/tutorial_level.tscn") func _on_settings_pressed() -> void: # 设置面板可以在这里打开一个自定义弹窗,本文先留占位 print("打开设置面板") func _on_quit_pressed() -> void: get_tree().quit()

注意几个细节:

  • 按钮节点用了%StartButton这种唯一名称语法,需要在检查器里给每个按钮开启“Editable Children”并确认节点名和代码一致;
  • 如果不想用%唯一名称,可以改成$CanvasLayer/Control/CenterContainer/VBoxContainer/StartButton这种完整路径;
  • 如果GameState没有注册为全局单例,脚本第一行GameState.has_seen_tutorial()就会直接报错,所以第 3 节的 Autoload 配置必须提前完成。

到这里,按 F5 运行项目,你应该能看到一个简单的主菜单,点击“退出”会退出游戏;“开始游戏”暂时因为tutorial_level.tscn和main_level.tscn还不存在而报错,这是正常的,我们在第 6 节把教学场景建出来。

5. 全局状态与本地存档

主菜单里的“是否看过引导”这个判断,来自全局单例GameState。它负责两件事:在内存中保存引导完成标记;把标记写入本地配置文件,让游戏重启后依然有效。

Godot 的user://路径对应系统为当前应用分配的存档目录。桌面平台上,Windows 通常在AppData/Roaming/Godot/app_userdata/项目名/下,Linux 和 macOS 也有对应的用户数据目录。在编辑器里按 F5 运行时会直接映射到本地,导出后也一样,不需要处理绝对路径。

创建scripts/game_state.gd:

extends Node const SAVE_PATH := "user://game_state.cfg" var tutorial_completed := false func _ready() -> void: _load_state() func has_seen_tutorial() -> bool: return tutorial_completed func complete_tutorial() -> void: tutorial_completed = true _save_state() func _load_state() -> void: var config := ConfigFile.new() var err := config.load(SAVE_PATH) if err == OK: tutorial_completed = config.get_value("game", "tutorial_completed", false) func _save_state() -> void: var config := ConfigFile.new() config.set_value("game", "tutorial_completed", tutorial_completed) var err := config.save(SAVE_PATH) if err != OK: push_warning("无法保存游戏状态,错误码:%s" % err)

这段逻辑的核心是:

  • _ready()在游戏启动时读取存档;
  • has_seen_tutorial()供主菜单判断走哪条路;
  • complete_tutorial()在引导完成时调用;
  • ConfigFile是 Godot 内置的键值对配置文件,读写格式类似 INI。

以后要增加音量、画质、语言等设置项,都在这个单例里加,保存结构统一用config.set_value(section, key, value)。

6. 新手引导场景实现

新手引导我做成一个简短的教学关卡:玩家出生在一片地面上,先移动到蓝色光圈,再跳上高台,到达第二个光圈后引导完成,自动进入正式关卡。

6.1 创建 TutorialLevel 场景

新建场景,根节点类型选Node3D,命名为TutorialLevel,保存为tutorial_level.tscn。

场景树如下:

TutorialLevel (Node3D) ├── WorldEnvironment ├── DirectionalLight3D ├── Player (CharacterBody3D) │ ├── CollisionShape3D │ └── MeshInstance3D ├── Ground (CSGBox3D) ├── MoveTargetArea (Area3D) │ ├── CollisionShape3D │ └── MeshInstance3D ├── JumpPlatform (CSGBox3D) ├── JumpTargetArea (Area3D) │ ├── CollisionShape3D │ └── MeshInstance3D ├── TutorialOverlay (CanvasLayer) │ └── Control │ └── PanelContainer │ └── VBoxContainer │ ├── TitleLabel │ ├── ContentLabel │ └── SkipButton └── TutorialDirector (Node)

在编辑器里按顺序操作:

  1. 添加WorldEnvironment,新建一个Environment资源,背景颜色可以设成浅蓝或灰色;
  2. 添加DirectionalLight3D,调整方向,保证场景有光照;
  3. 添加Ground,在检查器里调整Size为(20, 1, 20),勾选Use Collision;
  4. 添加Player,类型选CharacterBody3D,给它挂第 6.2 节的玩家脚本;
  5. 在Player下添加CollisionShape3D,形状选CapsuleShape3D,调整半径和高度;
  6. 在Player下添加MeshInstance3D,网格选CapsuleMesh或BoxMesh,给一个材质方便区分方向;
  7. 添加JumpPlatform,CSGBox3D,Size 设为(4, 2, 4),位置Y设为 1,勾选Use Collision,让玩家能跳上去;
  8. 移动目标和跳跃目标各加一个Area3D,下面挂CollisionShape3D(形状选BoxShape3D),大小约为(2, 2, 2);
  9. 两个 Area3D 下的MeshInstance3D可以用BoxMesh,材质设置为半透明蓝色,方便玩家看到目标点。

半透明材质的设置方式是在检查器中新建StandardMaterial3D,把Transparency设为Alpha,Albedo Color的 Alpha 调低到 0.4 左右。

6.2 玩家控制器脚本

给Player挂scripts/player_controller.gd:

extends CharacterBody3D @export var speed := 5.0 @export var jump_velocity := 4.5 var gravity := ProjectSettings.get_setting("physics/3d/default_gravity") func _physics_process(delta: float) -> void: if not is_on_floor(): velocity.y -= gravity * delta var input := Input.get_vector("move_left", "move_right", "move_forward", "move_back") var direction := (transform.basis * Vector3(input.x, 0, input.y)).normalized() if direction: velocity.x = direction.x * speed velocity.z = direction.z * speed else: velocity.x = move_toward(velocity.x, 0.0, speed) velocity.z = move_toward(velocity.z, 0.0, speed) if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = jump_velocity move_and_slide()

说明:

  • Input.get_vector接收四个动作名,分别对应负 X、正 X、负 Y、正 Y;
  • 这里把 2D 输入向量映射到 3D 的 XZ 平面,move_forward对应 Z 轴负方向;
  • transform.basis * Vector3(...)保证方向跟随角色朝向,如果以后做摄像机跟随,这个设计会更有用。

6.3 引导提示面板

TutorialOverlay是 CanvasLayer,负责显示引导标题、正文和跳过按钮。挂载scripts/tutorial_overlay.gd:

extends CanvasLayer @onready var panel := $Control/PanelContainer @onready var title_label := $Control/PanelContainer/VBoxContainer/TitleLabel @onready var content_label := $Control/PanelContainer/VBoxContainer/ContentLabel @onready var skip_button := $Control/PanelContainer/VBoxContainer/SkipButton func _ready() -> void: panel.hide() skip_button.pressed.connect(_on_skip_pressed) func show_hint(title: String, content: String) -> void: title_label.text = title content_label.text = content panel.show() func hide_hint() -> void: panel.hide() func _on_skip_pressed() -> void: GameState.complete_tutorial() get_tree().change_scene_to_file("res://scenes/main_level.tscn")

注意,这里的跳过按钮直接调用全局GameState.complete_tutorial(),所以玩家无论在哪个引导步骤选择“跳过”,下次进入主菜单都会显示“继续游戏”,并跳过教学关卡。

6.4 引导导演脚本

TutorialDirector是引导的大脑,挂在场景末端的Node上。它配置了两个区域触发步骤:走到 MoveTargetArea → 跳上 JumpTargetArea → 完成引导。

extends Node signal tutorial_finished @export var overlay: CanvasLayer @export var player: CharacterBody3D @export var move_target_area: Area3D @export var jump_target_area: Area3D enum Step { MOVE, JUMP, DONE } var current_step := Step.MOVE func _ready() -> void: assert(overlay != null, "overlay 未设置") assert(player != null, "player 未设置") assert(move_target_area != null, "move_target_area 未设置") assert(jump_target_area != null, "jump_target_area 未设置") overlay.show_hint("移动引导", "使用 WASD 控制角色,走到蓝色光圈中。") move_target_area.body_entered.connect(_on_move_area_body_entered) jump_target_area.body_entered.connect(_on_jump_area_body_entered) func _on_move_area_body_entered(body: Node3D) -> void: if body == player and current_step == Step.MOVE: current_step = Step.JUMP overlay.show_hint("跳跃引导", "按空格键跳跃,跳到高台上。") move_target_area.queue_free() func _on_jump_area_body_entered(body: Node3D) -> void: if body == player and current_step == Step.JUMP: current_step = Step.DONE overlay.show_hint("引导完成", "你已经掌握基础操作,即将进入正式关卡。") await get_tree().create_timer(1.5).timeout _finish() func _finish() -> void: tutorial_finished.emit() GameState.complete_tutorial() get_tree().change_scene_to_file("res://scenes/main_level.tscn")

脚本里几个关键点:

  • signal tutorial_finished可以留给外部监听;
  • body_entered信号只在Area3D检测到物理刚体或CharacterBody3D进入时触发;
  • move_target_area.queue_free()在第一步完成后销毁光圈,防止重复触发;
  • await get_tree().create_timer(1.5).timeout让完成提示显示 1.5 秒再切场景;
  • 场景切换前先调用GameState.complete_tutorial()保存状态。

把TutorialDirector节点的overlay、player、move_target_area、jump_target_area四个导出变量在检查器中拖拽赋值。这一步不赋值,运行时会直接断言报错。

6.5 正式关卡占位

第 4 节和第 6 节的change_scene_to_file都指向res://scenes/main_level.tscn。这个场景不需要复杂内容,可以在scenes/下新建一个简单 3D 场景,放一块地面、一个灯光和一个Label3D,显示“正式关卡”。

如果没有这个场景,点击“开始游戏”或完成引导时会报错:

Failed loading resource res://scenes/main_level.tscn

所以一定要先建一个占位场景。

7. 暂停菜单与返回主页

游戏进行中需要支持暂停和返回主页。暂停菜单我单独做成一个CanvasLayer场景,放在scenes/pause_menu.tscn。

7.1 场景结构

PauseMenu (CanvasLayer) └── Control └── PanelContainer └── VBoxContainer ├── Label (暂停) ├── ResumeButton ├── MenuButton └── QuitButton

将PauseMenu作为子节点加到main_level.tscn和tutorial_level.tscn中。

7.2 暂停脚本

给根节点挂scripts/pause_menu.gd:

extends CanvasLayer var is_paused := false @onready var pause_panel := $Control/PanelContainer @onready var resume_button := $Control/PanelContainer/VBoxContainer/ResumeButton @onready var menu_button := $Control/PanelContainer/VBoxContainer/MenuButton @onready var quit_button := $Control/PanelContainer/VBoxContainer/QuitButton func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS pause_panel.hide() resume_button.pressed.connect(_on_resume_pressed) menu_button.pressed.connect(_on_menu_pressed) quit_button.pressed.connect(_on_quit_pressed) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("ui_cancel"): _toggle_pause() func _toggle_pause() -> void: is_paused = not is_paused get_tree().paused = is_paused pause_panel.visible = is_paused func _on_resume_pressed() -> void: _toggle_pause() func _on_menu_pressed() -> void: get_tree().paused = false get_tree().change_scene_to_file("res://scenes/main_menu.tscn") func _on_quit_pressed() -> void: get_tree().paused = false get_tree().quit()

这段脚本的关键是:

  • process_mode = Node.PROCESS_MODE_ALWAYS,保证场景树暂停时,暂停菜单依然能接收输入和按钮点击;
  • ui_cancel默认绑定 Esc 键,不需要额外配置输入映射;
  • 返回主页前必须把get_tree().paused重置为false,否则新场景一进来就是暂停状态。

8. 功能测试与效果验证

场景搭完后,按 F5 从main_menu.tscn运行,按下面的清单验证流程。

8.1 主菜单验证

  • 预期:看到深色背景、游戏标题、“开始游戏”“设置”“退出”三个按钮。
  • 点击“退出”,游戏进程结束。
  • 如果按钮没有反应,检查脚本中按钮节点的路径和名称是否一致。

8.2 教学引导验证

  • 首次运行主菜单,点击“开始游戏”,进入tutorial_level.tscn。
  • 界面右上角或底部出现引导面板,文案是“移动引导”。
  • 用 WASD 控制角色走到蓝色光圈,光圈消失,引导面板切换为“跳跃引导”。
  • 按空格跳上高台,进入第二个光圈,引导面板显示“引导完成”。
  • 1.5 秒后自动切到main_level.tscn。

8.3 存档验证

  • 完成引导后回到主菜单,点击“开始游戏”,按钮文本应显示“继续游戏”,直接进入main_level.tscn。
  • 重启游戏,主菜单“开始游戏”依然显示“继续游戏”。
  • 如果想重新测试引导流程,删除存档文件user://game_state.cfg,或者临时改GameState里的tutorial_completed := false。

8.4 暂停菜单验证

  • 进入正式关卡后按 Esc,游戏暂停,暂停面板显示。
  • 点“恢复游戏”或者再次按 Esc,游戏继续。
  • 点“返回主页”,回到主菜单。
  • 如果按 Esc 没反应,检查PauseMenu是否被添加到当前场景,以及脚本process_mode是否设置。

8.5 预期结果汇总

测试项操作预期结果
主菜单显示F5 运行标题和按钮正常渲染
首次开始游戏点击开始进入教学场景
移动引导WASD 走到光圈提示切换为跳跃引导
跳跃引导空格跳上高台显示引导完成
存档生效重新进入主菜单开始按钮变为继续游戏
暂停按 Esc游戏暂停,面板显示
返回主页点击返回主页回到主菜单且暂停重置

9. 常见问题与排查方法

问题现象可能原因排查方式解决方案
运行后报错Failed loading resource场景路径写错检查change_scene_to_file里的路径确认main_level.tscn或tutorial_level.tscn存在
按钮点击无反应信号未连接,或节点路径错误在脚本加断点或print输出用%按钮名唯一名称,或使用完整节点路径
按 WASD 角色不动输入映射没有配置打开项目设置查看动作列表按第 3.3 节添加动作
按空格不能跳jump动作缺失检查输入映射添加空格键到jump动作
引导面板一直显示TutorialDirector 的导出变量未赋值看运行日志是否触发assert拖拽赋值overlay、player、target_area
角色直接穿过平台CSGBox 未启用碰撞检查Use Collision属性勾选Use Collision,改用 StaticBody3D 更稳
引导完成后场景不切换tutorial_finished信号未触发或路径错误查看_finish是否执行确认main_level.tscn路径,确认complete_tutorial()在切换前调用
中文文本显示为方块Godot 默认字体不支持中文检查 UI 控件字体设置导入开源中文字体,在主题中配置
暂停后无法恢复暂停菜单节点被暂停检查process_mode设置Node.PROCESS_MODE_ALWAYS
返回主页后游戏仍暂停切换场景前未重置暂停检查get_tree().paused切换前设为false
存档无法保存user://路径不可写查看输出警告检查系统用户目录权限,或补充错误处理

10. 最佳实践与使用建议

把这套流程放进真实项目之前,有几个工程化建议值得先做。

10.1 节点命名与引用

Godot 4 推荐用%唯一名称引用 UI 节点,而不是长长的路径。打开节点检查器,在目标节点的右键菜单里选择“设置为唯一名称”,脚本里用%StartButton引用。这样即使调整 UI 层级,脚本不用改。

10.2 引导文案集中管理

把引导文案拆成一个Array[Dictionary],集中放在GameState或单独的资源文件里。修改文案时不用打开场景,风险更小。

const TUTORIAL_STEPS := [ { "title": "移动引导", "content": "使用 WASD 控制角色,走到蓝色光圈中。" }, { "title": "跳跃引导", "content": "按空格键跳跃,跳到高台上。" } ]

这个数组可以直接替换TutorialDirector中的硬编码文案,后续要加新步骤,只要增加一个字典。

10.3 UI 布局注意安全区域

引导面板不一定非要在屏幕中间。如果想在正式项目中长期使用,建议把面板放在底部或右上角,避免遮挡玩家视野。带刘海屏的手机导出时,还要为 Safe Area 留出边距。

10.4 存档版本化

game_state.cfg目前只有一个字段。以后添加存档内容时要加一个version字段,读取时做版本判断,避免老存档升级后字段缺失导致崩溃。

config.set_value("game", "version", 1)

10.5 测试时手动重置状态

开发过程中会反复测试引导,每次都要删存档很麻烦。可以在GameState里增加一个调试快捷键,按住 Ctrl 加 Q 删除存档并重启:

func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("delete_save_debug"): DirAccess.remove_absolute(SAVE_PATH) get_tree().reload_current_scene()

调试功能记得在导出前移除。

11. 总结与下一步

到这里,你的 Godot 3D 工程已经具备一套完整的入口流程:主菜单、全局存档、教学引导、暂停菜单。从代码量来看,这只用了不到十个脚本文件,却覆盖了绝大多数独立游戏起步阶段需要的基础结构。

这套流程最值得验证的是“引导完成状态”和“场景切换”的配合:主菜单根据存档状态决定进入教学还是正式关卡,引导结束又把状态写回存档,下一次进入主菜单自动改变按钮文本。理解了这个循环,你就能把它扩展到更多入口场景,比如章节选择、难度选择、成就奖励。

如果你正在做一个 3D 项目,下一步可以做的扩展包括:

  • 把TutorialDirector的线性步骤改成通用任务系统,支持并发目标和条件分支;
  • 用AnimationPlayer给引导提示加入场、退场动画,让引导更有节奏;
  • 引导面板接入 Gamepad 输入,兼容手柄玩家;
  • 替换正式美术资源,把占位用的 CSG 几何体换成导入模型。

建议收藏备用,做独立游戏原型的时候直接把这套主菜单和引导体系拿过去用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询