4.2 C# 与 .NET 实操对照


4.2 C# 与 .NET 实操对照

本节摘要:C# 在 Godot 里通过绑定层与引擎互通——节点是对象,信号是事件,导出变量是特性标注的字段。本节用同一组任务的双语言实现做逐行对照,给出性能与生态的量化视角,最后划定双语言混用项目的结构约定与迁移路径。

同一个需求摆在桌上:角色受击扣血、血量归零宣告死亡、参数暴露给编辑器。两种语言各写一遍,差别在哪、代价在哪,看完就有账可算。

双语言并排:同一功能的两种写法

任务一:受击与死亡(带类型信号)

# GDScript 写法 class_name Player extends CharacterBody2D signal health_changed(current: int, max_value: int) signal died @export var max_health := 100 var _health: int func take_hit(dmg: int) -> void: _health = maxi(_health - dmg, 0) health_changed.emit(_health, max_health) if _health == 0: died.emit()
// C# 写法 using Godot; public partial class Player : CharacterBody2D { [Signal] public delegate void HealthChangedEventHandler(int current, int maxValue); [Signal] public delegate void DiedEventHandler(); [Export] public int MaxHealth { get; set; } = 100; private int _health; public void TakeHit(int dmg) { _health = Math.Max(_health - dmg, 0); EmitSignal(SignalName.HealthChanged, _health, MaxHealth); if (_health == 0) EmitSignal(SignalName.Died); } }

逐行对照能读出几件事。概念映射是系统的:信号对应事件委托、导出变量对应特性标注、引擎基类直接继承。语法重量不同:C# 多出委托声明、命名空间、花括号仪式,同样功能行数约为 GDScript 的两倍——这是"严谨税"。工具链不同:C# 侧有成熟的编译器、调试器与整套集成开发环境加持,大型重构的经验工具全部可用。

任务二:异步流程(等动画再等计时)

func intro() -> void: await fade_in() await get_tree().create_timer(1.0).timeout start_game()
public async void Intro() { await FadeIn(); // 异步方法 await ToSignal(GetTree().CreateTimer(1.0), SceneTreeTimer.SignalName.Timeout); StartGame(); }

异步的对照更耐人寻味。GDScript 的等待是语言内建、无类型色彩;C# 侧是标准异步模型,等信号要走"转信号"这座桥。纯语言内异步 C# 更顺,跨引擎信号异步 GDScript 更省。混用项目里,异步边界是设计时就要想清楚的地方——一个模块内部用哪套异步都行,跨模块最好统一走信号。

绑定层:互通的桥与税

两语言的互通不是魔法,是桥。C# 对象与引擎原生对象之间隔着一层绑定:调用引擎 API 过桥,数据在桥上转换。过桥有成本——单次调用纳秒级,无感;但每帧上万次的细粒度调用,桥费会显形。由此得出双语言的性能铁律:边界要粗,调用要少。让 C# 侧一次算完一大批,再把结果整块递给引擎,好过在两种世界间频繁穿梭。

// 反例:每帧过桥数千次 public override void _Process(double delta) { for (int i = 0; i < 5000; i++) _sprites[i].Position += new Vector2(1, 0); // 逐个过桥设置 } // 正例:整批处理,一次过桥 public override void _Process(double delta) { var positions = ComputeBatch(); // 纯 C# 批量计算 MultiSetPositions(positions); // 结果整块回传 }

生态账本:C# 带来什么、带走什么

带来的是整个 .NET 世界:成熟的单元测试框架、依赖注入容器、数学与物理库、序列化工具,以及团队招聘池——会 C# 的工程师远多于会 GDScript 的。带走的是轻量:需要运行时支持,编辑器体积变大;移动与网页平台的导出链路更复杂(第 10 章展开);构建步骤多一层,团队新人上手多一天。

性能的真实图景:纯计算密集型代码 C# 快约一个数量级;但游戏逻辑的日常形态是"大量引擎 API 调用夹一点计算",此时两语言差距被桥费摊平,常常只差两三倍甚至更少。先剖析再迁移的纪律在这里再次生效——迁移前先确认瓶颈真的是计算本身。

维度 GDScript C#
迭代速度 保存即生效,秒级 需编译,十几秒级
语法重量 轻,行数少 重,仪式感强
纯计算性能 基准 快约一个数量级
工具生态 引擎内置够用 整个 .NET 生态
团队招聘 会的人少 池子大
平台导出 全平台顺滑 移动与网页多一步
与引擎贴合度 原生 隔绑定层

混用项目的结构约定

双语言并存是受支持的正路,前提是立好规矩。

**按模块划语言,不按文件心情划。**整个系统用一种语言写完(比如战斗全 GDScript、寻路全 C#),跨语言调用只发生在模块边界。最糟糕的形态是同模块里两种语言交错调用——桥费翻倍,心智负担也翻倍。

**边界走信号与导出,不走内部类型。**跨语言接口用信号、导出变量与方法签名表达,别把一方的内部类塞给另一方——绑定层转换既费劲又容易踩类型坑。

**命名各守各的惯例。**C# 用大驼峰,GDScript 用蛇形,转换器按规则映射。写代码时别试图统一风格,尊重各自社区惯例,工具链才能正常识别。

**数据模型优先放资源。**武器配置、关卡数据这类纯数据用资源对象承载(第 8 章),两边语言都把它当数据读写,天然免疫语言边界。

迁移路径:从脚本到 C# 的五步

既有项目想把热区迁去 C#,推荐顺序:第一步,剖析器定位真热点,写成清单;第二步,把热点的输入输出整理成纯数据接口;第三步,用 C# 重写并保留同名信号与导出变量;第四步,逐个替换并在替换处做开关,两种实现可切换着跑,方便对照行为;第五步,跑回归测试,确认行为一致后删旧实现。整个过程的要义是每次只迁一个模块、始终可回退

⚠️ 常见坑:听说 C# 快就全项目改写。改写期间玩法迭代停滞,最后性能问题发现出在每帧实例化上百个节点上——语言根本不是瓶颈。迁移的启动条件只有一个:剖析报告点名某段纯计算代码独占帧时间。

本节要点回顾

  • 概念系统映射:信号对事件委托、导出对特性、基类直接继承;C# 行数约为两倍是"严谨税"
  • 桥费铁律:边界要粗调用要少,批量过桥好过高频穿梭
  • 性能真相:纯计算差一个量级,日常逻辑差得远;先剖析再迁移
  • 混用规矩:按模块划语言、边界走信号与数据、命名各守惯例
  • 迁移五步:定位 → 整理接口 → 重写 → 可切换替换 → 回归后删旧

两条脚本轨道都通了,下一节看第三轨道:什么时候需要请出原生扩展与着色器。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U