先聊点题外话。最近一圈独立游戏开发社区和 Game Jam 的统计榜陆续出来之后,很多群里都在讨论同一件事:Godot 首次在热门游戏引擎的使用率上超过了 Unity。对关注引擎生态的开发者来说,这算是九年来一个挺有标志性的反转。标题里那句“最大反转”可能带点情绪,但背后的趋势是真实的:开源引擎正在吃掉商业引擎原本最稳的独立游戏盘面。
这篇文章不是想制造 Unity 和 Godot 的对立,而是想借着这次热度,把两边的真实差异、选型逻辑、迁移思路,以及 Godot 的入门实操完整拆一遍。无论你是刚接触游戏开发的新人,还是从 Unity 转过来的老手,又或者只是被 C# 和 GDScript 的对比搅得有点晕,这篇都能给你一个相对清晰的坐标系。
1. 事件背景:Godot 超越 Unity 到底意味着什么
1.1 这个“超越”是怎样发生的
先说清楚,这里说的“超越”,并不是指商业市场规模或者大作数量,而是集中在独立游戏开发者和 Game Jam 参赛者之间的使用率统计。GMTK Game Jam 这类全球性活动每年都会公布参赛者使用的引擎分布,这些年 Unity 一直稳居前列。但在最近一届的数据里,Godot 的占比首次明显反超,这就成了很多人眼中的“历史性时刻”。
要知道,Unity 在过去九年里几乎就是独立游戏开发者的默认选项。从手机小游戏到 Steam 上的 2D 横版,再到部分 3D 作品,Unity 的教程数量、资源商店、社区问答都形成了强大的惯性。而 Godot 能在这种惯性下实现反超,说明开发者的选择逻辑正在发生变化,单靠“教程多”和“用的人多”已经不足以绑定新项目。
1.2 为什么这件事值得关注
引擎不仅是工具,也是项目技术选型的一部分。对独立开发者来说,引擎决定了:
- 代码采用什么语言组织(C#、GDScript 还是 C++/Rust);
- 美术资产导入与渲染管线的适配成本;
- 发布到 Steam、移动端、Web 端的效率;
- 长期维护时社区能提供多少可查的解决方案;
- 以及最实际的:引擎本身会不会因为商业策略调整而影响项目的收益结构。
当 Godot 的使用率上升,意味着开源引擎在生态成熟度上已经跨越了“能玩”的门槛,开始在“好用”和“省心”上跟商业引擎正面竞争。这个节点对准备选引擎的新项目是有实际参考价值的。
2. Godot 与 Unity 的核心差异
2.1 授权模式与商业风险
Unity 的商业模式经历了多次调整,从早期的免费+Pro 授权,到后来加入订阅制,再到 2023 年那场引发巨大争议的 Runtime Fee 政策讨论。虽然 Unity 后续做了妥协和调整,但这件事让很多开发者意识到:商业引擎的定价策略,是可以随时变化的。
Godot 采用 MIT 开源协议,意味着:
- 引擎本身完全免费;
- 没有按席位收费的编辑器授权;
- 没有按安装量或收入抽成的 Runtime 费用;
- 可以自由修改引擎源码,甚至 fork 成自己的版本;
- 商用项目没有分成压力。
对于长期做独立游戏、或者想自研内部工具链的团队来说,这种“代码在自己手里”的安全感是商业引擎给不了的。当然,开源也意味着你需要自己承担部分引擎级问题的排错成本,不是所有功能都有人替你兜底。
2.2 开发语言:GDScript 与 C#
这是新手最纠结的地方,也是很多 Unity 老玩家转到 Godot 后最大的不习惯。
Unity 这边:
- 主语言是 C#,属于强类型静态语言,类型系统完整,IDE 支持成熟(Rider、Visual Studio、VS Code 都很好用);
- 生态里有大量现成的 C# 库,不仅是游戏逻辑,还能做编辑器扩展、工具链、后端服务;
- 对熟悉 .NET 的开发者来说,Unity 更像是在“用 C# 写程序,顺便调引擎”。
Godot 这边:
- 官方主推 GDScript,语法接近 Python,动态类型为主,也支持静态类型标注;
- GDScript 与引擎的节点/信号系统深度绑定,写起来非常“直觉化”;
- Godot 4 也完整支持 C#,基于 .NET 6/8,可以用 C# 写游戏逻辑,但部分平台导出时需要额外配置;
- 此外还支持 C++、Rust、GDScript 2.0 等,扩展能力强。
简单来说:如果你追求快速上手、写脚本像写操作清单一样顺手,GDScript 会很舒服;如果你更看重类型安全和 .NET 生态复用,Godot 的 C# 支持也足够撑起中大型项目。
2.3 渲染管线和性能取向
Unity 的渲染管线相对成熟,URP(通用渲染管线)和 HDRP(高清渲染管线)覆盖了从移动端到 PC 高画质的宽度,加上大量商业插件,能在短时间内做出不错的画面效果。
Godot 4 在渲染上做了大重构:
- 新的 Vulkan 渲染器,支持 Forward+ 和 Mobile 两种后端;
- 2D 渲染引入了 GPU 粒子、2D 光照、法线贴图等能力;
- 3D 的 SDFGI(有向距离场全局光照)让动态光照效果有了质的提升;
- 内置的物理引擎在 Godot 4 中也换成了 Godot Physics,行为更稳定。
如果你是做 2D 游戏,Godot 的 2D 工作流其实比 Unity 更清爽,没有多余的坐标转换概念,2D 的世界坐标直接以像素为单位,锚点、对齐、TileMap 的操作都非常直观。
2.4 编辑器体验与资源商店
Unity 的 Asset Store 积累了大量资源,从模型、动画、插件到完整框架都有,这是它的护城河之一。但问题在于资源质量参差不齐,而且很多老插件在升级引擎版本后会出现兼容性问题。
Godot 的 Asset Library 规模比 Unity 小很多,但胜在质量相对聚焦,而且很多功能(比如 TileMap、动画树、导航网格)已经内置,不需要像 Unity 那样靠插件补齐。此外,Godot 编辑器本身是“场景即代码”的思路,.tscn文件是纯文本格式,方便版本控制对比和合并,这对多人协作非常友好。
3. 热度背后:为什么越来越多开发者转向 Godot
3.1 轻量、启动快、配置少
Godot 编辑器安装包只有几十 MB,解压即用,不需要额外装依赖。相比之下,Unity Hub、编辑器版本、模块包、许可证激活这一套流程,对新手和硬件配置一般的开发者来说本身就是门槛。
这个“轻”不仅体现在安装上,也体现在项目文件上。Unity 项目的 Library、Temp 目录动不动几个 GB,Git 仓库需要各种忽略规则。Godot 项目基本就是脚本、场景、资源文件,.godot缓存目录可以整个忽略,版本控制非常清爽。
3.2 免费无分成,适合长线项目
独立游戏开发本身周期就长,很多项目做两三年才上线,期间的引擎费用、插件费用都是持续成本。Godot 的 MIT 协议意味着哪怕项目赚到钱,也不用担心引擎方突然调整分成比例或订阅价格。
对个人开发者和小团队来说,这算是一种“把不确定性降到最低”的选择。
3.3 社区和教程生态在快速补齐
以前提到 Godot,很多人第一反应是“资料少,遇到问题搜不到”。但近两年情况变化很明显:
- Godot 官方文档越来越完善,尤其是 Godot 4 的文档;
- YouTube 上有大量完整的 Godot 教程系列;
- 热词里也能看到“手把手带你 Godot 游戏开发”“Godot AI”“Godot 4 Third Person Starter Project”这类内容,说明教程供给已经覆盖到具体场景;
- CSDN 和社区里也开始出现成体系的 Godot 入门文章和踩坑记录。
当然,和 Unity 海量的中文教程相比还有差距,但差距在肉眼可见地缩小。
3.4 对 Unity 信心变化的客观观察
2023 年 Unity 的 Runtime Fee 政策虽然最终调整,但已经让不少开发者意识到:商业引擎的条款可以因为市场压力而改变,而开发者的项目却被绑定在引擎上。越是依赖特定引擎的项目,越难在政策变动时快速迁移。
所以很多新项目在立项时,会直接把 Godot 放进候选名单,哪怕只是做技术验证,也是一种风险对冲。热度上升不是偶然的,而是理性选择的结果。
4. 从 Unity 迁移到 Godot:先搞懂概念对照
如果你是从 Unity 转过来,建议先放弃“逐 API 对应”的思路,因为两个引擎的设计哲学差别很大。Unity 是组件式(Component-based),Godot 是节点+场景树(Node & Scene Tree),但两者在某种程度上是“殊途同归”的。
4.1 核心概念对照表
| Unity 概念 | Godot 对应概念 | 备注 |
|---|---|---|
| GameObject | Node | 场景中的基本对象 |
| Component(MonoBehaviour 等) | 附加在 Node 上的脚本(Script) | Godot 中脚本就是节点行为的一部分 |
| Prefab | PackedScene(.tscn 文件) | 预制的独立场景或对象 |
| Scene | Scene(.tscn 文件) | 整个关卡、UI、对象都可以是场景 |
| Transform | Node2D / Node3D 的内置属性 | 位置、旋转、缩放都在节点属性里 |
| Inspector 窗口 | Inspector(检查器) | 类似,但默认只显示节点属性和导出变量 |
| Update() | _process(delta) / _physics_process(delta) | 每帧回调,物理帧独立 |
| Start() / Awake() | _ready() / _init() | 节点进入场景树时调用 |
| Instantiate() | 加载 PackedScene 并实例化 | 动态创建对象的方式 |
| Tag | Group | 用分组标记对象,比 Tag 更灵活 |
| Layer(碰撞层) | Collision Layer / Mask | 类似,但配置在物理属性里 |
4.2 Unity C# 脚本与 Godot GDScript 对比
以最基础的“按下方向键移动”为例,两者对比如下。
Unity C#:
using UnityEngine; public class PlayerMovement : MonoBehaviour { public float speed = 5f; void Update() { float horizontal = Input.GetAxis("Horizontal"); float vertical = Input.GetAxis("Vertical"); Vector3 direction = new Vector3(horizontal, vertical, 0); transform.Translate(direction * speed * Time.deltaTime); } }Godot GDScript:
extends CharacterBody2D @export var speed: float = 5.0 func _physics_process(delta): var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") velocity = direction * speed * 100 move_and_slide()可以看到,GDScript 的代码更集中,不需要额外引用命名空间,Input.get_vector直接把四个方向合成了向量,move_and_slide()自动处理碰撞。Unity 的写法也没有问题,只是思路不同:Unity 更偏向“组件对外提供方法”,Godot 更偏向“节点自身就是一个行为单元”。
5. Godot 入门实战:从创建项目到 2D 角色移动
下面用一个完整的 2D 角色移动示例,带你走一遍 Godot 4 的核心工作流。
5.1 创建项目与场景结构
打开 Godot 4,点击“新建项目”,输入项目名称,选择一个空目录,渲染器选择“Forward+”(如果目标是低端设备或 Web,可以选“Mobile”或“Compatibility”)。
创建完成后,在文件系统面板中右键新建场景,选择“2D 场景”。把根节点重命名为Player,类型保持Node2D。
接下来为玩家添加可视部分和碰撞体:
- 右键
Player,添加子节点Sprite2D,并给它的Texture分配一张图片资源; - 再添加子节点
CollisionShape2D,在Shape属性中选择RectangleShape2D或CircleShape2D; - 最后把根节点
Player的类型改为CharacterBody2D,这样节点才能处理物理移动和碰撞检测。
5.2 编写 GDScript 脚本
选中Player节点,点击“附加脚本”,保留默认路径player.gd,然后把脚本内容改成:
extends CharacterBody2D @export var move_speed: float = 300.0 func _physics_process(delta: float) -> void: # 获取输入:wasd 或方向键,引擎默认映射了 ui_left/ui_right/ui_up/ui_down var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") velocity = input_dir * move_speed move_and_slide()代码解释:
extends CharacterBody2D:声明当前脚本挂在 CharacterBody2D 上,获得move_and_slide()方法;@export var move_speed: float = 300.0:在检查器中暴露一个速度参数,方便调整,不需要改代码;Input.get_vector(...):返回一个二维向量,范围在 -1 到 1 之间,方向由输入决定;velocity:CharacterBody2D 内置的速度属性,赋值后通过move_and_slide()进行带碰撞的移动。
5.3 添加地面和碰撞目标
创建一个新的 2D 场景,保存为main.tscn,作为主场景。在main根节点下添加:
- 子节点
StaticBody2D,重命名为Ground; - 给
Ground添加CollisionShape2D,形状选WorldBoundaryShape2D或RectangleShape2D,放在画面底部; - 把
player.tscn实例化为main的子节点。
最后,把main.tscn设置为主场景:点击顶部菜单“项目”->“项目设置”->“常规”->“主场景”,选择main.tscn。
5.4 运行验证
按 F5 运行项目。如果一切正常,你会看到角色可以随方向键移动,并在碰到地面和边界时停下来,不会穿透。
如果角色出生位置不对,可以在编辑器中直接拖动Player节点调整位置。也可以代码初始化:
func _ready() -> void: global_position = Vector2(100, 100)global_position是全局坐标,position是相对父节点的坐标。在根节点下两者一致,在多层场景中要注意区分。
5.5 使用 C# 编写的版本
如果你更喜欢 C#,可以创建脚本时语言选择 C#。Godot 4 的 C# 脚本要求项目启用了 .NET 版本编辑器。核心逻辑如下:
using Godot; public partial class Player : CharacterBody2D { [Export] public float MoveSpeed { get; set; } = 300.0f; public override void _PhysicsProcess(double delta) { Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); Velocity = inputDir * MoveSpeed; MoveAndSlide(); } }注意 C# 脚本的类名要和文件名一致,并且继承自对应的 Godot 类型。在 Godot 4 中,C# 脚本类通常用public partial class声明,这样 Godot 的源代码生成器才能正确处理节点引用和信号绑定。
6. 常见问题与排查思路
6.1 按 F5 运行后黑屏或看不到任何物体
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 运行后黑屏 | 主场景未设置 | 在项目设置中指定主场景 |
| 场景里有节点但相机没对准 | 2D 场景缺少 Camera2D | 给主场景添加 Camera2D 子节点 |
| 物体位置不在视图范围内 | 坐标过大或过小 | 检查 global_position,必要时在 _ready 中重置 |
6.2 角色移动后卡在墙里
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 角色碰撞后抖动 | 物理帧中直接修改 position | 使用 velocity 和 move_and_slide(),不要直接改 position |
| 角色能穿过地面 | CollisionShape2D 形状与显示不符 | 检查碰撞形状的大小和位置 |
| 角色移动速度在不同机器上不同 | 没有乘 delta | 物理处理中必须基于 delta 计算,move_and_slide 内部会自动处理 |
6.3 GDScript 导入时报错 “Identifier not declared in the current scope”
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 脚本文件报错 | 引用了未定义的变量或函数 | 检查变量名、方法名拼写 |
| 节点名引用失败 | $"NodeName"写错 | 用get_node("NodeName")或确认节点路径 |
| 导出变量不显示 | 忘了@export前缀 | 在变量前加@export |
6.4 C# 编译报错 Assembly 不匹配
用 C# 开发 Godot 时,最常见的坑是 .NET SDK 版本与 Godot 内置版本不一致。Godot 4 的 .NET 版本需要安装对应版本的 .NET SDK,建议按照官方文档检查。如果出现Project is not configured correctly,先确认:
- 使用的是 Godot .NET 版编辑器,不是普通版;
- .NET SDK 版本满足要求;
- 项目生成后重新构建解决方案再运行。
7. 引擎选型与工程规范建议
7.1 小团队和个人开发者怎么选
没有“最好的引擎”,只有“更适合当前项目的引擎”。可以从下面几个维度评估:
选 Unity 的情况:
- 项目需要大量现成的插件和资源商店资产;
- 团队已经熟悉 C# 和 .NET 生态;
- 需要成熟的移动端性能调优工具(比如 URP、Profiler、Memory Profiler);
- 团队具备处理商业许可证变更的经验和预案。
选 Godot 的情况:
- 项目偏 2D,或者 2D+轻度 3D;
- 团队预算有限,希望引擎长期零成本;
- 对开源可控性和版本管理有要求;
- 希望在开发早期就能快速迭代,减少环境配置开销;
- 愿意接受教程和资源相对少的事实,但能通过官方文档和源码解决问题。
7.2 Godot 项目的工程规范
在实际项目中,以下几点能显著提升维护效率:
节点命名规范:
- 使用 CamelCase,如
PlayerSprite、LevelTileMap; - 场景根节点名与文件名保持一致;
- 脚本文件名与场景节点名保持一致。
场景拆分原则:
- 把可复用的对象单独做成场景,比如敌人、道具、UI 元素;
- 场景之间通过信号(Signal)通信,避免节点深度耦合;
- 每个场景尽量结构扁平,不要嵌套太深。
信号(Signal)优先:
Godot 的信号机制类似 Unity 的 Event 或 C# 的事件。子节点需要通知上层时,定义信号,上层连接;不要通过get_parent().get_parent()这样脆弱的方式反向访问。
# 敌人脚本示例 signal died func _on_hit(): died.emit()# 主场景连接信号 func _ready(): $Enemy.died.connect(_on_enemy_died) func _on_enemy_died(): print("敌人已消灭")版本控制:
.gitignore忽略.godot/目录;.tscn、.tres都是文本格式,可以正常 diff;- 图片、音频一类的二进制资源单独管理,避免大文件进入 Git。
7.3 C# 与 GDScript 的选用建议
如果项目以玩法原型为主,或者团队里没有强 C# 背景的成员,直接用 GDScript 是最快的路径。如果项目要做线程、网络、复杂数据结构和大量第三方 .NET 库集成,用 C# 会更顺手。
需要说明的是,Godot 的 C# 支持和 GDScript 不是互相排斥的关系,同一个项目里可以混用。但要注意两点:
- 信号和跨语言调用有轻微开销,尽量在边界层做好封装;
- 某些社区插件可能只提供 GDScript 版本,也可能只支持 C#,选型前先查一下目标插件的语言支持。
7.4 学习路线建议
如果你想认真学习 Godot,建议按下面的顺序推进:
- 先掌握 GDScript 基础语法:变量、函数、条件、循环、数组、字典;
- 理解场景树和节点:
_ready()、_process()、_physics_process()的生命周期; - 独立完成一个 2D 小游戏:角色移动、碰撞、敌人 AI、UI;
- 学习信号和自定义资源,搭建可复用的系统;
- 接触 3D 基础:节点、相机、光照、材质;
- 尝试把项目导出到目标平台,体验发布流程;
- 回到源码或官方文档,针对具体问题做深度研究。
8. 结尾
Godot 首次超越 Unity 这件事,真正有价值的不是“谁更厉害”的结论,而是它提醒所有开发者:引擎生态不是一成不变的,选型时要考虑的不只是今天能搜到多少教程,还包括未来几年项目发展的确定性和可控性。开源引擎的崛起对从业者来说,多了一个选择,也少了一份绑定。
如果你之前一直在 Unity 的舒适区里,现在是一个不错的时机去 Godot 里做一个几十 MB 的小原型,体验一下不同的工作流。如果你刚接触游戏开发,也可以直接在 Godot 开始——轻量、免费、够用,而且社区正在变得越来越友好。
另外提醒一句:无论选哪个引擎,都要尽早把项目工程规范(命名、场景拆分、信号通信、版本控制)立起来。这和引擎选型同样重要,甚至更重要。开发游戏从来不是“打开引擎写代码”这么简单,但拥有一个趁手的工具,确实能让事情顺很多。希望这篇能帮你在选型和入门的路上少踩几个坑。