
新手引导---0 从上手开始
首先是下载
打开https://godotengine.org/ ,这是godot的官网。右上角有- Download
进入后往下滑一段距离,选择你使用的操作系统,选好后向上滑,选择对应架构与类型并进行下载。由于网络原因,有时候需要等一段时间,才会有下载窗口弹出。
如果对这类操作比较熟悉,也可以自行前往github开源社区https://github.com/godotengine/godot 下载源码进行编译,并不一定需要使用安装包进行安装。
项目创建
完成安装之后,打开应用。弹出的窗口上半部分大概是这样的布局。
“创建”、“导入”、“扫描”按钮分别为创建项目,导入项目,以及扫描文件夹中的可用项目。
点击“创建”按钮创建项目,项目名称对语言不敏感,可以使用多种文字命名,但依然建议命名标准化、规范化。同时,需要注意项目保存路径的选取,避免管理混乱。可以点击“浏览”按钮,浏览目标文件夹,查看是否有名称重复或者名称易于混淆的项目。
默认情况,创建项目时会同时创建文件夹,可以通过点击“创建文件夹”右边的开关按钮,设置是否同时创建文件夹。
根据实际需要选择渲染器,“兼容”选项3D渲染效果或许并不理想,但兼容性最好。
节点介绍
成功创建项目之后,会弹出编辑器,默认的编辑脚本是gdscript。一般需要先创建根节点,作为项目运行时默认第一个打开的场景节点。
2D场景节点及其子节点
点击“创建根节点”下的“2D场景”按钮,创建2D场景节点。点击“Node2D”后按F2,或双击“Node2D”可重命名该节点。
下方的“文件系统”中的“node_2d.tscn”就是该场景文件实例。
“res://” 代表该项目的项目文件夹,只不过名称表示为“res://” 。可以右键 - “新建”,创建文件夹用以存放文件。也可以根据实际需要选择“新建”中的其他内容进行创建。
编辑器右侧是控制面板,点击选中“Node2D”节点,则会在“检查器”显示“Node2D”的属性。如果没有选中的节点,则检查器不显示任何属性。
选中节点按下"ctrl+a"快捷键,则弹出创建子节的面板。双击,或选中要创建的节点后按下“确定”按钮则创建子节点。
按下“创建”,则在选中的“Node2D”节点下创建了一个“Node”节点。
同理可以创建其他节点。
godot引擎虽然常被认为是游戏制作引擎,但由于它的结构设计,用于软件制作也有不错的效果。但就如它怎么来,我还是希望先介绍它的游戏制作功能。而我对godot项目的历史并不熟悉,因此本篇不会叙述。
“Node”节点的衍生节点
在godot中所有节点都由Node节点衍生。而节点又被称为“类”,可以在脚本中由关键字class定义。
在创建node面板,可以看到“Node”节点的子类(继承类)。
1.“Viewport”是所有“视口”的抽象基类,“视口”包括耳熟能详的“窗口”以及场景中的某个矩形区域。并不是所有视口都能直接渲染在屏幕上。
2.“CanvaItem”是所有2D对象的抽象基类,包括先前展示的“Node2D”节点及其衍生节点,此外还包括“Control”节点及其衍生节点。
3.“Node3D”节点,顾名思义,其实就是3D节点的基类。
4.“AnimationMixer”节点:“AnimationPlayer” 和 “AnimationTree” 的基类。是指导帧渲染过程的动画节点,通常使用其衍生节点“AnimationPlayer”作为动画节点控制2D角色的动作动画。
5.“AudioStreamPlayer”节点是用于播放音频的节点。
6.“CanvasLayer”是用于2D场景中的对象的独立渲染的节点,作用是将2D节点固定渲染在“视口”上,即使节点的公共位置发生变化,节点的图片纹理也不一定跟着移动。由于它固定渲染在“视口”上,通常作为游戏中的血条,蓝条等对象的父节点。
7.“Timer”是倒数计时器节点,用于倒数计时,但精确度不是很高。
以上7个节点是较为常用的基础节点。其中“CanvaItem”节点更是常用中的常用,也是本文要具体介绍的部分。
现在可以小小地施展拳脚了
双击将原先的“Node2D”节点命名为“游戏场景”,按下“ctrl + a”,在创建节点面板中选择“CanvasLayer”的衍生节点“CharacterBody2D”。
这是内置的2D角色通用节点,它左边的黄色感叹号标记是警告提示标志。在当前情况下,代表该角色通用节点没有配置碰撞形状,需要给它添加一个形状,否则无法与其他物体计算碰撞。
就是它。
但是还需要一些工作。这是代表碰撞形状的节点,但是还需要手动配置它的形状。
选中“CollisionShape2D”节点,在右侧的属性面板中的“Shape”属性可以编辑它的形状。
如果你编辑器的视口没有跑开,就可以看到编辑器中生成了一个矩形,并可以直接通过拖放操作改变它的形状大小。
需要注意,改变形状大小的时候,形状节点的位置可能也会发生改变。
如果将“Position”的“x”和“y”还原为0,“CharacterBody2D”与“CollisionShape2D”的中心就可以恢复对齐,因为“CharacterBody2D”是“CollisionShape2D”的上一级父节点。
关于角色的添加,更好的用法是将角色作为场景添加。在原先的场景标签旁点击“+”按钮就可以创建新场景。
可以点击“其他节点”,选中“CharacterBody2D”节点创建。
也可以通过更改节点类型创建。以及一些其他的方式。
通过“最近使用”快速修改节点。
按“ctrl+s”快捷键保存。
建议先在“res://” 目录下创建“角色”文件夹或“character”文件夹,并将该角色场景保存在创建的文件夹中。
双击文件夹图标进入“角色”文件夹,并点击保存。可以根据实际需要修改文件名称。
之后就可以生成场景文件。
场景文件的使用方法有很多,点击场景标签,返回开始的场景后,可以直接拖拽场景文件进入场景。
也可以拖到场景节点列表中,拖到哪个节点,那个节点就是它的父节点。可以通过“ctrl+z”撤销操作,“ctrl+y”还原操作。
可以点击场景标签或双击场景文件,返回角色场景节点,在这里可以修改节点名称。修改好后记得“ctrl+s”保存。
再拖拽放置场景文件,效果如下。
通过以上操作,我们基本了解了节点创建。这时候我们需要游戏动起来。好,右上角有个小工具,可以点击按钮或使用快捷键(F5)运行整个项目或(F6)运行当前场景。如果要运行整个项目,需要选择一个场景作为主场景。
如果点击“选择当前”,就会选择当前所在的“node_2d”场景为主场景。如果点击“选择”则会在项目中选择场景。可以根据文件位置自行选择。如果卡住了无法选取,可以关闭窗口后再一次运行整个项目或选择其他场景作为主场景。
如果成功了就会跳出一个调试窗口,这个窗口中当然什么都没有。
在解释以上问题之前,先介绍修改主场景的方式。可以选择未设为主场景的文件,单击右键,选择“设为主场景”。
也可以在“项目” - “项目设置” - “应用” - “运行”中设置。
现在给“悟空”添加图片,双击“悟空.tscn”场景文件或点击“悟空”场景标签进入场景。创建项目时,文件系统中默认会带有一个小老弟的图片。
可以将图片拖到场景中,完成后场景列表会自动创建一个精灵节点。
这时候按F6运行当前场景,会自动保存,并弹出调试窗口。窗口中的小老弟露出了一个角。
关闭后返回“node_2d”场景,可以看到场景中的图像也发生了改变,因为原本的“悟空.tscn”已经改变并且被保存。
(如果主场景是“node_2d”场景)按F5运行整个项目,或在“node_2d”场景下按F6运行,都可以弹出“node_2d”场景的调试窗口。
我这里添加“悟空”场景作为一些操作演示。图中虽然添加了“悟空”但是只显示两个图片,因为图片发生重叠。
在场景标签下方有一些功能按钮,可以对节点的形状样式进行修改。
选择悟空之后在“选择”模式(按键盘Q键)下,长按“ctrl”可以同时长按鼠标左键拖拽旋转。其他操作可以根据上图提示进行尝试。
选择悟空之后按键盘W键切换到“移动”模式,只能移动节点位置;按E键只能旋转节点;按S键只能缩小放大拉伸节点。
这些功能不做多赘述,也都可以通过“ctrl+z”撤销,“ctrl+y”还原。
滚动鼠标滚轮,将视线拉远可以看到有一条线框,这就是当前“视口”的范围。调整角色位置后进行调试,可以看到变化。
按F8关闭调试,视口大小可以“项目” - “项目设置” - “显示” - “窗口” - “大小”中对“视口宽度”和“视口高度”进行设置。
也可以通过给“游戏场景”添加“Camera2D”节点进行控制。按搜索栏右边的“X”按钮清空内容,搜索Camera2D(不区分大小写),或在节点列表中找。
添加好后,可以看到场景中多出了一个框,这时进行调试,调试窗口的内容会发生变化。
关于视口以及其他许多节点的复杂变化,仅通过节点属性检查器设置无法实现,需要书写代码进行脚本编辑,并且想要让角色动起来,也需要脚本实现。
前面介绍了,选中节点,按下“ctrl+a”可以为选中的节点添加子节点。此外,选中节点,可以通过添加脚本按钮为该节点添加脚本。
确定脚本名称,如果需要修改脚本路径,可以点击“路径”输入框右侧的文件夹按钮。
脚本创建完成后,选中的节点会增加标记,而选中有标记的节点时,添加脚本按钮会变为分离脚本按钮。
这时候可以进行脚本编辑。“node_2d.gd”文件就是脚本文件。extends关键字声明对应的节点类型,“extends Node2D”表示只有“ Node2D”类型的节点可以使用该脚本。
如果不知道选中的节点是什么类型的节点,可以鼠标右键“打开文档”查看。
选中文档后快捷键“ctrl+w”关闭打开的文档,也可以是使用鼠标右键“关闭”或“关闭文档”。
代码解读:
#这是内置的初始化函数,表示节点初始化完成后执行的内容。
func _ready() -> void:
#冒号之后下一行需要缩进。
pass
#这是内置的帧函数,表示每帧都执行的内容。帧可以理解为特定的时间间隔。
func _process(delta: float) -> void:
pass
#“func ”表示声明某个函数。
#“允许使用的文本” + “()”表示函数名称,如_ready()。
#“ -> void”表示不能使用return关键字。函数如果需要使用return关键字则不能使用“ -> void”。但是不建议修改内置的_ready()函数声明方式。
# “:”表示声明完成。
#需要注意:声明函数后,如果暂时不写入内容,应在函数下方补充“pass”关键字,并且注意段落缩进格式。
#gdscript的代码读取按照从上到下进行,默认使用键盘“tab”键作为段落缩进。
缩进方式可以通过点击“编辑器” - “编辑器设置” - “文本编辑器” - “行为” - “缩进”进行修改,将类型修改为spaces,也就是键盘空格键。
也可以不修改。
这里简单写一个移动的例子。如果书写出现了障碍,可以尝试将输入法切换到英文输入,并检查逗号与冒号是否是英文格式,如果不是,需改为英文格式。
#每一帧悟空都向上移动
func _process(delta: float) -> void:
#使用“向上”函数。
向上()
pass
#声明一个让“悟空”向上移动的函数
func 向上():
#godot的坐标系x:左为-,右为+。y:上为-,下为+。
$"悟空".global_position.y -= 1
#global_position为公共位置。position为相对于“悟空”的父节点“游戏场景”的位置。
#此处使用position效果是一样的。
pass
“_process”函数代表内置的每帧都会运行的函数,与文中自定义的“向上”函数不同。因此可以将它看作是一个函数的启动器,每一帧都会运行段落中的代码。上述例子代表每一帧运行一次“向上”函数。
上方代码区中,$"悟空"代表“悟空”节点,可以鼠标长按拖拽到代码区进行使用,也可以对其进行唯一标识声明或是函数化声明。
鼠标右键 - “作为唯一名称访问”将其声明为唯一名称,场景内其他节点及其子节点不能使用该名称。
函数化声明有多种,比较常见的是“var”关键字声明和“@onready var”声明。两者区别在于“var”表示直接声明,“@onready var”表示节点就绪后声明。
函数内允许“var”声明的原因是,在非全局脚本中,函数只能在节点就绪后调用。而不在全局脚本列表中的脚本都是非全局脚本。
我们可以看看什么是全局脚本。点击“项目” - “项目设置” - “全局” - “自动加载”。
有多添加方式,可以先创建一个脚本文件,将其作为全局脚本。也可以在项目设置面板新创建一个脚本文件,并将其设为全局脚本。但全局脚本只能使用字母或下划线开头,之后包括字母、数字、下划线在内的文本作为名称。
如果未启用,记得启用。注意它的名称。
现在可以使用这个“aa”了。打开aa.gd文件,在aa脚本中声明一个类型为string名称为aa的全局变量,并将“aa”赋值给aa变量。
“ctrl+s”保存后,回到“node_2d.gd”,我们就可以调用“aa.gd”中的变量,并在_ready()函数中使用“print()”函数将它的内容打印出来。
_ready()函数是一个自动运行的内置函数,表示节点加载完成后就运行函数里的代码。如果书写出现了障碍,可以尝试将输入法切换到英文输入,并检查逗号与冒号是否是英文格式,如果不是,需改为英文格式。
运行后将输出“aa”。
for i in range(100):
#冒号之后下一行需要缩进。
print(Aa.aa)
#“for i in range(100)”表示使用“range()”函数创建一个序列,并使用临时变量i进行遍历。
#每次遍历执行一次“print(Aa.aa)”,在输出框打印Aa.aa的内容,也就是打印“aa”。
#如果“print(Aa.aa)”改为“print(i)”,则依次输出0-99,一共100个数字。
#在gdscript中,“#”后面的内容表示注释,不计入代码,可以不缩进。
#“range(100)”表示创建一个0-99的长度为100的序列(0-100不包括100的序列)。
#具体的用法可以鼠标右键点击“range”关键字,“查找符号”查看详细用法。
现在我们知道了使用“for”语句快速连续执行代码,但是要实现游戏操作,我们需要连接设备输入控制。godot内置_input()方法可以对设备输入信号进行接收,使用的方法与_ready()函数以及_process()函数类似,都是自动运行的函数。
#举个例子,每次点击鼠标左键,执行一次向上函数。但长按左键并不能连续激发“向上”函数。
func _input(event: InputEvent) -> void:
#如果事件是鼠标按钮。
if event is InputEventMouseButton:
#如果事件为按下。
if event.is_pressed():
#“==1”表示鼠标左键,2表示鼠标右键。
if event.button_index == 1:
向上()
pass
pass
func 向上():
%"悟空".position.y -= 1
pass
如果想使用键盘控制,则可以使用以下代码。
#长按键盘W键可以连续激发“向上”函数,但开始会有一小段停顿。
func _input(event: InputEvent) -> void:
#如果事件是键盘按键。
if event is InputEventKey:
#如果事件为按下。
if event.is_pressed():
#如果按下的键为W键。
if event.keycode == KEY_W:
向上()
pass
pass
func 向上():
%"悟空".position.y -= 1
pass
也有其他的实现方法,比如封装到移动函数,再在_process()函数中调用。不过前提是需要设置输入事件名称,点击“项目” - “项目设置” - “输入映射”。
以上就完成了输入事件“左”的创建,同理可以创建右、上、下或者其他事件。
func _process(delta: float) -> void:
移动()
pass
func 移动():
if Input.is_action_pressed("左"):
%"悟空".position.x -= 1
if Input.is_action_pressed("右"):
%"悟空".position.x += 1
if Input.is_action_pressed("上"):
%"悟空".position.y -= 1
if Input.is_action_pressed("下"):
%"悟空".position.y += 1
运行效果如下:
如果想制作一个平台跳跃类游戏,“CharacterBody2D”自身有封装好的移动方法。在许多版本中,只需要对“悟空”添加脚本就可以生成移动方法,因为例子中的“悟空”是“CharacterBody2D”节点类。
但是建议将原先的移动函数从_ready(), _process(), _input()等内置函数中删除或注释,例子如下。
func _ready() ->void:
pass
func _process() ->void:
#在“移动()”前面加“#”将代码注释
#移动()
pass
func _input()
#如果事件是鼠标按钮。
if event is InputEventMouseButton:
#如果事件为按下。
if event.is_pressed():
#“==1”表示鼠标左键,2表示鼠标右键。
if event.button_index == 1:
#在“向上()”前面加“#”将代码注释
#向上()
pass
#如果事件是键盘按键。
if event is InputEventKey:
#如果事件为按下。
if event.is_pressed():
#如果按下的键为W键。
if event.keycode == KEY_W:
#在“向上()”前面加“#”将代码注释
#向上()
pass
func 移动():
if Input.is_action_pressed("左"):
%"悟空".position.x -= 1
if Input.is_action_pressed("右"):
%"悟空".position.x += 1
if Input.is_action_pressed("上"):
%"悟空".position.y -= 1
if Input.is_action_pressed("下"):
%"悟空".position.y += 1
func 向上():
%"悟空".position.y -= 1
pass
使用内置的运动控制运行,可以看到角色会向下掉落,因为项目中没有放置墙面或地面。
鼠标点击选择“游戏场景”,“ctrl+a”创建子节点,添加阻挡物作为墙面或地面。
完成后运行结果如下。因为场景内添加的3个角色都继承于同一个场景对象,所以移动控制对3个角色都有效。如果在_process()中没有将“移动()”注释,则可以实现后面的单独移动。因为“移动()”指向的是“悟空”,而3个角色中只有一个角色的名称为“悟空”。
因为“移动()”指向的是“悟空”,而3个角色中只有一个角色的名称为“悟空”。
内置的运动控制为:键盘方向键左右控制左右移动,空格键控制跳跃。
extends CharacterBody2D
#设置速度,const关键字声明为常量,无法改变,可以改为var关键字声明为变量。
# var SPEED = 300.0
const SPEED = 300.0
#跳跃初速度
const JUMP_VELOCITY = -400.0
func _physics_process(delta: float) -> void:
#如果不触地
if not is_on_floor():
velocity += get_gravity() * delta
#跳跃
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
#控制左右移动
var direction := Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
#根据向量“velocity”进行移动。
move_and_slide()
接下来给地面添加图片,再将镜头交给主角“悟空”,游戏的最基本元素就成型了。
运行效果如下:
可以看到有很多瑕疵,并且项目中没有设置游戏关卡。关于图片穿过地面的问题,是因为角色的碰撞形状大小与图片形状大小不一致。
调整后可以这样:
运行后效果如下:
最后,资源的添加可以通过系统的文件管理器添加。如果是电脑端,可以在弹出的窗口中自行创建文件夹,添加图片、音频、动画等。
在添加资源的同时,需要注意做好分类,这对于任何项目都十分有益处。
完成以上练习之后,可以进入关卡设计部分。如果想要完整做出一个项目,需要熟悉godot的许多节点以及内置方法,培养程序思维以及项目整体思维。本文到这里结束。