Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

C# 基础

前言

这个页面简单介绍了 C# 是什么,以及如何在 Godot 中使用 C#。然后,你可能想看看 如何使用特定功能 ,阅读有关 C# 和 GDScript API 之间的差异 并(重新)访问逐步教程的 编写脚本部分

C# 是由 Microsoft 开发的高级编程语言。在 Godot 中,它是使用 .NET 6.0 实现的。

注意

在 Godot 4 中用 C# 编写的项目目前无法导出到 Web 平台。如果要在 Web 平台上使用 C#,请考虑使用 Godot 3。 (C# 的)Android 和 iOS 平台支持从 Godot 4.2 开始提供,但仍处于实验阶段,并且 存在一些限制

备注

不是 一个关于 C# 语言整体的全面教程。如果你还不熟悉其语法或功能,请参阅 Microsoft C# 指南 或在其他地方寻找合适的介绍。

先决条件

Godot 捆绑了运行已经编译好的游戏所需的 .NET 部分,但 Godot 不包括构建和编译游戏所需的 MSBuild 和 C# 编译器等工具。这些工具包含在 .NET SDK 中,需要单独安装。

综上所述,你必须安装了 .NET SDK 支持 .NET 的 Godot 版本。

.NET 下载页面 下载并安装SDK的最新稳定版本。

重要

如果你使用 64 位版本的 Godot,请务必安装 64 位版本的 SDK。

如果你正在从源代码构建 Godot,请确保按照 使用 .NET 编译 页面中概述的步骤来启用 .NET 支持。

配置外部编辑器

Godot 内置的脚本编辑器对 C# 的支持是最小的。考虑使用外部 IDE 或编辑器,如 Visual Studio Code 或 MonoDevelop。这些都为 C# 提供了自动完成、调试和其他有用的功能。要在 Godot 中选择一个外部编辑器,点击 Editor(编辑器) → Editor Settings(编辑器设置) ,向下滚动到 Dotnet(.NET) 。在 Dotnet(.NET) 下,点击 Editor(编辑器) ,然后选择你的外部编辑器。Godot 目前支持以下外部编辑器:

  • Visual Studio 2022

  • Visual Studio Code

  • MonoDevelop

  • Visual Studio for Mac

  • JetBrains Rider

关于如何配置外部编辑器,请参见以下章节:

JetBrains Rider

阅读完 "预备知识" 部分,就可以下载安装 JetBrains Rider

在 Godot 的 Editor(编辑器) → Editor Settings(编辑器设置) 菜单中:

  • 设置 Dotnet(.NET) -> Editor(编辑器) -> External Editor(外部编辑器)JetBrains Rider

在 Rider 中:

  • 设置 MSBuild version.NET Core

  • 安装 Godot support 插件。

Visual Studio Code

看完 "预备知识" 部分,就可以下载安装 Visual Studio Code (又名 VS Code)。

在 Godot 的 Editor(编辑器) → Editor Settings(编辑器设置) 菜单中:

  • 设置 Dotnet(.NET) -> Editor(编辑器) -> External Editor(外部编辑器)Visual Studio Code

在 Visual Studio Code 中:

  • 安装 C# 扩展。

备注

如果你使用的是 Linux,需要安装 Mono SDK 才能使用 C# 工具插件。

为了配置一个用于调试的项目,你需要在 .vscode 文件夹中拥有一个 tasks.jsonlaunch.json 文件,并进行必要的配置。一个示例配置可以在 这里 找到。在 launch.json 文件中,请确保相关配置中的 program 参数指向你的 Godot 可执行文件,你可以通过将它更改为可执行文件的路径或定义一个指向可执行文件的 GODOT4 环境变量来实现。现在,当你在 Visual Studio Code 中启动调试器时,你的 Godot 项目将会运行。

Visual Studio(仅限 Windows)

下载并安装最新版本的 Visual Studio 。如果你选择了正确的工作负载,Visual Studio 将包含所需的 SDK,所以你不需要手动安装 "预先告知" 部分列出的内容。

在安装 Visual Studio 时,请选择以下工作负载:

  • .NET 桌面开发

在 Godot 的 Editor(编辑器) → Editor Settings(编辑器设置) 菜单中:

  • 设置 Dotnet(.NET) -> Editor(编辑器) -> External Editor(外部编辑器)Visual Studio

备注

如果你看到了类似“Unable to find package Godot.NET.Sdk”的错误,你的 NuGet 配置可能有问题,需要进行修复。

修复 NuGet 配置文件的简单方法就是重新生成一个。在文件浏览器窗口中前往 %AppData%\NuGet。将 NuGet.Config 文件重命名或删除。重新构建 Godot 项目时,就会自动用默认值创建该文件。

创建 C# 脚本

成功为 Godot 设置 C# 之后,在场景的节点的上下文菜单中选择 Attach Script (添加脚本) 时,应该看到以下选项:

../../../_images/attachcsharpscript.webp

请注意,虽然在某些细节上有所差别,但在使用C#进行脚本编写时,大多数概念都是相通的。如果你是 Godot 的新手,你可能希望在这个时候查看 脚本语言 上的教程。虽然在文档中缺乏详细的 C# 示例,但大多数概念可以从 GDScript 中转移过来。

项目设置和工作流程

当你创建第一个 C# 脚本时,Godot 会为你的 Godot 项目初始化 C# 项目文件。这包括生成 C# 解决方案( .sln )和项目文件( .csproj ),以及一些实用文件和文件夹( .godot/mono ) 。除了 .godot/mono 之外,所有这些文件都很重要,应该提交到你的版本控制系统中。 .godot 文件夹下的所有内容都可以安全地添加到你的版本控制系统的忽略列表中。在排除故障时, 有时删除 .godot/mono 文件夹并让它重新生成可能有所帮助。

示例

这是一个空白的 C# 脚本,带有一些注释,以演示其工作方式。

using Godot;

public partial class YourCustomClass : Node
{
    // Member variables here, example:
    private int _a = 2;
    private string _b = "textvar";

    public override void _Ready()
    {
        // Called every time the node is added to the scene.
        // Initialization here.
        GD.Print("Hello from C# to Godot :)");
    }

    public override void _Process(double delta)
    {
        // Called every frame. Delta is time since the last frame.
        // Update game logic here.
    }
}

如你所见,在 GDScript 中的全局作用域中,像 Godot 的 print 这样的函数可以在 GD 静态类中使用,该类是 Godot 命名空间的一部分。要查看 GD 类中所有方法的完整列表,请参阅: @GDScript@GlobalScope 的类参考页面。

备注

请记住,你希望附加到节点上的类应与 .cs 文件的名称相同。否则,你将会遇到以下错误:

"Cannot find class XXX for script res://XXX.cs"

C# 和 GDScript 之间的一般差异

C# API 使用 PascalCase 而不是 GDScript/C++ 中使用的 snake_case 。 在可能的情况下,字段和 getters/setters 已转换为属性。一般来说,C# Godot API 一贯力求尽可能合理。

有关更多信息,请参见 C# API 与 GDScript 的差异 页面。

警告

You need to (re)build the project assemblies whenever you want to see new exported variables or signals in the editor. This build can be manually triggered by clicking the Build button in the top right corner of the editor.

../../../_images/build_dotnet1.webp

你还需要重新构建项目集,以应用 "工具" 脚本中的更改。

目前的陷阱和已知问题

由于 Godot 中 C# 支持相对较新,因此存在一些发展过程中的问题和需要解决的事项。以下是在 Godot 中使用 C# 时应注意的最重要问题的列表,但如果有疑问,还可以查看官方的 .NET 问题的跟踪

  • 编写编辑器插件是可能的,但是目前相当复杂。

  • 热重载时,当前状态不被保存和恢复,导出变量除外。

  • 附加 C# 脚本需要引用一个类,该类名需要匹配其文件名。

  • Get()/Set()Call()/CallDeferred() 等方法,以及信号连接方法 Connect() 都依赖于 Godot 的 snake_case API 命名规范。所以, CallDeferred("AddChild") 无法调用 AddChild ,因为API需要原始 snake_case 版本的 add_child 。不过,自定义属性和方法没有此限制。建议在 PropertyNameMethodNameSignalName 中使用公开的 StringName ,以避免额外的 StringName 分配以及担心蛇形(snake_case)命名。

截止到 Godot 4.0 版本,支持将 .NET 项目导出到桌面平台(Linux、Windows和macOS)。其他平台将在未来的 4.x 版本中获得支持。

常见陷阱

你可能会在尝试修改 Godot 对象中的一些值时遇到以下错误,例如在尝试改变一个 Node2D 的 X 坐标时:

public partial class MyNode2D : Node2D
{
    public override _Ready()
    {
        Position.X = 100.0f;
        // CS1612: Cannot modify the return value of 'Node2D.Position' because
        // it is not a variable.
    }
}

这是完全正常的。C# 中的结构体(在这个例子中,是一个 Vector2 )在赋值时会被复制,意味着当你从一个属性或索引器中获取这样一个对象时,你得到的是它的一个副本,而不是它本身。修改这个副本而不重新赋值是没有任何效果的。

解决方法很简单:获取整个结构体,修改你想要修改的值,然后重新赋值给属性。

var newPosition = Position;
newPosition.X = 100.0f;
Position = newPosition;

自 C# 10 起,还可以在结构体上使用 with 表达式 ,让你只需一行实现相同的效果。

Position = Position with { X = 100.0f };

你可以在 C# 语言参考 中了解更多关于这个错误的信息。

C# 在 Godot 中的性能

根据一些初步的 基准测试 ,C# 在 Godot 中的性能,虽然总体在相同数量级,但在某些简单情况下大约是 GDScript 的 ~4× 。C++ 仍然稍微快一些;具体情况将根据你的使用情况而变化。对于大多数常规脚本工作负载而言,GDScript 可能足够快。

大多数基于 GodotObject 的 Godot C# 对象(例如任何像 Control 这样的 NodeCamera3D 这样的 Node3D )的属性,需要使用本地(互操作)调用,因为它们与 Godot 的 C++ 核心进行通信。如果你需要在单个代码位置多次修改或读取这些属性的值,请考虑将其分配给本地变量:

using Godot;

public partial class YourCustomClass : Node3D
{
    private void ExpensiveReposition()
    {
        for (var i = 0; i < 10; i++)
        {
            // Position is read and set 10 times which incurs native interop.
            // Furthermore the object is repositioned 10 times in 3D space which
            // takes additional time.
            Position += new Vector3(i, i);
        }
    }

    private void Reposition()
    {
        // A variable is used to avoid native interop for Position on every loop.
        var newPosition = Position;
        for (var i = 0; i < 10; i++)
        {
            newPosition += new Vector3(i, i);
        }
        // Setting Position only once avoids native interop and repositioning in 3D space.
        Position = newPosition;
    }
}

将原始数组(例如 byte[] )或 string 传递给 Godot 的 C# API 需要进行数据封装,这在性能上相对较昂贵。

string 隐式转换为 NodePathStringName 会产生本地互操作和数据封装的成本,因为必须将 string 封装并传递到相应的本地构造函数。

在 Godot 中使用 NuGet 包

NuGet 包可以与 Godot 一起安装和使用,就像任何 C# 项目一样。许多 IDE 都可以直接添加软件包,也可以通过在项目根目录下的 .csproj 文件中添加软件包引用来手动添加它们:

    <ItemGroup>
        <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
    </ItemGroup>
    ...
</Project>

从 Godot 3.2.3 开始,Godot 在下次构建项目时自动下载并设置新添加的 NuGet 包。

分析 C# 代码

以下工具可用于对托管代码进行性能和内存分析:

  • JetBrains Rider 配合 dotTrace/dotMemory 插件。

  • 独立版的 JetBrains dotTrace/dotMemory。

  • Visual Studio。

使用 JetBrains tools 和 Visual Studio 可以同时对托管和非托管代码进行分析,但仅限于 Windows。