第二章-Coin Dash
Coin Dash
- Project setup
- 向量和2D坐標(biāo)系
- Vectors
- Pixel rendering像素渲染
- 第一部分–玩家場景
- 創(chuàng)建場景
- 精靈動畫
- 碰撞形狀
- 編寫Player腳本
- 移動Player
- 關(guān)于delta
- 選擇動畫
- 開始和結(jié)束玩家的移動
- 準(zhǔn)備碰撞
- 第2部分-硬幣場景
- 節(jié)點(diǎn)設(shè)置
- 使用組
- 腳本
- 第三部分–主場景
- 節(jié)點(diǎn)設(shè)置
- 主腳本
- 初始化
- 開始新游戲
- 檢查剩余硬幣
- 第4部分–用戶界面
- 節(jié)點(diǎn)設(shè)置
- 錨和邊距
- 信息標(biāo)簽
- 分?jǐn)?shù)和時(shí)間顯示
- 容器
- 通過GDScript更新UI
- 使用按鈕
- 游戲結(jié)束
- 將HUD添加到Main
- 第5部分-完成
- 視覺效果
- 什么是補(bǔ)間?
- 聲音
- 提升能力
- 硬幣動畫
- 障礙物
- 總結(jié)
這第一個(gè)項(xiàng)目將指導(dǎo)你制作你的第一個(gè)Godot引擎項(xiàng)目。你將學(xué)習(xí)Godot編輯器如何工作,如何構(gòu)建一個(gè)項(xiàng)目,以及如何構(gòu)建一個(gè)小型2D游戲。
重要提示-即使您不是游戲開發(fā)的新手,也不要跳過本章。 盡管您可能已經(jīng)了解了許多基本概念,但本項(xiàng)目將介紹您需要了解的一些基本Godot功能和設(shè)計(jì)范例。 在開發(fā)更復(fù)雜的項(xiàng)目時(shí),將基于這些概念。
本章中的游戲稱為Coin Dash。 您的角色必須在屏幕上四處移動,在爭分奪秒的比賽中收集盡可能多的硬幣。 完成后,游戲?qū)⑷缦滤?#xff1a;
Project setup
啟動Godot并創(chuàng)建一個(gè)新項(xiàng)目,確保使用“創(chuàng)建文件夾”按鈕以確保該項(xiàng)目的文件與其他項(xiàng)目分開保存。 您可以在這里https://github.com/PacktPublishing/Godot-Game-Engine-Projects/releases下載有關(guān)游戲的藝術(shù)和聲音的Zip文件(統(tǒng)稱為資產(chǎn))。
將此文件解壓到你的新項(xiàng)目文件夾中。
在這個(gè)項(xiàng)目中,您將制作三個(gè)獨(dú)立的場景:Player,Coin和HUD,它們都將合并到游戲的Main場景中。 在較大的項(xiàng)目中,創(chuàng)建單獨(dú)的文件夾來保存每個(gè)場景的資產(chǎn)和腳本可能很有用,但是對于這個(gè)相對較小的游戲,您可以將場景和腳本保存在根文件夾中,該根文件夾稱為res://( res是資源的縮寫)。 項(xiàng)目中的所有資源都將相對于res://文件夾放置。 您可以在左上角的FileSystem停靠欄中看到您的項(xiàng)目文件夾:
例如,硬幣的圖片將位于res://assets/coin/。
這個(gè)游戲?qū)⑹褂秘Q屏模式,所以你需要調(diào)整游戲窗口的大小。點(diǎn)擊項(xiàng)目菜單,選擇項(xiàng)目設(shè)置,如下圖所示:
查找“顯示/窗口”部分,并將“寬度”設(shè)置為480,將“高度”設(shè)置為720。同樣在此部分,將“拉伸/模式”設(shè)置為2D,將“長寬比”保持不變。 這將確保,如果用戶調(diào)整游戲窗口的大小,則所有內(nèi)容都將適當(dāng)縮放,并且不會拉伸或變形。 如果愿意,還可以取消選中“可調(diào)整大小”復(fù)選框,以防止完全調(diào)整窗口的大小。
向量和2D坐標(biāo)系
注意:本節(jié)是2D坐標(biāo)系的非常簡短的概述,并且不會非常深入地研究矢量數(shù)學(xué)。 本文旨在對這些主題如何應(yīng)用于Godot中的游戲開發(fā)做一個(gè)高層次的概述。向量數(shù)學(xué)是游戲開發(fā)中必不可少的工具,因此,如果您需要對該主題有更廣泛的了解,請參閱可汗學(xué)院的線性代數(shù)系列(https://www.khanacademy.org/math/linear-algebra)。
在2D模式下工作時(shí),您將使用直角坐標(biāo)來標(biāo)識空間中的位置。 2D空間中的特定位置被寫為一對值,例如(4,3),分別代表沿x和y軸的位置。 以此方式可以描述2D平面中的任何位置。
在2D空間中,Godot遵循將x軸指向右側(cè),將y軸指向下方的通用計(jì)算機(jī)圖形慣例:
如果你是計(jì)算機(jī)圖形學(xué)或游戲開發(fā)的新手,正y軸指向向下而不是向上,這可能看起來很奇怪,因?yàn)槟憧赡茉跀?shù)學(xué)課上學(xué)到的不一樣。然而,這種方向在計(jì)算機(jī)圖形應(yīng)用中非常常見。
Vectors
您也可以將位置(4,3)視為與(0,0)點(diǎn)或原點(diǎn)的偏移量。 想象一下從原點(diǎn)指向該點(diǎn)的箭頭:
此箭頭是向量。 它代表了大量有用的信息,包括該點(diǎn)的位置(4,3),其長度m和其與x軸的夾角θ。 總之,這是一個(gè)位置向量,換句話說,它描述了空間中的位置。 向量還可以表示運(yùn)動,加速度或具有x和y分量的任何其他量。
在Godot中,向量(用于2D的Vector2或用于3D的Vector3)被廣泛使用,并且在本書的項(xiàng)目構(gòu)建過程中將使用它們。
Pixel rendering像素渲染
Godot中的向量坐標(biāo)是浮點(diǎn)數(shù),而不是整數(shù)。這意味著一個(gè)Vector2可以有一個(gè)小數(shù)值,比如(1.5,1.5)。由于對象不能以半數(shù)像素繪制,這對于像素藝術(shù)游戲來說,可能會造成視覺上的問題,因?yàn)槟阋_保紋理的所有像素都被繪制出來。
為了解決這個(gè)問題,請打開項(xiàng)目|項(xiàng)目設(shè)置,并在下面找到渲染/質(zhì)量部分。
側(cè)邊欄并啟用 “使用像素捕捉”,如下截圖所示。
如果你在游戲中使用2D像素藝術(shù),最好在開始項(xiàng)目時(shí)就啟用這個(gè)設(shè)置。這個(gè)設(shè)置在3D游戲中沒有效果。
第一部分–玩家場景
你要做的第一個(gè)場景定義了玩家對象。創(chuàng)建一個(gè)單獨(dú)的玩家場景的好處之一是,你可以獨(dú)立地測試它,甚至在你創(chuàng)建游戲的其他部分之前。隨著你的項(xiàng)目規(guī)模和復(fù)雜度的增長,這種游戲?qū)ο蟮姆蛛x將變得越來越有用。保持單個(gè)游戲?qū)ο笙嗷シ蛛x,使它們更容易排除故障,修改,甚至完全替換而不影響游戲的其他部分。這也使得你的玩家可以重復(fù)使用–你可以把玩家場景放到一個(gè)完全不同的游戲中去,而且它的工作原理是一樣的。
玩家場景將顯示你的角色及其動畫,響應(yīng)用戶的輸入,相應(yīng)地移動角色,并檢測與游戲中其他物體的碰撞。
創(chuàng)建場景
首先點(diǎn)擊 "添加/創(chuàng)建新節(jié)點(diǎn) "按鈕,選擇一個(gè)Area2D。然后,點(diǎn)擊它的名字,并將其改為Player。點(diǎn)擊Scene | Save Scene來保存場景。這是場景的根節(jié)點(diǎn)或頂層節(jié)點(diǎn)。您將通過向這個(gè)節(jié)點(diǎn)添加子節(jié)點(diǎn)來為Player添加更多的功能:
在添加任何子代之前,最好確保你不會因?yàn)辄c(diǎn)擊它們而意外地移動或調(diào)整它們的大小。選擇Player節(jié)點(diǎn),點(diǎn)擊鎖旁邊的圖標(biāo):
工具提示會說,確保對象的子對象是不可選擇的,如上面的屏幕截圖所示。
在創(chuàng)建一個(gè)新場景時(shí),總是這樣做是個(gè)好主意。如果一個(gè)物體的碰撞形狀或精靈被偏移或縮放,它會導(dǎo)致意想不到的錯(cuò)誤并且難以修復(fù)。使用此選項(xiàng),節(jié)點(diǎn)及其所有子節(jié)點(diǎn)將始終一起移動。
精靈動畫
使用Area2D,你可以檢測到其他物體重疊或撞到玩家,但是Area2D自己沒有一個(gè)外觀,所以點(diǎn)擊玩家節(jié)點(diǎn)并添加一個(gè)AnimatedSprite(動畫精靈)節(jié)點(diǎn)作為子節(jié)點(diǎn)。動畫精靈將為你的player處理外觀和動畫。注意,節(jié)點(diǎn)旁邊有一個(gè)警告符號。一個(gè)動畫精靈需要一個(gè)SpriteFrames資源,它包含了它可以顯示的動畫。要?jiǎng)?chuàng)建一個(gè),在檢查器中找到frame屬性,然后點(diǎn)擊 | New SpriteFrames:
接下來,在同樣的位置,點(diǎn)擊,打開SpriteFrames面板:
左邊是動畫的列表。點(diǎn)擊默認(rèn)的一個(gè),并將其重命名為run。然后,單擊 "添加 "按鈕,創(chuàng)建第二個(gè)名為idle的動畫和第三個(gè)名為hurt的動畫。
在左側(cè)的FileSystem停靠欄,找到run, idle,和hurt的玩家圖像,并將其拖入相應(yīng)的動畫中:
每個(gè)動畫的默認(rèn)速度設(shè)置為每秒5幀。這有點(diǎn)太慢了,所以點(diǎn)擊每個(gè)動畫,將速度(FPS)設(shè)置為8。在 "檢查器 "中,勾選 "播放 "屬性旁邊的 “開啟”,然后選擇一個(gè)動畫來查看動畫的運(yùn)行情況:
稍后,你將根據(jù)玩家正在做的事情,編寫代碼在這些動畫之間進(jìn)行選擇。但首先,你需要完成對玩家節(jié)點(diǎn)的設(shè)置。
碰撞形狀
當(dāng)使用Area2D或Godot中的其他碰撞對象之一時(shí),它需要具有定義的形狀,否則就無法檢測到碰撞。 碰撞形狀定義了對象占據(jù)的區(qū)域,并用于檢測重疊和/或碰撞。 形狀由Shape2D定義,包括矩形,圓形,多邊形和其他類型的形狀。
為了方便起見,當(dāng)您需要向區(qū)域或物理物體添加形狀時(shí),可以將CollisionShape2D作為子級添加。 然后,選擇所需的形狀類型,然后可以在編輯器中編輯其大小。
添加一個(gè)CollisionShape2D作為Player的子級(確保你沒有把它添加為AnimatedSprite的子級)。這將允許你確定玩家的hitbox,或其碰撞區(qū)域的邊界。在 "檢查器 "中,在 "形狀 "旁邊,點(diǎn)擊,然后選擇 “New RectangleShape2D”。調(diào)整形狀的大小以覆蓋精靈:
注意不要縮放形狀的輪廓! 僅使用尺寸手柄(紅色)來調(diào)整形狀! 碰撞在縮放的碰撞形狀下無法正常工作。
你可能已經(jīng)注意到,碰撞形狀并沒有在精靈上居中。這是因?yàn)榫`本身沒有垂直居中。我們可以通過給AnimatedSprite添加一個(gè)小的偏移來解決這個(gè)問題。點(diǎn)擊節(jié)點(diǎn),在檢查器中尋找Offset屬性。將其設(shè)置為(0,-5)。
當(dāng)你完成后,你的Player場景應(yīng)該是這樣的:
編寫Player腳本
現(xiàn)在,你已經(jīng)準(zhǔn)備好添加一個(gè)腳本了。腳本允許你添加內(nèi)置節(jié)點(diǎn)沒有提供的額外功能。點(diǎn)擊Player節(jié)點(diǎn),然后點(diǎn)擊添加腳本按鈕:
在 "腳本設(shè)置 "窗口中,你可以保持默認(rèn)設(shè)置不變。如果你還記得保存場景(見前面的截圖),腳本將自動被命名為與場景的名稱相匹配。點(diǎn)擊創(chuàng)建,你會被帶到腳本窗口。你的腳本將包含一些默認(rèn)的注釋和提示。你可以刪除注釋(以#開頭的行)。請參考以下代碼片段。
extends Area2D# class member variables go here, for example: # var a = 2 # var b = "textvar"func _ready():# Called every time the node is added to the scene. # Initialization herepass#func _process(delta):# # Called every frame. Delta is time since last frame. # # Update game logic here.# pass每個(gè)腳本的第一行將描述該腳本附加到的節(jié)點(diǎn)類型。 接下來,您將定義類變量:
extends Area2Dexport (int) var speed var velocity = Vector2() var screensize = Vector2(480, 720)在speed變量上使用export關(guān)鍵字可以使您在Inspector中設(shè)置其值,并讓Inspector知道該變量應(yīng)包含的數(shù)據(jù)類型。 這對于您想要能夠調(diào)整的值非常方便,就像調(diào)整節(jié)點(diǎn)的內(nèi)置屬性一樣。 單擊Player節(jié)點(diǎn),并將Speed屬性設(shè)置為350,如以下屏幕截圖所示:
velocity將包含角色當(dāng)前的移動速度和方向,screensize將用于設(shè)置玩家的移動限制。稍后,游戲的主場景將設(shè)置這個(gè)變量,但現(xiàn)在你將手動設(shè)置它,以便你可以測試。
移動Player
接下來,你將使用_process()函數(shù)來定義player將做什么。_process()函數(shù)在每一幀都會被調(diào)用,所以你會用它來更新你的游戲中那些你期望經(jīng)常變化的元素。你需要玩家做三件事。
- 檢查鍵盤輸入
- 向指定方向移動
- 播放相應(yīng)的動畫
首先,您需要檢查輸入。 對于此游戲,您需要檢查四個(gè)方向輸入(四個(gè)箭頭鍵)。 輸入動作在“輸入映射”選項(xiàng)卡下的項(xiàng)目設(shè)置中定義。 在此選項(xiàng)卡中,您可以定義自定義事件并為它們分配不同的鍵,鼠標(biāo)操作或其他輸入。 默認(rèn)情況下,Godot將事件分配給鍵盤箭頭,因此您可以將其用于此項(xiàng)目。
您可以使用Input.is_action_pressed()檢測是否按下了輸入,如果按住該鍵,則返回true;否則,返回false。 組合所有四個(gè)按鈕的狀態(tài)將為您提供最終的移動方向。 例如,如果您同時(shí)按住向右和向下,則所得的速度矢量將為(1,1)。 在這種情況下,由于我們同時(shí)添加了水平和垂直移動,因此player的移動速度要比水平移動的速度快。
你可以通過將速度歸一化來防止這種情況發(fā)生,也就是將它的長度設(shè)為1,然后乘以所需的速度:
func get_input(): velocity = Vector2()if Input.is_action_pressed("ui_left"): velocity.x -= 1if Input.is_action_pressed("ui_right"): velocity.x += 1if Input.is_action_pressed("ui_up"): velocity.y -= 1if Input.is_action_pressed("ui_down"): velocity.y += 1if velocity.length() > 0:velocity = velocity.normalized() * speed通過在get_input()函數(shù)中將所有這些代碼分組在一起,可以使以后更輕松地進(jìn)行更改。 例如,您可以決定更改為模擬操縱桿或其他類型的控制器。 從_process()調(diào)用此函數(shù),然后通過生成的速度更改玩家的位置。 為了防止player離開屏幕,可以使用clip()函數(shù)將位置限制為最小值和最大值:
func _process(delta): get_input()position += velocity * deltaposition.x = clamp(position.x, 0, screensize.x) position.y = clamp(position.y, 0, screensize.y)單擊“播放編輯的場景”(F6),并確認(rèn)您可以沿屏幕的所有方向移動player。
關(guān)于delta
_process()函數(shù)包含一個(gè)名為delta的參數(shù),然后乘以速度。什么是delta?
游戲引擎嘗試以一致的每秒60幀的速度運(yùn)行。然而,這可能會由于計(jì)算機(jī)速度變慢而改變,無論是在Godot還是從計(jì)算機(jī)本身。如果幀速率不一致,那么它將影響游戲?qū)ο蟮囊苿印@?#xff0c;假設(shè)一個(gè)對象每幀移動10個(gè)像素。如果一切運(yùn)行順利,這將轉(zhuǎn)化為在一秒內(nèi)移動600像素。但是,如果其中一些幀需要更長的時(shí)間,那么在那一秒中可能只有50幀,所以對象只移動了500像素。
Godot,像大多數(shù)游戲引擎和框架一樣,通過傳遞給你delta來解決這個(gè)問題,delta是指自上一幀以來的經(jīng)過時(shí)間。大多數(shù)情況下,這將是0.016秒左右(或16毫秒左右)。如果你把你想要的速度(600 px/s)乘以delta,你將得到一個(gè)正好是10的運(yùn)動。然而,如果將delta增加到0.3,那么對象將移動18像素。總的來說,移動速度保持一致,并且不受幀率的影響。
還有一個(gè)好處是,你可以用px/s而不是px/frame的單位來表達(dá)你的運(yùn)動,這樣更容易可視化。
選擇動畫
現(xiàn)在播放器可以移動了,您需要根據(jù)AnimatedSprite是移動還是靜止來更改AnimatedSprite正在播放哪個(gè)動畫。 運(yùn)行動畫的圖面朝右,這意味著應(yīng)將其水平翻轉(zhuǎn)(使用Flip H屬性)以向左移動。 將此添加到_process()函數(shù)的末尾:
if velocity.length() > 0:$AnimatedSprite.animation = "run"$AnimatedSprite.flip_h = velocity.x < 0 else:$AnimatedSprite.animation = "idle"請注意,這段代碼走了一個(gè)小捷徑,flip_h是一個(gè)布爾屬性,這意味著它可以是真或假。布爾值也是像<這樣比較的結(jié)果,正因?yàn)槿绱?#xff0c;我們可以將屬性設(shè)置為等于比較的結(jié)果。這一行就相當(dāng)于這樣寫出來:
if velocity.x < 0:$AnimatedSprite.flip_h = true else:$AnimatedSprite.flip_h = false再次播放場景,并檢查每種情況下的動畫是否正確。確保AnimatedSprite中的Playing設(shè)置為On,這樣動畫就會播放。
開始和結(jié)束玩家的移動
當(dāng)游戲開始時(shí),主場景需要通知玩家游戲已經(jīng)開始。添加start()函數(shù)如下,主場景將用它來設(shè)置玩家的開始動畫和位置:
func start(pos): set_process(true) position = pos$AnimatedSprite.animation = "idle"die()函數(shù)會在玩家遇到障礙或超時(shí)時(shí)被調(diào)用:
func die():$AnimatedSprite.animation = "hurt" set_process(false)設(shè)置set_process(false)會使這個(gè)節(jié)點(diǎn)不再調(diào)用_process()函數(shù)。這樣一來,當(dāng)玩家已經(jīng)死了,他們就不能再通過按鍵輸入來移動了。
準(zhǔn)備碰撞
玩家應(yīng)該檢測它何時(shí)碰到硬幣或障礙物,但你還沒有讓他們這樣做。沒關(guān)系,因?yàn)槟憧梢允褂肎odot的信號功能來實(shí)現(xiàn)它。信號是節(jié)點(diǎn)發(fā)出信息的一種方式,其他節(jié)點(diǎn)可以檢測到并做出反應(yīng)。許多節(jié)點(diǎn)都有內(nèi)置的信號,例如,當(dāng)一個(gè)物體碰撞時(shí),或者當(dāng)一個(gè)按鈕被按下時(shí),會向你發(fā)出警報(bào)。你也可以為自己的目的定義自定義信號。
通過將信號連接到您要偵聽和響應(yīng)的節(jié)點(diǎn)來使用信號。 可以在檢查器或代碼中建立此連接。 在項(xiàng)目的后面,您將學(xué)習(xí)如何以兩種方式連接信號。
在腳本的頂部添加以下內(nèi)容(在擴(kuò)展Area2D后):
signal pickup signal hurt這些定義了自定義信號,當(dāng)你的玩家觸摸硬幣或障礙物時(shí),他們會發(fā)出(發(fā)送)信號。觸摸會被Area2D本身檢測到。選擇 "Player "節(jié)點(diǎn),點(diǎn)擊 "檢查器 "旁邊的 "節(jié)點(diǎn) "標(biāo)簽,可以看到player可以發(fā)出的信號列表:
注意你的自定義信號也在那里。由于其他對象也將是Area2D節(jié)點(diǎn),所以你需要area_entered()信號。選擇它,然后單擊 “連接”。在連接信號窗口中點(diǎn)擊連接–你不需要改變?nèi)魏芜@些設(shè)置。Godot會自動在您的腳本中創(chuàng)建一個(gè)名為_on_Player_area_entered()的新函數(shù)。
連接信號時(shí),除了讓Godot為您創(chuàng)建函數(shù)外,您還可以提供要將信號鏈接到的現(xiàn)有函數(shù)的名稱。 如果您不希望Godot為您創(chuàng)建功能,請將Make Function開關(guān)切換到Off。
在這個(gè)新函數(shù)中加入以下代碼:
func _on_Player_area_entered( area ): if area.is_in_group("coins"):area.pickup() emit_signal("pickup")if area.is_in_group("obstacles"): emit_signal("hurt")die()當(dāng)檢測到另一個(gè)Area2D時(shí),它將被傳遞到函數(shù)中(使用area變量)。 硬幣對象將具有Pickup()函數(shù),該函數(shù)定義拾取后硬幣的行為(例如播放動畫或聲音)。 創(chuàng)建硬幣和障礙物時(shí),將它們分配給適當(dāng)?shù)慕M,以便可以檢測到它們。
總結(jié)一下,這是目前完整的玩家腳本:
extends Area2Dsignal pickup signal hurtexport (int) var speed var velocity = Vector2() var screensize = Vector2(480, 720)func get_input(): velocity = Vector2()if Input.is_action_pressed("ui_left"): velocity.x -= 1if Input.is_action_pressed("ui_right"): velocity.x += 1if Input.is_action_pressed("ui_up"): velocity.y -= 1if Input.is_action_pressed("ui_down"):velocity.y += 1if velocity.length() > 0:velocity = velocity.normalized() * speedfunc _process(delta): get_input()position += velocity * deltaposition.x = clamp(position.x, 0, screensize.x) position.y = clamp(position.y, 0, screensize.y)if velocity.length() > 0:$AnimatedSprite.animation = "run"$AnimatedSprite.flip_h = velocity.x < 0 else:$AnimatedSprite.animation = "idle"func start(pos): set_process(true) position = pos$AnimatedSprite.animation = "idle"func die():$AnimatedSprite.animation = "hurt" set_process(false)func _on_Player_area_entered( area ): if area.is_in_group("coins"):area.pickup() emit_signal("pickup")if area.is_in_group("obstacles"): emit_signal("hurt")die()第2部分-硬幣場景
在這部分,你將制作硬幣供玩家收集。這將是一個(gè)單獨(dú)的場景,描述單個(gè)硬幣的所有屬性和行為。一旦保存,主場景將加載硬幣場景并創(chuàng)建多個(gè)實(shí)例(即副本)。
節(jié)點(diǎn)設(shè)置
點(diǎn)擊場景|新建場景,然后添加以下節(jié)點(diǎn)。不要忘記設(shè)置子節(jié)點(diǎn)不被選中,就像你在Player場景中做的那樣。
- Area2D (named Coin)
- AnimatedSprite
- CollisionShape2D
添加節(jié)點(diǎn)后一定要保存場景。
像在player場景中那樣設(shè)置AnimatedSprite。這一次,你只有一個(gè)動畫:一個(gè)閃亮/閃光效果,使硬幣看起來不那么平坦和無聊。添加所有的幀,并將速度(FPS)設(shè)置為12。圖片有點(diǎn)大,所以將AnimatedSprite的Scale設(shè)置為(0.5,0.5)。在CollisionShape2D中,使用CircleShape2D,并將其大小覆蓋硬幣圖像。不要忘記:在確定碰撞形狀的大小時(shí),千萬不要使用比例手柄。圓圈形狀有一個(gè)手柄,可以調(diào)整圓圈的半徑。
使用組
組為節(jié)點(diǎn)提供了標(biāo)記系統(tǒng),使您可以識別相似的節(jié)點(diǎn)。 一個(gè)節(jié)點(diǎn)可以屬于任意數(shù)量的組。 您需要確保所有硬幣都在一個(gè)名為“硬幣”的組中,以便player腳本對觸摸硬幣做出正確反應(yīng)。 選擇“硬幣”節(jié)點(diǎn),然后單擊“節(jié)點(diǎn)”選項(xiàng)卡(找到信號的相同選項(xiàng)卡),然后選擇“組”。 在框中輸入硬幣,然后單擊添加,如以下屏幕截圖所示:
腳本
接下來,在Coin節(jié)點(diǎn)上添加一個(gè)腳本。如果你在模板設(shè)置中選擇 “空”,Godot會創(chuàng)建一個(gè)沒有任何注釋或建議的空腳本。幣的腳本的代碼比玩家的代碼短得多:
extends Area2Dfunc pickup(): queue_free()Pickup()函數(shù)由Player腳本調(diào)用,并告訴硬幣被收集后該怎么做。 queue_free()是Godot的節(jié)點(diǎn)刪除方法。 它安全地從樹中刪除該節(jié)點(diǎn),并將其及其所有子節(jié)點(diǎn)從內(nèi)存中刪除。 稍后,您將在此處添加視覺效果,但是到目前為止,消失的硬幣已經(jīng)足夠了。
queue_free()不會立即刪除該對象,而是將其添加到要在當(dāng)前幀末尾刪除的隊(duì)列中。 這比立即刪除該節(jié)點(diǎn)更安全,因?yàn)橛螒蛑羞\(yùn)行的其他代碼可能仍需要該節(jié)點(diǎn)存在。 通過等待直到幀結(jié)束,Godot可以確保可以訪問該節(jié)點(diǎn)的所有代碼均已完成,并且可以安全地刪除該節(jié)點(diǎn)。
第三部分–主場景
主場景是將游戲的所有部分聯(lián)系在一起的。它將管理玩家、硬幣、計(jì)時(shí)器和游戲的其他部分。
節(jié)點(diǎn)設(shè)置
創(chuàng)建一個(gè)新場景,并添加一個(gè)名為Main的節(jié)點(diǎn)。要將Player添加到場景中,點(diǎn)擊實(shí)例按鈕并選擇您保存的Player.tscn:
現(xiàn)在,添加以下節(jié)點(diǎn)作為Main的子節(jié)點(diǎn),命名如下:
- TextureRect (命名為Background)–用于背景圖片。
- Node(命名為CoinContainer)–用于存放所有硬幣。
- Position2D(命名為PlayerStart)–標(biāo)記Player的起始位置。
- Timer(命名為GameTimer)–用于跟蹤時(shí)間限制。
確保Background是第一個(gè)子節(jié)點(diǎn)。 節(jié)點(diǎn)按所示順序繪制,因此在這種情況下背景將位于Player后面。 通過將grass.png圖像從資產(chǎn)文件夾拖動到Texture屬性中,將圖像添加到Background節(jié)點(diǎn)。
將拉伸模式更改為平鋪,然后單擊布局| Full Rect將幀調(diào)整為屏幕大小,如以下屏幕截圖所示:
將PlayerStart節(jié)點(diǎn)的Position設(shè)置為(240,350)。
您的場景布局應(yīng)如下所示:
主腳本
在Main節(jié)點(diǎn)中添加一個(gè)腳本(使用Empty模板)并添加以下變量:
extends Nodeexport (PackedScene) var Coin export (int) var playtimevar level var score var time_left var screensize var playing = false現(xiàn)在,當(dāng)您單擊Main時(shí),Coin和Playtime屬性將顯示在檢查器中。 從“文件系統(tǒng)”面板中拖動Coin.tscn并將其放在Coin屬性中。
將“playtime”設(shè)置為30(這是游戲?qū)⒊掷m(xù)的時(shí)間)。 其余的變量將在以后的代碼中使用。
初始化
接下來,添加_ready()函數(shù):
func _ready(): randomize()screensize = get_viewport().get_visible_rect().size$Player.screensize = screensize$Player.hide()在GDScript中,可以使用$通過名稱引用特定的節(jié)點(diǎn)。 這使您可以找到屏幕的大小并將其分配給Player的screensize變量。 hide()使玩家開始時(shí)不可見(您將使它們在游戲?qū)嶋H開始時(shí)顯示)。
在符號中,節(jié)點(diǎn)名稱是相對于運(yùn)行腳本的節(jié)點(diǎn)而言的。例如,符號中,節(jié)點(diǎn)名稱是相對于運(yùn)行腳本的節(jié)點(diǎn)而言的。例如,符號中,節(jié)點(diǎn)名稱是相對于運(yùn)行腳本的節(jié)點(diǎn)而言的。例如,Node1/Node2指的是Node1的子節(jié)點(diǎn)(Node2),而Node1本身就是當(dāng)前運(yùn)行腳本的子節(jié)點(diǎn)。Godot的自動完成功能會在您輸入時(shí)從樹中推薦節(jié)點(diǎn)名稱。請注意,如果節(jié)點(diǎn)的名稱包含空格,您必須在它周圍加上引號,例如,$“My Node”。
如果希望每次運(yùn)行場景時(shí)“隨機(jī)”數(shù)字的序列都不同,則必須使用randomize()。 從技術(shù)上講,這為隨機(jī)數(shù)生成器選擇了一個(gè)隨機(jī)種子。
開始新游戲
接下來,new_game()函數(shù)將初始化一個(gè)新游戲的一切。
func new_game(): playing = true level = 1score = 0time_left = playtime$Player.start($PlayerStart.position)$Player.show()$GameTimer.start() spawn_coins()除了將變量設(shè)置為其初始值之外,此函數(shù)還調(diào)用Player的start()函數(shù)以確保其移至正確的起始位置。 啟動游戲計(jì)時(shí)器,它將倒計(jì)時(shí)游戲中的剩余時(shí)間。
您還需要一個(gè)函數(shù),該函數(shù)將根據(jù)當(dāng)前級別創(chuàng)建若干硬幣:
func spawn_coins():for i in range(4 + level): var c = Coin.instance()$CoinContainer.add_child(c) c.screensize = screensizec.position = Vector2(rand_range(0, screensize.x), rand_range(0, screensize.y))在這個(gè)函數(shù)中,你創(chuàng)建了許多Coin對象的實(shí)例(這次是在代碼中,而不是通過點(diǎn)擊Instance a Scene按鈕),并將其添加為CoinContainer的子節(jié)點(diǎn)。每當(dāng)你實(shí)例一個(gè)新的節(jié)點(diǎn)時(shí),必須使用add_child()將其添加到樹中。最后,你為硬幣選擇一個(gè)隨機(jī)的位置。你會在每個(gè)關(guān)卡開始時(shí)調(diào)用這個(gè)函數(shù),每次產(chǎn)生更多的硬幣。
最終,你會希望new_game()在玩家點(diǎn)擊開始按鈕時(shí)被調(diào)用。現(xiàn)在,為了測試一切是否正常,將new_game()添加到你的_ready()函數(shù)的結(jié)尾,然后點(diǎn)擊播放項(xiàng)目(F5)。當(dāng)你被提示選擇一個(gè)主場景時(shí),選擇Main.tscn。現(xiàn)在,每當(dāng)你播放項(xiàng)目時(shí),主場景就會被啟動。
這時(shí),你應(yīng)該看到你的玩家和五個(gè)硬幣出現(xiàn)在屏幕上。當(dāng)玩家觸摸到一枚硬幣時(shí),它就會消失。
檢查剩余硬幣
主腳本需要檢測玩家是否撿到了所有的硬幣。由于硬幣都是CoinCointainer的子節(jié)點(diǎn),你可以在這個(gè)節(jié)點(diǎn)上使用get_child_count()來找出還剩下多少硬幣。把這個(gè)放在_process()函數(shù)中,這樣每一幀都會被檢查。
func _process(delta):if playing and $CoinContainer.get_child_count() == 0: level += 1time_left += 5 spawn_coins()如果沒有更多的硬幣剩余,那么玩家就會進(jìn)入下一關(guān)。
第4部分–用戶界面
你的游戲需要的最后一塊是用戶界面(UI)。這是一個(gè)用于顯示玩家在游戲過程中需要看到的信息的界面,在游戲中,這也被稱為抬頭顯示器(HUD),因?yàn)樾畔⑹且愿采w在游戲視圖之上的方式出現(xiàn)的。在游戲中,這也被稱為抬頭顯示器(HUD),因?yàn)檫@些信息是以疊加的形式出現(xiàn)在游戲視圖之上的。你也會用這個(gè)場景來顯示一個(gè)開始按鈕。
HUD將顯示以下信息:
- 分?jǐn)?shù)
- 剩余時(shí)間
- 一個(gè)信息,如游戲結(jié)束
- 一個(gè)啟動按鈕
節(jié)點(diǎn)設(shè)置
創(chuàng)建一個(gè)新場景,并添加一個(gè)名為HUD的CanvasLayer節(jié)點(diǎn)。CanvasLayer節(jié)點(diǎn)允許你在游戲的其他部分之上的一層上繪制你的UI元素,這樣它所顯示的信息就不會被任何游戲元素(如玩家或硬幣)所覆蓋。
Godot提供了各種各樣的UI元素,可以用來創(chuàng)建任何東西,從生命條等指標(biāo)到庫存等復(fù)雜界面。事實(shí)上,你用來制作這個(gè)游戲的Godot編輯器就是使用這些元素在Godot中構(gòu)建的。UI元素的基本節(jié)點(diǎn)是從Control擴(kuò)展而來的,并在節(jié)點(diǎn)列表中以綠色圖標(biāo)出現(xiàn)。為了創(chuàng)建你的UI,你將使用各種Control節(jié)點(diǎn)來定位、格式化和顯示信息。下面是HUD完成后的樣子。
錨和邊距
控制節(jié)點(diǎn)有一個(gè)位置和大小,但它們也有稱為錨和邊距的屬性。錨定義了節(jié)點(diǎn)的邊緣相對于父容器的原點(diǎn)或參考點(diǎn)。邊距表示從控制節(jié)點(diǎn)的邊緣到其相應(yīng)錨點(diǎn)的距離。當(dāng)您移動或調(diào)整控制節(jié)點(diǎn)的大小時(shí),邊距會自動更新。
信息標(biāo)簽
在場景中添加一個(gè)Label節(jié)點(diǎn),并將其名稱改為MessageLabel。這個(gè)標(biāo)簽將顯示游戲的標(biāo)題,以及游戲結(jié)束時(shí)的Game Over。這個(gè)標(biāo)簽應(yīng)該在游戲屏幕上居中。你可以用鼠標(biāo)拖動它,但為了精確地放置UI元素,你應(yīng)該使用Anchor屬性。
選擇 “視圖”|"顯示助手 "來顯示有助于查看錨點(diǎn)位置的圖釘,然后單擊 "布局 "菜單并選擇 “HCenter Wide”。
現(xiàn)在,MessageLabel跨過屏幕的寬度并垂直居中。 檢查器中的“文本”屬性設(shè)置標(biāo)簽顯示的文本。將其設(shè)置為 Coin Dash!,并將 Align 和 Valign 設(shè)置為 Center。
Label節(jié)點(diǎn)的默認(rèn)字體非常小,所以下一步就是分配一個(gè)自定義字體。向下滾動到 "檢查器 "中的 "自定義字體 "部分,選擇 “新建動態(tài)字體”(New DynamicFont),如下截圖所示:
現(xiàn)在,點(diǎn)擊DynamicFont,您可以調(diào)整字體設(shè)置。從FileSystem dock中,拖動Kenney Bold.ttf字體,并將其放入字體數(shù)據(jù)屬性中。設(shè)置大小為48,如下截圖所示:
分?jǐn)?shù)和時(shí)間顯示
HUD的頂部將顯示玩家的分?jǐn)?shù)和剩余時(shí)間。這兩個(gè)都將是Label節(jié)點(diǎn),位于游戲屏幕的相對兩側(cè)。你將使用容器節(jié)點(diǎn)來管理它們的位置,而不是將它們分開放置。
容器
UI容器會自動安排其子控件節(jié)點(diǎn)(包括其他容器)的位置。您可以使用它們在元素周圍添加padding,將它們居中,或?qū)⒃匕葱谢蛄信帕小C糠N類型的容器都有特殊的屬性來控制它們?nèi)绾伟才牌渥庸?jié)點(diǎn)。您可以在 "檢查器 "的 "自定義常量 "部分看到這些屬性。
UI容器會自動安排其子控件節(jié)點(diǎn)(包括其他容器)的位置。您可以使用它們在元素周圍添加padding,將它們居中,或?qū)⒃匕葱谢蛄信帕小C糠N類型的容器都有特殊的屬性來控制它們?nèi)绾伟才牌渥庸?jié)點(diǎn)。您可以在 "檢查器 "的 "自定義常量 "部分看到這些屬性。
要管理分?jǐn)?shù)和時(shí)間標(biāo)簽,在HUD中添加一個(gè)MarginContainer節(jié)點(diǎn)。使用 "布局 "菜單將錨點(diǎn)設(shè)置為頂部寬。在 "自定義常量 "部分,將 “右邊距”、"頂部邊距 "和 "左邊距 "設(shè)置為10。這將增加一些padding,這樣文字就不會貼著屏幕的邊緣了。
由于分?jǐn)?shù)和時(shí)間標(biāo)簽將使用與MessageLabel相同的字體設(shè)置,所以如果你復(fù)制它將節(jié)省時(shí)間。點(diǎn)擊MessageLabel并按Ctrl + D(macOS上為Cmd + D)兩次來創(chuàng)建兩個(gè)重復(fù)的標(biāo)簽。將它們都拖拽到MarginContainer上,使其成為它的子節(jié)點(diǎn)。命名一個(gè) ScoreLabel 和另一個(gè) TimeLabel,并將兩個(gè)標(biāo)簽的 Text 屬性設(shè)置為 0。將 ScoreLabel 的 Align 設(shè)置為 Left,TimeLabel 的 Align 設(shè)置為 Right。
通過GDScript更新UI
將腳本添加到HUD節(jié)點(diǎn)。 例如,此腳本將在需要更改UI元素的屬性時(shí)更新UI元素,例如,每當(dāng)收集硬幣時(shí)都會更新分?jǐn)?shù)文本。 請參考以下代碼:
extends CanvasLayer signal start_gamefunc update_score(value):$MarginContainer/ScoreLabel.text = str(value)func update_timer(value):$MarginContainer/TimeLabel.txt = str(value)主場景的腳本將調(diào)用這些函數(shù)來更新顯示,每當(dāng)值有變化時(shí),就會更新。對于MessageLabel,你還需要一個(gè)定時(shí)器來使它在短暫的時(shí)間后消失。添加一個(gè)定時(shí)器節(jié)點(diǎn),并將其名稱改為MessageTimer。在 "檢查器 "中,將其 "等待時(shí)間 "設(shè)置為2秒,并勾選 "One Shot "設(shè)置為 “On”。這樣可以確保在啟動時(shí),定時(shí)器只運(yùn)行一次,而不是重復(fù)運(yùn)行。添加以下代碼。
func show_message(text):$MessageLabel.text = text$MessageLabel.show()$MessageTimer.start()在這個(gè)函數(shù)中,你可以顯示消息并啟動定時(shí)器。要隱藏消息,連接MessageTimer的timeout()信號,并添加這個(gè):
func _on_MessageTimer_timeout():$MessageLabel.hide()使用按鈕
添加一個(gè)Button節(jié)點(diǎn),并將其名稱改為StartButton。這個(gè)按鈕將在游戲開始前顯示,當(dāng)點(diǎn)擊時(shí),它將隱藏自己,并向主場景發(fā)送一個(gè)信號來啟動游戲。將Text屬性設(shè)置為Start,并像對MessageLabel那樣改變自定義字體。在 "布局 "菜單中,選擇 “中心底部”。這將使按鈕位于屏幕的最底部,因此可以通過按向上箭頭鍵或編輯頁邊距并將Top設(shè)置為-150,Bottom設(shè)置為-50來將其向上移動一點(diǎn)。
當(dāng)一個(gè)按鈕被點(diǎn)擊時(shí),會發(fā)出一個(gè)信號。在StartButton的節(jié)點(diǎn)標(biāo)簽中,連接pressed()信號:
func _on_StartButton_pressed():$StartButton.hide()$MessageLabel.hide() emit_signal("start_game")HUD發(fā)出start_game信號,通知Main該開始新游戲了。
游戲結(jié)束
你的UI的最后任務(wù)是對游戲結(jié)局做出反應(yīng):
func show_game_over(): show_message("Game Over") yield($MessageTimer, "timeout")$StartButton.show()$MessageLabel.text = "Coin Dash!"$MessageLabel.show()在這個(gè)函數(shù)中,你需要Game Over消息顯示兩秒鐘,然后消失,這就是show_message()的作用。然而,你也希望在消息消失后顯示開始按鈕。yield()函數(shù)暫停執(zhí)行函數(shù),直到給定節(jié)點(diǎn)(MessageTimer)發(fā)出給定信號(超時(shí))。一旦接收到信號,函數(shù)就會繼續(xù),將你返回到初始狀態(tài),這樣你就可以再次播放。
將HUD添加到Main
現(xiàn)在,你需要設(shè)置主場景和HUD之間的通信。在Main場景中添加一個(gè)HUD場景的實(shí)例。在Main場景中,連接GameTimer的timeout()信號并添加以下內(nèi)容:
func _on_GameTimer_timeout(): time_left -= 1$HUD.update_timer(time_left) if time_left <= 0:game_over()每當(dāng)GameTimer超時(shí)(每秒鐘),剩余時(shí)間就會減少。接下來,連接Player的pickup()和hurt()信號:
func _on_Player_pickup(): score += 1$HUD.update_score(score)func _on_Player_hurt(): game_over()當(dāng)游戲結(jié)束時(shí),需要發(fā)生幾件事,所以添加以下功能:
func game_over(): playing = false$GameTimer.stop()for coin in $CoinContainer.get_children(): coin.queue_free()$HUD.show_game_over()$Player.die()此函數(shù)暫停游戲,還循環(huán)遍歷硬幣并刪除剩余的硬幣,以及調(diào)用HUD的show_game_over()函數(shù)。
最后,StartButton需要激活new_game()函數(shù)。點(diǎn)擊HUD實(shí)例,選擇其new_game()信號。在信號連接對話框中,單擊Make Function to Off,并在Method In Node字段中,鍵入new_game。這將把信號連接到現(xiàn)有的函數(shù),而不是創(chuàng)建一個(gè)新的函數(shù)。請看下面的截圖:
從_ready()函數(shù)中刪除new_game()并將這兩行添加到new_game()函數(shù)中:
$HUD.update_score(score) $HUD.update_timer(time_left)現(xiàn)在,你可以玩這個(gè)游戲了 確認(rèn)所有的部分都能按預(yù)期工作:分?jǐn)?shù)、倒計(jì)時(shí)、游戲結(jié)束和重新開始等。如果你發(fā)現(xiàn)有一個(gè)部分不工作,回去檢查你創(chuàng)建它的步驟,以及它與游戲其他部分連接的步驟。
第5部分-完成
您已經(jīng)創(chuàng)建了一個(gè)可運(yùn)行的游戲,但是仍然可以使它變得更加令人興奮。 游戲開發(fā)人員使用“juice”一詞來描述使游戲感覺良好的事物。 果汁可以包括聲音,視覺效果或其他任何可以增加玩家娛樂性的內(nèi)容,而不必改變游戲性。
在本節(jié)中,您將添加一些小juice來完成游戲。
視覺效果
當(dāng)您拾起硬幣時(shí),它們就會消失,這并不是很吸引人。 添加視覺效果會使收集大量硬幣變得更加令人滿意。
首先在Coin場景中添加一個(gè)Tween節(jié)點(diǎn)。
什么是補(bǔ)間?
tween是一種使用特定函數(shù)對一些值進(jìn)行插值(逐漸改變)的方法(從一個(gè)起始值到一個(gè)結(jié)束值)。例如,你可以選擇一個(gè)穩(wěn)定地改變數(shù)值的函數(shù),或者一個(gè)開始時(shí)很慢但速度逐漸加快的函數(shù)。Tweening有時(shí)也被稱為緩動。
當(dāng)在Godot中使用Tween節(jié)點(diǎn)時(shí),你可以指定它來改變一個(gè)節(jié)點(diǎn)的一個(gè)或多個(gè)屬性。在本例中,你將增加硬幣的Scale,同時(shí)使用Modulate屬性使其淡出。
在Coin的_ready()函數(shù)中添加這一行:
$Tween.interpolate_property($AnimatedSprite, 'scale',$AnimatedSprite.scale,$AnimatedSprite.scale * 3, 0.3,Tween.TRANS_QUAD,Tween.EASE_IN_OUT)interpolate_property()函數(shù)使Tween改變一個(gè)節(jié)點(diǎn)的屬性。有七個(gè)參數(shù):
- 要影響的節(jié)點(diǎn)
- 需要更改的屬性
- 屬性的初始值
- 屬性的最終值
- 持續(xù)時(shí)間(以秒為單位)
- 使用的功能
- 方向
當(dāng)玩家拿起硬幣時(shí),tween應(yīng)該開始播放。替換pickup()函數(shù)中的queue_free():
func pickup(): monitoring = false$Tween.start()將監(jiān)控設(shè)置為false,可以確保在tween動畫中,如果玩家觸摸到硬幣,則不會發(fā)出area_enter()信號。
最后,當(dāng)動畫結(jié)束時(shí),硬幣應(yīng)該被刪除,所以連接Tween節(jié)點(diǎn)的tween_completed()信號:
func _on_Tween_tween_completed(object, key): queue_free()現(xiàn)在,當(dāng)你運(yùn)行游戲時(shí),你應(yīng)該看到硬幣在撿到時(shí)越來越大。這是很好的,但是當(dāng)同時(shí)應(yīng)用于多個(gè)屬性時(shí),tweens會更加有效。你可以添加另一個(gè)interpolate_property(),這次是為了改變精靈的不透明度。這是通過改變調(diào)制屬性來實(shí)現(xiàn)的,調(diào)制屬性是一個(gè)Color對象,并將其alpha通道從1(不透明)改為0(透明)。請參考以下代碼:
$Tween.interpolate_property($AnimatedSprite, 'modulate',Color(1, 1, 1, 1),Color(1, 1, 1, 0), 0.3,ween.TRANS_QUAD,Tween.EASE_IN_OUT)聲音
聲音是游戲設(shè)計(jì)中最重要但經(jīng)常被忽視的部分之一。好的聲音設(shè)計(jì)可以為你的游戲增加大量的juice,而你只需付出很小的努力.聲音可以給玩家反饋,將他們與角色的情感聯(lián)系起來,甚至成為游戲玩法的一部分。
在這個(gè)游戲中,你要添加三種音效。在主場景中,添加三個(gè)AudioStreamPlayer節(jié)點(diǎn),并將它們命名為CoinSound、LevelSound和EndSound。將音頻文件夾中的每一個(gè)聲音(你可以在FileSystem dock中的assets下找到它)拖到每個(gè)節(jié)點(diǎn)的相應(yīng)Stream屬性中。
要播放聲音,請?jiān)谄渖险{(diào)用play()函數(shù)。 將$ CoinSound.play()添加到_on_Player_pickup()函數(shù),將$ EndSound.play()添加到game_over()函數(shù),將$ LevelSound.play()添加到spawn_coins()函數(shù)。
提升能力
物品有很多可能會給玩家?guī)硇⌒〉膬?yōu)勢或力量。 在本部分中,您將添加一個(gè)能量道具,使玩家在收集時(shí)獲得少量時(shí)間加成。 偶爾會出現(xiàn)一小段時(shí)間,然后消失。
新的場景將與你已經(jīng)創(chuàng)建的硬幣場景非常相似,所以點(diǎn)擊你的硬幣場景,選擇場景|另存為,并將其保存為Powerup.tscn。將根節(jié)點(diǎn)的名稱改為Powerup,并通過點(diǎn)擊清除腳本按鈕刪除腳本: 。你還應(yīng)該斷開 area_entered 信號(你以后會重新連接)。在 "組 "選項(xiàng)卡中,通過點(diǎn)擊刪除按鈕(它看起來像一個(gè)垃圾桶)刪除硬幣組,并將其添加到一個(gè)名為Powerups的新組中代替。
在AnimatedSprite中,將硬幣的圖片改為powerup,你可以在res://assets/pow/文件夾中找到。
點(diǎn)擊添加一個(gè)新的腳本,并復(fù)制Coin.gd腳本中的代碼。將_on_Coin_area_entered的名稱改為_on_Powerup_area_entered,再將area_entered信號連接到它。記住,這個(gè)函數(shù)名將由信號連接窗口自動選擇。
接下來,添加一個(gè)名為Lifetime的Timer節(jié)點(diǎn)。這將限制對象在屏幕上停留的時(shí)間。將其等待時(shí)間設(shè)置為2,將One Shot和Autostart都設(shè)置為On。連接它的超時(shí)信號,這樣就可以在時(shí)間段結(jié)束時(shí)將其刪除:
func _on_Lifetime_timeout(): queue_free()現(xiàn)在,轉(zhuǎn)到您的Main場景并添加另一個(gè)名為PowerupTimer的Timer節(jié)點(diǎn)。 將其“One Shot”屬性設(shè)置為“開”。 您可以使用另一個(gè)AudioStreamPlayer添加音頻文件夾中的Powerup.wav聲音。
連接超時(shí)信號,并添加以下代碼來生成Powerup:
func _on_PowerupTimer_timeout(): var p = Powerup.instance() add_child(p)p.screensize = screensizep.position = Vector2(rand_range(0, screensize.x),rand_range(0, screensize.y))Powerup場景需要通過添加一個(gè)變量來鏈接,然后在Inspector中拖動場景到屬性中,就像你之前對Coin場景所做的那樣:
export (PackedScene) var Powerup能量的出現(xiàn)應(yīng)該是不可預(yù)知的,所以每當(dāng)你開始一個(gè)新的關(guān)卡時(shí),需要設(shè)置PowerupTimer的等待時(shí)間。在用 spawn_coins()產(chǎn)生新的硬幣后,將其添加到 _process()函數(shù)中:
$PowerupTimer.wait_time = rand_range(5, 10) $PowerupTimer.start()現(xiàn)在,你將會有能量出現(xiàn),最后一步就是當(dāng)收集到一個(gè)加電時(shí),給玩家一些獎(jiǎng)勵(lì)時(shí)間。目前,玩家腳本假設(shè)它碰到的任何東西不是硬幣就是障礙物。修改Player.gd中的代碼,檢查被撞到的是哪種物體:
func _on_Player_area_entered( area ): if area.is_in_group("coins"):area.pickup() emit_signal("pickup", "coin")if area.is_in_group("powerups"): area.pickup() emit_signal("pickup", "powerup")if area.is_in_group("obstacles"): emit_signal("hurt")die()請注意,現(xiàn)在你發(fā)出的拾取信號帶有一個(gè)額外的參數(shù),命名對象的類型。現(xiàn)在,Main.gd中的相應(yīng)函數(shù)可以被修改為接受該參數(shù),并使用匹配語句來決定采取什么行動:
func _on_Player_pickup(type): match type:"coin":score += 1$CoinSound.play()$HUD.update_score(score) "powerup":time_left += 5$PowerupSound.play()$HUD.update_timer(time_left)match語句是if語句的有用替代方法,尤其是當(dāng)您要測試大量可能的值時(shí)。
嘗試運(yùn)行游戲并收集能量。 確保聲音播放并且計(jì)時(shí)器增加五秒鐘。
硬幣動畫
創(chuàng)建硬幣場景時(shí),添加了AnimatedSprite,但尚未播放。 硬幣動畫顯示在硬幣表面?zhèn)鞑サ拈W光效果。 如果所有硬幣同時(shí)顯示,則看起來太規(guī)則了,因此每個(gè)硬幣在動畫中需要一個(gè)小的隨機(jī)延遲。
首先,點(diǎn)擊AnimatedSprite,然后點(diǎn)擊Frames資源。確保Loop被設(shè)置為Off,Speed被設(shè)置為12。
在Coin場景中添加一個(gè)Timer節(jié)點(diǎn),并在_ready()中添加這段代碼:
$Timer.wait_time = rand_range(3, 8) $Timer.start()現(xiàn)在,連接定時(shí)器的timeout()信號,并添加這個(gè):
func _on_Timer_timeout():$AnimatedSprite.frame = 0$AnimatedSprite.play()試著運(yùn)行游戲,觀察硬幣的動畫。這是一個(gè)很好的視覺效果,只需要付出很小的努力。在專業(yè)游戲中,你會注意到很多這樣的效果。雖然非常微妙,但視覺上的吸引力讓人有更愉悅的體驗(yàn)。
前面的Powerup對象有一個(gè)類似的動畫,你可以用同樣的方式添加。
障礙物
最后,可以通過引入一個(gè)玩家必須避開的障礙物,讓游戲更具挑戰(zhàn)性。碰到障礙物就會結(jié)束游戲。
為仙人掌創(chuàng)建一個(gè)新場景,并添加以下節(jié)點(diǎn)。
- Area2D (named Cactus)
- Sprite
- CollisionShape2D
將仙人掌紋理從FileSystem停靠點(diǎn)拖到Sprite的Texture屬性。 將RectangleShape2D添加到碰撞形狀并調(diào)整其大小,以使其覆蓋圖像。 還記得您是否在播放器腳本中添加了area.is_in_group(“ obstacless”)嗎? 使用“節(jié)點(diǎn)”選項(xiàng)卡(“檢查器”旁邊)將仙人掌主體添加到障礙物組。
現(xiàn)在,將仙人掌實(shí)例添加到主場景中,并將其移動到屏幕上半部的某個(gè)位置(遠(yuǎn)離Player生成的位置)。 玩游戲,看看碰到仙人掌會發(fā)生什么。
您可能已經(jīng)發(fā)現(xiàn)了一個(gè)問題:硬幣會在仙人掌后面產(chǎn)生,因此無法撿起。 放置硬幣后,如果發(fā)現(xiàn)硬幣與障礙物重疊,則需要移動硬幣。 連接硬幣的area_entered()信號并添加以下內(nèi)容:
func _on_Coin_area_entered( area ): if area.is_in_group("obstacles"):position = Vector2(rand_range(0, screensize.x), rand_range(0, screensize.y))如果添加了前面的Powerup對象,則需要對其area_entered信號執(zhí)行相同的操作。
總結(jié)
在本章中,您通過創(chuàng)建一個(gè)基本的2D游戲來學(xué)習(xí)Godot Engine的基礎(chǔ)知識。您設(shè)置了項(xiàng)目并創(chuàng)建了多個(gè)場景,使用精靈和動畫,捕獲用戶輸入,使用信號與事件通信,并使用控制節(jié)點(diǎn)創(chuàng)建了UI。你在這里學(xué)到的東西是重要的技能,你將在任何Godot項(xiàng)目中使用。
在進(jìn)入下一章之前,先看一下這個(gè)項(xiàng)目。你明白每個(gè)節(jié)點(diǎn)在做什么嗎?是否有任何你不理解的代碼部分?如果有,請回過頭來復(fù)習(xí)本章的那部分內(nèi)容。
另外,可以自由地對游戲進(jìn)行實(shí)驗(yàn),改變一些東西。最好的方法之一是改變它們,看看會發(fā)生什么,從而很好地感受到游戲的不同部分正在做什么。
在下一章中,你將探索更多Godot的功能,并學(xué)習(xí)如何通過構(gòu)建一個(gè)更復(fù)雜的游戲來使用更多的節(jié)點(diǎn)類型。
總結(jié)
以上是生活随笔為你收集整理的第二章-Coin Dash的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 python 填写 word 表格
- 下一篇: 评测三款最流行的azw3阅读器