编写脚本
简介
在Godot 3.0版本之前,编写游戏脚本的唯一选择是使用 GDScript.如今,Godot已经有四种(没错,是四种!)官方语言,并具有动态添加额外的脚本语言的能力!
这很好,主要在于提供了大量的灵活性,但也使得我们对语言的支持工作变得更加困难.
不过,Godot中的”主要”语言是GDScript和VisualScript.选择它们的主要原因是它们与Godot的整合程度,这使得体验更佳顺畅;两者都有非常好的编辑器集成,而C#和C++需要在外部IDE中进行编辑.如果您是静态语言的忠实粉丝,也可以使用C#和C++.
GDScript
如上所述, GDScript 是Godot使用的主要语言.由于与Godot的高度集成,使用它与其他语言相比有一些积极意义:
简单,优雅,设计上为Lua、Python、Squirrel等语言用户所熟悉.
快速加载和编译.
编辑器集成非常令人愉快,有属于被编辑场景的节点、信号、以及其它条目的代码补全功能.
有内建的矢量类型(比如Vector、Transform等),大量使用线性代数时非常有效.
像静态类型语言一样高效地支持多线程——这是我们避免使用诸如Lua、Squirrel等虚拟机的原因之一.
不使用垃圾回收器,因此它以牺牲一小部分自动化为代价,换取了确定性(大多数对象都是引用计数).
如果需要更高的性能,它的动态特性可以很容易地优化C ++中的代码段(通过GDNative),而无需重新编译引擎.
如果还没决定好,并且对编程有经验,特别是动态类型语言,那就选择GDScript吧!
可视化脚本
从3.0开始,Godot开始支持 可视化脚本.这是”节点和连线”语言的典型实现,但适用于Godot的开发方式.
对非程序员来说,可视化编程是一个很好的工具,甚至对于经验丰富的开发人员来说也是如此,他们想让部分代码易于被他人访问,比如游戏设计师和美术工作者.
程序员也可以用它创建状态机或自定义可视节点工作流程,例如对话系统.
.NET / C
由于微软的C#是游戏开发者的最爱,我们增加了官方支持.C#是一种成熟的语言,人们为它编写了大量代码,而且支持增加得益于微软的慷慨捐赠.
它在性能和易用性之间有很好的折衷,尽管必须注意它的垃圾回收器.
由于Godot使用 Mono .NET运行时,因此理论上任何第三方.NET库或框架都可用于编写Godot脚本,以及任何符合通用语言标准架构的语言,如F#、Boo或ClojureCLR.但实际上,C#是唯一官方支持的.NET选项.
GDNative / C++
最后是3.0版本的一个亮点:GDNative允许使用C++编写脚本,无需重新编译(甚至都不用重启)Godot.
任意C++版本都可以使用,并且由于我们使用内部C API桥接,因此生成的共享库使用的混合编译器品牌和版本可以完美地工作.
这种语言是性能上的最佳选择,它不需要在整个游戏中都使用,因为其他部分可以用GDScript或Visual Script编写.它的API清晰且易于使用,类似于Godot的实际C++ API.
通过GDNative接口可以使用更多语言,但请记住我们没有官方支持.
编写场景脚本
在教程剩下的部分里,我们将建立一个GUI场景,包含一个按钮和一个标签,当按下按钮时会更新标签.这将会演示:
编写脚本并附加到节点上.
通过信号来连接到UI元素.
编写脚本,使其能访问场景中的其它节点.
在继续之前,请确保先略过并标记 GDScript 参考. 这是一种设计得很简单的语言,并且该参考文献各部分,以使您可以更容易地获得概念的概述.
场景设置
如果上一教程的”实例化”项目仍然开着,请将它关闭(“项目”->”退出到项目列表”)并创建一个新项目.
使用从”添加子场景/Add Child Node”选项访问的”添加子节点”对话框(或通过按Ctrl + A键)来创建具有以下节点的层次结构:
面板
标签
按钮
场景树看起来应该是这个样子:
使用2D编辑器定位并调整按钮和标签的大小,使其看起来像下面的样子.可以在 属性检查器(Inspector) 选项卡中设置文本.
最后,保存场景,用例如 sayhello.tscn
这样的名字.
添加脚本
右击面板(Panel)节点, 在弹出菜单中选择 附加脚本(Attach Script):
脚本创建对话框会弹出.这个对话框允许您设置脚本语言、类名和其它相关选项.
在GDScript里,文件本身代表了类,所以类名一栏无法编辑.
我们要附加脚本的节点是个Panel(面板),所以继承一栏会被自动填入``Panel``.这是我们想要的,因为脚本的目的就是扩展这个面板节点的功能.
最后,输入脚本的路径名然后选择新建(Create):
然后将创建脚本并将其添加到节点上.您可以在”场景”选项卡中的节点旁边的”打开脚本”图标,以及”属性检查器”下的脚本属性中看到:
要编辑脚本,请选择这两个按钮中的一个,在上图中两个按钮都被高亮突出显示.这将带您到脚本编辑器,其中将包含默认模板:
这里没有多少内容.``_ready()`` 函数在节点及其所有子节点进入活动场景时被调用.**注意:** _ready()
并不是构造器;构造器是 _init()
.
脚本的作用
脚本为节点添加行为.它用于控制节点的功能以及与其他节点(子节点,父节点,兄弟姐妹等)的交互方式.脚本的局部作用域是节点.换句话说,脚本继承了该节点提供的功能.
处理信号
当某种特定类型的动作发生时,信号被”发出”,并且它们可以连接到任意脚本实例的任意函数.信号主要用于GUI节点,尽管其他节点也有信号,您甚至可以在自己的脚本中定义自定义信号.
在此步骤中,我们将”按下”信号连接到自定义函数.形成连接是第一部分,定义自定义功能是第二部分.对于第一部分,Godot提供了两种创建连接的方式:通过编辑器提供的可视界面、或通过代码.
虽然在本系列教程的其余部分中,我们将使用代码方法,但现在还是让我们介绍一下编辑器界面的工作方式,以备将来参考.
在场景树中选择 Button
节点,然后选择 节点(Node)
选项卡.接下来,确保已选择 信号(Signals)
.
如果稍后在 BaseButton
下选择 pressed()
并单击右下角的 Connect ...
按钮,则将打开连接创建对话框.
对话框的顶部显示了场景的节点列表,其中发射节点的名称会以蓝色突出显示.在这里选择”面板”节点.
对话框的底部显示了将要创建的方法的名称.方法名字默认会包含发出节点的名字(在当前例子中为”Button”),即:”_on_[EmitterNode]_[signal_name]“.
至此,已经介绍了有关如何使用可视界面的指南.但是,这是一个脚本教程,因此,为了学习起见,让我们深入研究手动过程!
为此,我们将介绍一个可能是Godot程序员最常使用的函数: Node.get_node().此函数使用场景中相对于拥有脚本的节点的节点路径来获取节点.
为了方便起见,删除 extends Panel
下面的所有内容.您将手动填写脚本的其余部分.
由于Button和Label是有脚本附加的Panel下的兄弟节点,因此可以通过在 _ready()
函数下输入以下内容来获取Button引用:
GDScript
C#
func _ready():
get_node("Button")
public override void _Ready()
{
GetNode("Button");
}
接下来,编写一个在按下按钮时将被调用的函数:
GDScript
C#
func _on_Button_pressed():
get_node("Label").text = "HELLO!"
public void _OnButtonPressed()
{
GetNode<Label>("Label").Text = "HELLO!";
}
最后,使用 Object.connect() 将按钮的”按下”信号连接到 _ready()
.
GDScript
C#
func _ready():
get_node("Button").connect("pressed", self, "_on_Button_pressed")
public override void _Ready()
{
GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
}
脚本最终看起来是这个样子:
GDScript
C#
extends Panel
func _ready():
get_node("Button").connect("pressed", self, "_on_Button_pressed")
func _on_Button_pressed():
get_node("Label").text = "HELLO!"
using Godot;
// IMPORTANT: the name of the class MUST match the filename exactly.
// this is case sensitive!
public class sayhello : Panel
{
public override void _Ready()
{
GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
}
public void _OnButtonPressed()
{
GetNode<Label>("Label").Text = "HELLO!";
}
}
运行场景并按下按钮,您应该得到以下结果:
您好啊!恭喜您完成了您的第一个场景脚本.
注解
关于本教程的一个常见的误解是 get_node(path)
的工作原理.对于给定的节点,``get_node(path)`` 搜索其直接子节点.在上面的代码中,这意味着Button必须是Panel的子项.如果Button是Label的子项,则获得它的代码是:
GDScript
C#
# Not for this case,
# but just in case.
get_node("Label/Button")
// Not for this case,
// but just in case.
GetNode("Label/Button")
另外,请记住,节点是通过名称而不是类型来引用的.
注解
连接对话框的右侧面板用于将特定值绑定到所连接函数的参数.您可以添加和删除不同类型的值.
代码方法还使用第四个 Array
参数启用此功能,该参数默认为空.随时阅读 Object.connect
方法以获取更多信息.