























铺垫了这么多,(我觉得)终于有足够的基础知识可以到Godot中用C#编写脚本了。
请先使用.NET版的Godot创建一个新项目作为试验场地,我们先从编写一个简单的玩家脚本开始熟悉Godot的C# API。
支持.NET的Godot和标准版的Godot在界面和核心功能上区别不大。我们新建一个2D场景,然后同样是点击添加脚本按钮新建脚本。
和标准版不一样的是,这里的语言选项除了GDScript之外多了一个C#选项。
这里需要选择C#。当然在.NET版中GDScript也是支持的,后面会将如何用交互使用两种不同的脚本语言。
设置外部编辑器
一般来说我们会在其它编辑器或者IDE中编辑C#代码,这里需要检查一下Godot的外部编辑器设置。
在Editor->Editor Settings(编辑器设置)中,打开高级设置开关,找到或者搜索DotNet部分。选择Editor(编辑器)选项以进行C#编辑器相关的设置。
一般来说只要你正常安装了你的IDE或者编辑器,这里勾选使用外部编辑器,选择你需要用的编辑器就好了。
第一个C#脚本
如果你打开刚才新建的C#源文件,你首先会看到这个类的头部有一个我还没提到过的关键字:
public partial class Player : CharacterBody
partial关键字表示这里的代码只是这个类的部分定义,还有部分定义出现在其它地方。
为什么会这样呢?因为Godot会为你的C#脚本通过源代码生成器生成一写额外的代码,这些代码用于支持你的代码和Godot交互,也是为了方便开发。只要你需要创建一个“脚本”——一个继承了Godot内置类型的C#脚本,建议直接从Godot编辑器中创建!这样可以避免一些麻烦或者Godot抽疯。
命名空间Godot
通过Godot编辑器创建的C#脚本会自带using Godot;,Godot的API都放在这个命名空间里。如果你需要在非脚本文件中使用Godot提供的各种类型或者功能,只需要using一下就可以了。
重写通知方法
我们在GDScript中经常会写一些下划线开头的特殊方法来实现游戏功能,比如_ready、_process啥的。这些方法主要是响应一些比较关键的引擎事件通知,这些方法可以在GDScript中重写,只不过GDScript不用override关键字罢了。
自然,这些方法在C#中也表现为对对应方法的重写。由于代码风格的不同,这些方法在C#中也以下划线开头,但是首字母是大写的。
我@onready呢?
我们时常需要通过一个变量来引用当前场景下的某个节点,但是获取节点只有在场景准备完成之后才是安全的。GDScript的@onready本身也就是个语法糖,相当于在_ready中调用get_node方法获得对节点的引用。
Godot自己没有通过某种办法在C#中加入这个功能。因此要获得对节点的引用,需要在_Ready中调用GetNode:
private Area2D area; public override void _Ready() { area = GetNode<Area2D>("Area2D"); }
GetNode方法在C#中会有多个重载版本,一般来说直接用泛型版本,传入你的节点的类型作为类型参数就可以了。这样就不用把返回值又转换一遍类型。参数同样是用字符串表示的节点路径。
这个东西很微妙,有其他开发者利用代码生成器生成这些代码,但是实际上也省不了多少代码。
我print呢?
天经地义的,在开发过程中,我们时常会需要往终端输出一些文本便于调试。
当然,你仍然可以使用C#的Console.WriteLine,你的游戏也不会报错也不会怎样。只不过Console.WriteLine输出的东西不会显示在Godot的输出面板中。你可能注意到无论是标准版还是.NET版Godot都有两个启动文件,有一个是带console字样的。带console字样的会启动一个终端,终端里会显示输出信息。如果你用Console.WriteLine输出内容的话,输出的东西会显示在这个终端中(其实你的编IDE中的终端也会显示)。
话说回来,你其实也可以用Godot的print。正如前面所说,C#是相当面向对象的语言,没有所谓的全局函数。而在GDScript中表现为全局函数的那些常用函数,在C#的API中也得放到一个类中。这个类就是GD。要使用GDScript中的print,只需要调用GD.Print。
我数学函数呢?
GDScript的全局函数中有很多数学函数,这些函数没有被引入GD类中。
前面的文章中提到过,C#的标准库中提供了一个System.MathF(考虑到Godot主要使用float,你可能不太会用System.Math),里面有各种常用的数学函数。然而,Godot的C# API也提供了GDScript里那些数学函数,它们放在Godot.Mathf里面。我觉得这个名字真得改,太容易混淆了。
那么你该用哪个呢?毕竟System.MathF和Godot.Mathf提供的函数实际上有一些是重合的。考虑到Godot本身是C++写的,有些API可能会涉及跨语言调用,这个过程可能会有一些额外的消耗。因此C#里本身就有的函数我认为应该考虑使用C#的版本。当然,Godot.Mathf里面还有一些是System.MathF没有的(比如Lerp),这种函数就别多想了,直接用就是了。
我@export呢?
好在@export在C#中有一个比较简单的写法。比如你想export玩家的移动速度,你要这样写:
[Export] private int speed;
[Export]是C#中特性(attribute)的语法。它可以给C#中的各种元素(比如字段、属性、方法、类型)添加一些“元数据”。所谓元数据就是比代码本身更高层次的数据,这些东西会给编译器或者其它要读取、检查代码本身的代码提供额外的信息。说白了,这些东西说到底是给其他东西看的,我们直接用就好。
它的语法是方括号中间写上某个特性的构造函数。由于C#的语法糖设计,Export本身叫ExportAttribute,后面的Attribute可以省掉。如果这里调用的无参构造函数,括号也可以省掉。所以总的来说特性的语法还是比较简洁好看。如果你喜欢也可以把特性写在前面:[Export] private int speed;。当然有些时候只能写在前面,比如应用到参数的时候。
如果你有更详细的配置需要,也可以在C#中实现。比如你在GDScript中写@export_category("Character")来给属性分类展示,你在C#中也可以写:
[ExportCategory("Character")] [Export] private int speed;
当然有一些东西和GDScript是不一样的。比如你想限制speed的范围,在GDScript中你会写@export_range(1, 100)。在C#中没有单独这样一个特性,而是需要单独在Export特性的参数中指明:
[Export(PropertyHint.Range, "1,100")] private int speed;
Export的这个版本的构造函数有两个参数,第一个参数是所谓PropertyHint也就是告诉Godot你想要在编辑器中如何显式它的编辑项目。第二个参数比较抽象,是一个字符串,不同的hint写法也不一样。对于这里的范围来说,这个字符串应该是一个用逗号隔开的几个数字。最多写3个数字,分别对应最小值、最大值、步长。步长就是你点击编辑器中的上下箭头时,这个属性要加减多少。
更具体的要查看Godot的文档中对GDScript和C#中export相关的介绍。
如果你使用过Unity,可能记得Unity的[SerializeField],功能是类似的。Godot的[Export]不仅支持字段也支持属性,并且非public的访问级别也支持。
向量使用的问题
假如我们要实现一个基本的移动功能,我们在GDScript中可能会这样做:
if Input.is_action_just_pressed("right"): velocity.x = speed
GDScript的Input是一个全局对象,当然在C#中就是一个静态类了。语法其实都一样的。
但是在设置速度的分量时你就会犯难:
Velocity.Y = speed;
这行代码会直接报错。当然要解决这个问题,就是直接给它一个新值,Vector2同样用new关键字加构造函数构造。:
Velocity = new Vector2(speed, Velocity.Y);
前面的代码错误信息说的是,因为Velocity等向量类型属性是作为属性暴露出来的,返回的值是一个临时的值,不允许直接修改临时值。这里具体是怎么回事我会在后面的文章中详细讲解。你可能心里已经在骂C#了,但是先别急,后面会说这到底是怎么回事。
别忘了编译
C#和GDScript(至少在直观感受上)不太一样的一点是,C#需要一个明显的编译的过程。这是C#和.NET的设计造成的。你可能听说过编程语言有编译型的和解释型的。编译型的编程语言在运行前必须要编译成某种状态,解释型不需要在运行前编译,而是在运行时处理云云。当然说实话,如果把编译型和解释型作为数轴的两极,很多语言其实是落在靠中间的位置。
C#运行前需要进行编译,但是很多时候不会直接编译成机器码,而是其运行时运行的一种中间代码。修改C#代码之后,你可以直接在你的编辑器中build(这个操作有的翻译成“构建”,有的又翻译成“生成”),也可以点击Godot编辑器右上角那排按钮中的锤子图标(快捷键Alt+B)。如果你直接运行游戏Godot也会自动先构建一遍。看到这个警告不要怕,如果你在代码中修改了一些会影响编辑器中展示的东西的代码,Godot也会提示你要手动构建一遍才会显示出来。
这个编译的过程明显要个几秒钟,但是肯定比你在Unreal中编译C++代码要快很多。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。