Godot 4.2 C#:加载Packed Scene后Ready信号未触发问题咨询
Godot 4.2 C# 场景切换后Setup方法未触发的原因与解决办法
问题原因
Root节点的Ready信号仅在游戏启动时触发一次——也就是主场景加载完成、整个初始化流程结束时发射。SceneOne在自身_Ready中订阅了这个信号,所以能正常触发Setup;但切换到SceneTwo时,Root已经完成初始化,不会再次发射Ready信号,因此SceneTwo订阅的Setup方法不会被调用。
从你的输出日志也能验证这一点:只有SceneOne的Scene 1 Ready和Scene 1 In Setup被打印,SceneTwo的对应日志完全没出现,说明Root的Ready信号没有再次触发。
解决方案
方案1:直接在场景自身的_Ready中调用Setup
场景节点的_Ready方法本身就是场景加载完成、节点树就绪后的回调,完全可以替代Root的Ready信号来触发初始化逻辑。修改代码如下:
SceneOne.cs 修改后
using Godot; public partial class SceneOne : Node3D { [Export] private string _SceneTwoPackedPath; private PackedScene _SceneTwoPacked; public override void _Ready() { _SceneTwoPacked = ResourceLoader.Load<PackedScene>(_SceneTwoPackedPath); GD.Print("Scene 1 In Ready"); // 直接调用Setup,无需订阅Root的Ready信号 Setup(); GD.Print("Scene 1 Almost Ready"); } public override void _Process(double delta) { GD.Print("Scene 1 Processing"); } public void Setup() { GD.Print("Scene 1 Ready"); GD.Print("Scene 1 In Setup"); GetTree().ChangeSceneToPacked(_SceneTwoPacked); } }
SceneTwo.cs 修改后
using Godot; public partial class SceneTwo : Node3D { [Export] private PackedScene _SceneOnePacked; public override void _Ready() { GD.Print("Scene 2 In Ready"); // 直接调用Setup Setup(); GD.Print("Scene 2 Almost Ready"); } public override void _Process(double delta) { GD.Print("Scene 2 Processing"); } public void Setup() { GD.Print("Scene 2 Ready"); GD.Print("Scene 2 In Setup"); GetTree().ChangeSceneToPacked(_SceneOnePacked); } }
方案2:自定义信号传递关卡数据(适配你的关卡随机化需求)
如果需要在加载场景(SceneOne)计算完关卡数据后,传递给游戏场景(SceneTwo)初始化,可以通过公共属性实现:
- 在SceneTwo中定义公共属性接收关卡数据:
public partial class SceneTwo : Node3D { // 用于接收加载场景传递的关卡数据 public Dictionary<string, object> LevelData { get; set; } public override void _Ready() { GD.Print("Scene 2 In Ready"); Setup(); GD.Print("Scene 2 Almost Ready"); } public void Setup() { GD.Print("Scene 2 Ready"); GD.Print("Scene 2 In Setup"); // 使用LevelData还原关卡 if (LevelData != null) { GD.Print($"还原关卡数据:{LevelData}"); } } }
- 在SceneOne的Setup中切换场景时传递数据:
public void Setup() { GD.Print("Scene 1 Ready"); GD.Print("Scene 1 In Setup"); // 随机生成关卡数据 var levelData = new Dictionary<string, object> { ["EnemyCount"] = GD.RandRange(5, 15), ["MapSeed"] = GD.RandSeed() }; // 实例化场景并传递数据 var sceneTwoInstance = _SceneTwoPacked.Instantiate<SceneTwo>(); sceneTwoInstance.LevelData = levelData; GetTree().ChangeSceneToNode(sceneTwoInstance); }
方案3:全局单例管理器(更适合复杂关卡系统)
创建一个全局的LevelManager单例,负责存储和管理关卡数据:
- 创建LevelManager.cs:
using Godot; public partial class LevelManager : Node { public static LevelManager Instance { get; private set; } public Dictionary<string, object> CurrentLevelData { get; set; } public override void _Ready() { if (Instance == null) { Instance = this; // 保持节点不被销毁 DontDestroyOnLoad(this); } else { QueueFree(); } } }
- 在SceneOne中生成数据并存入管理器:
public void Setup() { GD.Print("Scene 1 Ready"); GD.Print("Scene 1 In Setup"); // 生成随机关卡数据 var levelData = new Dictionary<string, object> { ["EnemyCount"] = GD.RandRange(5, 15), ["MapSeed"] = GD.RandSeed() }; LevelManager.Instance.CurrentLevelData = levelData; GetTree().ChangeSceneToPacked(_SceneTwoPacked); }
- 在SceneTwo的Setup中读取数据:
public void Setup() { GD.Print("Scene 2 Ready"); GD.Print("Scene 2 In Setup"); var levelData = LevelManager.Instance.CurrentLevelData; if (levelData != null) { GD.Print($"还原关卡数据:{levelData}"); } }
适配关卡随机化的建议
如果你的关卡随机化逻辑比较复杂,推荐使用全局单例管理器的方案:
- 加载场景专注于数据计算和资源预加载,完成后切换到游戏场景
- 游戏场景从管理器中读取数据,完成关卡还原
- 这种解耦方式更便于扩展,比如后续添加关卡保存、难度调整等功能
内容的提问来源于stack exchange,提问作者Stephen Hyberger
相关产品推荐
相关产品推荐

