Godot C# 脚本开发

FreeGuideOnline 最新 2026-07-13

csharp using Godot; using System;

public partial class MyNode : Node2D { public override void _Ready() { GD.Print("Hello from C#!"); } }


点击运行当前场景,控制台将输出 `Hello from C#!`,表明脚本工作正常。

### 基本代码结构解析
- `using Godot;` 引入 Godot 核心命名空间,所有 Godot 类型都在此。
- `partial` 关键字表明该类会与 Godot 引擎自动生成的代理类合并,不要删除。
- `_Ready()` 是 Godot 生命周期方法,在节点进入场景树并准备就绪时调用。
- `GD.Print()` 对应 GDScript 的 `print()`,输出调试信息到控制台。

## C# 脚本的核心概念

### 节点路径与访问
在 C# 中操作节点,需要获取节点引用。最常见的方式是在场景里通过 `GetNode<T>()` 方法按路径查找。

```csharp
// 获取子节点 "Sprite2D"
var sprite = GetNode<Sprite2D>("Sprite2D");
// 获取唯一命名的节点(使用 % 前缀,类似场景唯一名称)
var player = GetNode<CharacterBody2D>("%Player");

注意:使用 % 前缀必须在 Godot 编辑器中右键节点 -> 设为场景唯一名称

信号(Signal)的连接

Godot 信号机制在 C# 中通过 [Signal] 特性和委托事件实现。可以从编辑器连接,也可以用代码手动连接。

编辑器连接:在节点面板的“信号”选项卡,双击信号并选择目标节点与方法,Godot 会自动生成方法桩。

代码连接

public override void _Ready()
{
    Button button = GetNode<Button>("Button");
    button.Pressed += OnButtonPressed;
}

private void OnButtonPressed()
{
    GD.Print("按钮被按下");
}

发射自定义信号:

[Signal]
public delegate void HealthDepletedEventHandler();

public void TakeDamage(int amount)
{
    // ... 处理伤害逻辑
    if (health <= 0)
        EmitSignal(SignalName.HealthDepleted);
}

SignalName.HealthDepleted 是由 [Signal] 特性自动生成的常量,避免字符串硬编码。

引用类型与资源

Godot 的资源在 C# 中使用相同类名。例如加载一个 PackedScene 并实例化:

PackedScene bulletScene = GD.Load<PackedScene>("res://bullet.tscn");
Node2D bullet = bulletScene.Instantiate<Node2D>();
AddChild(bullet);

推荐在类顶部用 Resource 特性(或直接导出属性)方便在编辑器中拖拽赋值:

[Export]
public PackedScene BulletScene { get; set; }

输入处理

_Process_PhysicsProcess 中检测输入。C# 使用 Input 单例,方法名称与 GDScript 略有不同,遵循 PascalCase 命名。

public override void _PhysicsProcess(double delta)
{
    Vector2 velocity = Vector2.Zero;

    if (Input.IsActionPressed("ui_right"))
        velocity.X += 1;
    if (Input.IsActionPressed("ui_left"))
        velocity.X -= 1;
    if (Input.IsActionPressed("ui_down"))
        velocity.Y += 1;
    if (Input.IsActionPressed("ui_up"))
        velocity.Y -= 1;

    // 归一化后移动
    velocity = velocity.Normalized() * speed;
    // 假设我们有一个 CharacterBody2D 节点
    // 通常使用 _PhysicsProcess 来调用 MoveAndSlide()
}

注意:_Process 接收 double delta,用于逐帧更新;_PhysicsProcess 接收固定的物理步骤 double delta

常用 API 快速参考

数学与坐标

  • Vector2, Vector3 代表二维和三维向量,运算与 GDScript 一致。
  • 坐标转换:Position, GlobalPosition, Transform 等属性直接访问。
  • 实用函数:Mathf.DegToRad(45), Mathf.Sin(angle) 等来自 Godot.Mathf

延时与定时器

// 创建一次性计时器(使用场景树定时器最安全)
Timer timer = new Timer();
timer.WaitTime = 2.0;
timer.OneShot = true;
timer.Timeout += () => GD.Print("2秒到");
AddChild(timer);
timer.Start();

// 简单等待几帧
await ToSignal(GetTree().CreateTimer(1.0), SceneTreeTimer.SignalName.Timeout);
GD.Print("等待1秒后执行");

ToSignal 返回 SignalAwaiter,必须在异步方法(async)中使用,并且不能在 _Ready 中直接 await 而不声明 async

场景切换

PackedScene nextScene = GD.Load<PackedScene>("res://Level2.tscn");
GetTree().ChangeSceneToPacked(nextScene);

获取当前场景树根节点

Node root = GetTree().Root;
Node currentScene = GetTree().CurrentScene;