Godot开发-场景实例化

前言

在 Godot 引擎中使用 C# 实例化场景(Scene)是一个常见操作,通常用于动态加载和生成游戏对象(如敌人、子弹、UI 元素等)。

方式 代码 优点 缺点
[Export] + 编辑器赋值 public PackedScene MyScene; 安全、灵活、无需硬编码路径 需手动拖拽
GD.Load<PackedScene>(path) GD.Load<PackedScene>(path) 动态加载 路径易错,性能稍差(应缓存)
ResourceLoader.Load ResourceLoader.Load<PackedScene>(path) 更底层控制 略微复杂

实例化场景

方式1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using Godot;

public partial class GameManager : Node2D
{
// 方式 1️⃣:通过 [Export] 在编辑器中拖拽赋值(推荐!)
[Export]
public PackedScene EnemyScene { get; set; }

public override void _Ready()
{
// 如果未在编辑器赋值,可 fallback 到代码加载(不推荐频繁使用)
if (EnemyScene != null)
{
// 创建实例
var enemyInstance = EnemyScene.Instantiate<Enemy>();

// 初始化位置
enemyInstance.Position = new Vector2(100, 200);

// 必须添加到场景树!否则不会渲染或运行
AddChild(enemyInstance);
}
}
}

关键点

  • 使用 [Export] 让你在 Godot 编辑器中直接拖拽 .tscn 文件 到属性上,避免硬编码路径。
  • Instantiate<T>() 返回强类型对象(需继承自 Node)。
  • 必须调用 AddChild(),否则节点不在场景树中,不会被处理。

方式2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using Godot;

public partial class GameManager : Node2D
{
private PackedScene EnemyScene;

public override void _Ready()
{
// 如果未在编辑器赋值,可 fallback 到代码加载(不推荐频繁使用)
if (EnemyScene == null)
{
EnemyScene = GD.Load<PackedScene>("res://Enemies/Enemy.tscn");
}

// 创建实例
var enemyInstance = EnemyScene.Instantiate<Enemy>();

// 初始化位置
enemyInstance.Position = new Vector2(100, 200);

// 必须添加到场景树!否则不会渲染或运行
AddChild(enemyInstance);
}
}

方式3

1
2
3
4
if (EnemyScene == null)
{
EnemyScene = ResourceLoader.Load<PackedScene>("res://Scenes/Enemy.tscn");
}

在 Godot C# 中,GD.Load<PackedScene>("...")ResourceLoader.Load<PackedScene>("...") 功能上几乎完全相同,它们最终都会调用引擎底层的资源加载机制。

ResourceLoader.Load() 支持更多重载。

例如:

1
2
// 指定类型提示和缓存模式(虽然 C# 泛型通常已足够)
var scene = ResourceLoader.Load("res://Enemies/Enemy.tscn", "PackedScene", ResourceLoader.CacheMode.Ignore);

GD.Load<T>() 是简化封装,不支持这些参数:

1
var scene = GD.Load<PackedScene>("res://Enemies/Enemy.tscn"); // 无额外选项

结论

日常开发推荐使用 GD.Load<T>():代码更简洁,类型安全,符合 C# 习惯。

清理实例

清理实例(避免内存泄漏)

当不再需要实例时,应从场景树中移除:

1
2
// 完全删除节点(推荐)
enemyInstance.QueueFree(); // 异步安全释放(下一帧执行)