我希望您已经厌倦了等待本文的第二部分,该部分涉及使用Godot Engine和游戏示例(如《硬币》)来处理使用Godot Engine进行游戏开发的各个方面? 议程上已经准备了许多“美味”和“健康”的东西。 立即预约,在本文中我们将完成先前开始的游戏,您可以在此处开始阅读它的开始- 在Godot Engine上创建游戏“ Like Coins”。 第1部分 ,但是系列文章将继续,因为 有太多的材料使我放弃了其中的一部分,但是我们一定会在稍后再讲。 让“ gamedev”开始!
场景“主要”
在本文的上半部分,我们停在了主要阶段( Main
),从那以后,也许我们将继续。 我们将删除之前添加的所有内容(如果您添加了一些内容来检查所有内容的工作原理),如果没有任何内容加载到场景中,则应添加Node
,它将是下面列出的节点的父级,依次还应该添加到场景中:
ColorRect
(“背景”)-填充背景色;
Player
“ Player”对象(希望您不要困惑,因为我将Player
场景称为对象?);
Node
(“容器”)-用于临时存放硬币的“容器”;
Position2D
(“ PlayerStart”)-在游戏开始时设置“ Player”对象的初始位置;
Timer
(“ GameTimer”)-时间限制计数器;
选择ColorRect
并在工具栏上选择: Layout -> Full Rect
将其拉伸到屏幕的整个区域(以后,我们通常会使用此功能,因此,我建议您自己研究Layout
列表中指定的其他操作),为此节点,在属性“颜色”中指定所需的填充颜色。 您可以使用TextureRect
进行相同的TextureRect
,只是需要填充“ Texture”属性来填充图像,而不是填充图像。 对于Position2D
,在“ position”属性中,指定“ x”和“ y”的值-这将用作Player
的初始位置。 当然,借助脚本,您可以直接在Player
本身中设置定位值,但我们不仅学习开发游戏,而且还学习“ Godot”,因此,考虑解决一个问题的不同选项将是多余的。
“主要”脚本
为Node
添加脚本并打印以下内容:
extends Node
属性“硬币”和“播放时间”将显示在Inspector
。 将场景“ Coin.tscn”拖到“ Coin”属性,并将值“ playtime”设置为“ 40”(游戏时间以秒为单位)。
游戏开始时,每次都应该进行初始化-准备工作,确定应用程序的高质量和无错误操作所需的参数。 这是必不可少的步骤,因此您首先应注意这一点。
func _ready(): randomize()
请注意,当指定Player
对象的名称时,将使用“ $”符号-这是“语法糖”,它使您可以直接访问当前场景中的节点,这是get_node("Node1")
方法的一种很好的替代方法(尽管不禁止使用后者)。 如果“ Node1”的后代是“ Node2”,则也可以使用此方法- $Node1/Node2
。 请注意,自动填充功能在Godot中非常有用,因此请不要忽略它。 不希望在节点名称中使用空格,但仍然有效,在这种情况下,请使用引号- $"My best Node1"
。
新游戏
要开始新游戏,我们将为此确定相应的功能,然后例如可以通过按下按钮来调用该功能。
func new_game(): playing = true
函数“ start()”(其参数为$PlayerStart.position
会将播放器移至起始位置,而函数“ spawn_coins()”负责在运动场上生成硬币。
func spawn_coins(): for i in range(4 + level): var c = Coin.instance() $CoinContainer.add_child(c) c.window_size = window_size c.position = Vector2(rand_range(0, window_size.x), rand_range(0, window_size.y))
range(4 + level)
函数将返回一个具有给定范围的数组,该数组的值等于硬币数量和当前级别的值之和。 一个范围可以包含一个参数,例如在我们的例子中,可以是两个参数或三个参数(第三个参数将是一个数组步骤)。 在此函数中,我们创建“ Coin”对象的几个实例,并将CoinContainer
添加为子元素(希望您不要忘记我们已经可以访问该对象,这要归功于PackedScene
)。 请记住,每次创建新节点的instance()
方法)时,都必须使用add_child()
将其添加到树中。 接下来,我们设置可能产生硬币的区域,以使它们不会意外出现在屏幕后面,然后随机分配位置。 最后一行看起来不太美观,因此我建议通过使用Singleton来简化它。
辛格尔顿
Singleton的中间名称是“启动”。 已经暗示了吧? 我告诉你,一个单例的工作方式如下:我们可以在其中编写所需内容的脚本(从声明变量开始,以场景的“切换”结束,包括加载和卸载它们),然后运行应用程序,并且可以从任何位置访问其所有内容。项目要点。 从某种意义上讲,这是一种在任何给定时间可用的自定义全局存储库。
注意,该项目有其自己的全局存储库,我们也可以使用其内容,您可以使用ProjectSettings.get_setting(name)
来访问它,其中name
是所需参数的名称。
现在,要使用存储库“ _G”中的某些内容,只需按名称调用它,然后指定要调用的方法,或那里的所有内容即可。 因此,创建一个空脚本并在其中编写如下所示的函数:
extends Node func rand(): var rrand = Vector2(rand_range(40, 760), rand_range(40, 540)) return rrand

接下来,保存它并转到项目设置: Project -> Project Settings -> AutoLoad
。 我们选择新创建的脚本,为其设置一个名称,例如“ _G”,然后返回“ spawn_coins()”函数以略微调整期限,将其替换为以下代码:
... c.position = _G.rand()
现在值得通过将“ spawn_coins()”放入“ _ready()”块并在F5上运行应用程序来检查发生了什么。 并且不要忘记选择Main.tscn
作为主场景,如果由于某种原因选择错误,则可以手动更改主场景,为此,您需要转到项目设置: General -> Run -> MainScene
。 它行得通吗? 然后继续前进。
还剩几枚硬币?
让我们继续。 接下来,您需要检查还剩下多少硬币才能将玩家转移到下一个级别,以增加5秒的时间的形式给他一个小的“奖励”,然后重新生成硬币。
func _process(delta):
使用者介面
我们的整个界面将由以下元素组成:得分指示器,当前级别,时间,游戏名称以及将触发游戏启动的按钮。 使用CanvasLayer
父级创建一个场景( HUD.tscn
)(允许您在运动场上方绘制用户界面)。 展望未来,我要说,管理用户界面元素并不是很方便,至少对我来说是这样,但是相当广泛的元素列表和积极的发展在光明的未来激发了人们对引擎这一方面发展的积极情绪。

在Godot中,有所谓的“控制节点”,使您可以根据父级的指定参数自动设置子元素的格式。 每种类型的“控制节点”都有特殊的属性,可控制它们如何控制其后代的位置。 MarginContainer
是这种类型的一个鲜明代表,必须将其添加到场景中。 借助Layout -> Top Wide
在窗口顶部拉伸它,并在此对象的属性的Margin
部分中,从边缘(左,上和右)指定缩进。 MarginContainer
应该具有三个具有以下名称的子Label
: ScoreLabel
, LevelLabel
和TimeLabel
。 将它们添加到场景中。 使用Align
属性,使它们与左,中和右对齐。 仍然需要添加另一个Label
( Messagelabel
),将其放置在中间,所有这些都借助Layout
,将按钮( StartButton
) StartButton
。

现在,让界面具有响应性,我们需要更新时间,收集的硬币数量并突出显示当前水平。 为HUD
节点添加脚本。
extends CanvasLayer signal start_game func update_score(value): $MarginContainer/ScoreLabel.text = str(value) func update_level(value): if len(str(value)) == 1: $MarginContainer/TimeLabel.text = "0: 0" + str(value) else: $MarginContainer/TimeLabel.text = "0: " + str(value) func update_timer(value): $MarginContainer/TimeLabel.txt = str(value)
对于MessageLabel
我们需要一个计时器,以便在短时间内更改消息的文本。 添加一个Timer
节点,并将其名称替换为MessageTimer
。 在检查器中,将等待时间设置为2秒,然后选中“ One Shot
字段中的复选框。 这样可以确保计时器在启动时仅运行一次。
func show_message(text): $MessageLabel.text = text $MessageLabel.show() $MessageTimer.start()
将timeout()
信号连接到“ MessageTimer”并添加以下内容:
func _on_MessageTimer_timeout(): $MessageLabel.hide()
在StartButton
的“节点”选项卡上,连接pressed()
信号。 当您单击StartButton
按钮时StartButton
该按钮应与MessageLabel
一起消失,然后向主场景发送信号,随后我们将通过滑动执行函数“ new_game()”来成功拦截该场景。 我们使用以下代码实现此目的。 不要忘记Text
属性中的按钮来设置任何文字来启动游戏。
func _on_StartButton_pressed(): $StartButton.hide() $MessageLabel.hide() emit_signal("start_game")
为了最终完成界面,我们将编写最后一个final函数-该函数显示有关游戏结束的消息。 在此功能中,我们需要显示“ Game Over”字样不超过两秒钟,然后消失,这要归功于功能“ show_message()”。 但是,一旦通知游戏结束的消息消失,您必须再次显示新游戏的开始按钮。 yield()
暂停该函数的执行,直到收到MessageTimer
的信号,并从MessageTimer
接收到有关其执行的信号为止,该函数将继续执行,使我们返回其原始状态,以便我们可以再次开始新游戏。
func show_game_over(): show_message("Game Over") yield($MessageTimer, "timeout") $StartButton.show() $MessageLabel.text = "LIKE COINS!" $MessageLabel.show()
结局?
让我们在HUD
和Main
之间设置反馈。 将HUD
场景添加到主场景,并通过添加以下内容通过主场景上的timeout()
连接GameTimer
信号:
func _on_GameTimer_timeout(): time_left -= 1
然后连接播放pickup()
和die()
信号。
func _on_Player_pickup(): score += 1 $HUD.update_score(score) func _on_Player_die(): game_over()
在游戏结束时,应该发生更多不可忽视的事情。 编写以下代码,我将进行解释。
func game_over(): playing = false $GameTimer.stop() for coin in $CoinContainer.get_children(): coin.queue_free() $HUD.show_game_over() $Player.die()
此函数将停止游戏,然后show_game_over()
计算剩余的硬币并show_game_over()
剩余的硬币,然后show_game_over()
HUD
调用show_game_over()
。 下一步是启动动画并停止执行Player
节点的过程。
最后,您必须激活StartButton
,该StartButton
必须连接到new_game()
函数。 单击HUD
节点,然后在连接对话框中单击“ Make Function to Off
(这将阻止创建新功能),然后在“ Method In Node
字段中,指定所连接功能的名称new_game
。 这会将信号连接到现有功能,而不是创建一个新功能。
最后new_game()
是从_ready()
函数中删除new_game()
并将以下两行添加到new_game()
函数中:
... $HUD.update_score(score) $HUD.update_timer(time_left)
现在我们可以充满信心地说游戏已经准备就绪,现在已经可以“玩”了,但是没有效果。 我们将在下一篇文章中讨论后者,它将极大地关注各种“装饰”,以使游戏玩法多样化并进一步探索“ Godot”的可能性。 因此,不要忘记关注文章的发布。 祝你好运!