日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第二章-Coin Dash

發布時間:2024/1/1 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第二章-Coin Dash 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Coin Dash

  • Project setup
  • 向量和2D坐標系
  • Vectors
  • Pixel rendering像素渲染
  • 第一部分–玩家場景
  • 創建場景
  • 精靈動畫
  • 碰撞形狀
  • 編寫Player腳本
  • 移動Player
  • 關于delta
  • 選擇動畫
  • 開始和結束玩家的移動
  • 準備碰撞
  • 第2部分-硬幣場景
  • 節點設置
  • 使用組
  • 腳本
  • 第三部分–主場景
  • 節點設置
  • 主腳本
  • 初始化
  • 開始新游戲
  • 檢查剩余硬幣
  • 第4部分–用戶界面
  • 節點設置
  • 錨和邊距
  • 信息標簽
  • 分數和時間顯示
  • 容器
  • 通過GDScript更新UI
  • 使用按鈕
  • 游戲結束
  • 將HUD添加到Main
  • 第5部分-完成
  • 視覺效果
  • 什么是補間?
  • 聲音
  • 提升能力
  • 硬幣動畫
  • 障礙物
  • 總結

這第一個項目將指導你制作你的第一個Godot引擎項目。你將學習Godot編輯器如何工作,如何構建一個項目,以及如何構建一個小型2D游戲。

重要提示-即使您不是游戲開發的新手,也不要跳過本章。 盡管您可能已經了解了許多基本概念,但本項目將介紹您需要了解的一些基本Godot功能和設計范例。 在開發更復雜的項目時,將基于這些概念。

本章中的游戲稱為Coin Dash。 您的角色必須在屏幕上四處移動,在爭分奪秒的比賽中收集盡可能多的硬幣。 完成后,游戲將如下所示:

Project setup

啟動Godot并創建一個新項目,確保使用“創建文件夾”按鈕以確保該項目的文件與其他項目分開保存。 您可以在這里https://github.com/PacktPublishing/Godot-Game-Engine-Projects/releases下載有關游戲的藝術和聲音的Zip文件(統稱為資產)。

將此文件解壓到你的新項目文件夾中。

在這個項目中,您將制作三個獨立的場景:Player,Coin和HUD,它們都將合并到游戲的Main場景中。 在較大的項目中,創建單獨的文件夾來保存每個場景的資產和腳本可能很有用,但是對于這個相對較小的游戲,您可以將場景和腳本保存在根文件夾中,該根文件夾稱為res://( res是資源的縮寫)。 項目中的所有資源都將相對于res://文件夾放置。 您可以在左上角的FileSystem停靠欄中看到您的項目文件夾:

例如,硬幣的圖片將位于res://assets/coin/。

這個游戲將使用豎屏模式,所以你需要調整游戲窗口的大小。點擊項目菜單,選擇項目設置,如下圖所示:

查找“顯示/窗口”部分,并將“寬度”設置為480,將“高度”設置為720。同樣在此部分,將“拉伸/模式”設置為2D,將“長寬比”保持不變。 這將確保,如果用戶調整游戲窗口的大小,則所有內容都將適當縮放,并且不會拉伸或變形。 如果愿意,還可以取消選中“可調整大小”復選框,以防止完全調整窗口的大小。

向量和2D坐標系

注意:本節是2D坐標系的非常簡短的概述,并且不會非常深入地研究矢量數學。 本文旨在對這些主題如何應用于Godot中的游戲開發做一個高層次的概述。向量數學是游戲開發中必不可少的工具,因此,如果您需要對該主題有更廣泛的了解,請參閱可汗學院的線性代數系列(https://www.khanacademy.org/math/linear-algebra)。

在2D模式下工作時,您將使用直角坐標來標識空間中的位置。 2D空間中的特定位置被寫為一對值,例如(4,3),分別代表沿x和y軸的位置。 以此方式可以描述2D平面中的任何位置。

在2D空間中,Godot遵循將x軸指向右側,將y軸指向下方的通用計算機圖形慣例:

如果你是計算機圖形學或游戲開發的新手,正y軸指向向下而不是向上,這可能看起來很奇怪,因為你可能在數學課上學到的不一樣。然而,這種方向在計算機圖形應用中非常常見。

Vectors

您也可以將位置(4,3)視為與(0,0)點或原點的偏移量。 想象一下從原點指向該點的箭頭:

此箭頭是向量。 它代表了大量有用的信息,包括該點的位置(4,3),其長度m和其與x軸的夾角θ。 總之,這是一個位置向量,換句話說,它描述了空間中的位置。 向量還可以表示運動,加速度或具有x和y分量的任何其他量。

在Godot中,向量(用于2D的Vector2或用于3D的Vector3)被廣泛使用,并且在本書的項目構建過程中將使用它們。

Pixel rendering像素渲染

Godot中的向量坐標是浮點數,而不是整數。這意味著一個Vector2可以有一個小數值,比如(1.5,1.5)。由于對象不能以半數像素繪制,這對于像素藝術游戲來說,可能會造成視覺上的問題,因為你要確保紋理的所有像素都被繪制出來。

為了解決這個問題,請打開項目|項目設置,并在下面找到渲染/質量部分。
側邊欄并啟用 “使用像素捕捉”,如下截圖所示。

如果你在游戲中使用2D像素藝術,最好在開始項目時就啟用這個設置。這個設置在3D游戲中沒有效果。

第一部分–玩家場景

你要做的第一個場景定義了玩家對象。創建一個單獨的玩家場景的好處之一是,你可以獨立地測試它,甚至在你創建游戲的其他部分之前。隨著你的項目規模和復雜度的增長,這種游戲對象的分離將變得越來越有用。保持單個游戲對象相互分離,使它們更容易排除故障,修改,甚至完全替換而不影響游戲的其他部分。這也使得你的玩家可以重復使用–你可以把玩家場景放到一個完全不同的游戲中去,而且它的工作原理是一樣的。

玩家場景將顯示你的角色及其動畫,響應用戶的輸入,相應地移動角色,并檢測與游戲中其他物體的碰撞。

創建場景

首先點擊 "添加/創建新節點 "按鈕,選擇一個Area2D。然后,點擊它的名字,并將其改為Player。點擊Scene | Save Scene來保存場景。這是場景的根節點或頂層節點。您將通過向這個節點添加子節點來為Player添加更多的功能:

在添加任何子代之前,最好確保你不會因為點擊它們而意外地移動或調整它們的大小。選擇Player節點,點擊鎖旁邊的圖標:

工具提示會說,確保對象的子對象是不可選擇的,如上面的屏幕截圖所示。

在創建一個新場景時,總是這樣做是個好主意。如果一個物體的碰撞形狀或精靈被偏移或縮放,它會導致意想不到的錯誤并且難以修復。使用此選項,節點及其所有子節點將始終一起移動。

精靈動畫

使用Area2D,你可以檢測到其他物體重疊或撞到玩家,但是Area2D自己沒有一個外觀,所以點擊玩家節點并添加一個AnimatedSprite(動畫精靈)節點作為子節點。動畫精靈將為你的player處理外觀和動畫。注意,節點旁邊有一個警告符號。一個動畫精靈需要一個SpriteFrames資源,它包含了它可以顯示的動畫。要創建一個,在檢查器中找到frame屬性,然后點擊 | New SpriteFrames:

接下來,在同樣的位置,點擊,打開SpriteFrames面板:

左邊是動畫的列表。點擊默認的一個,并將其重命名為run。然后,單擊 "添加 "按鈕,創建第二個名為idle的動畫和第三個名為hurt的動畫。

在左側的FileSystem停靠欄,找到run, idle,和hurt的玩家圖像,并將其拖入相應的動畫中:

每個動畫的默認速度設置為每秒5幀。這有點太慢了,所以點擊每個動畫,將速度(FPS)設置為8。在 "檢查器 "中,勾選 "播放 "屬性旁邊的 “開啟”,然后選擇一個動畫來查看動畫的運行情況:

稍后,你將根據玩家正在做的事情,編寫代碼在這些動畫之間進行選擇。但首先,你需要完成對玩家節點的設置。

碰撞形狀

當使用Area2D或Godot中的其他碰撞對象之一時,它需要具有定義的形狀,否則就無法檢測到碰撞。 碰撞形狀定義了對象占據的區域,并用于檢測重疊和/或碰撞。 形狀由Shape2D定義,包括矩形,圓形,多邊形和其他類型的形狀。
為了方便起見,當您需要向區域或物理物體添加形狀時,可以將CollisionShape2D作為子級添加。 然后,選擇所需的形狀類型,然后可以在編輯器中編輯其大小。

添加一個CollisionShape2D作為Player的子級(確保你沒有把它添加為AnimatedSprite的子級)。這將允許你確定玩家的hitbox,或其碰撞區域的邊界。在 "檢查器 "中,在 "形狀 "旁邊,點擊,然后選擇 “New RectangleShape2D”。調整形狀的大小以覆蓋精靈:

注意不要縮放形狀的輪廓! 僅使用尺寸手柄(紅色)來調整形狀! 碰撞在縮放的碰撞形狀下無法正常工作。

你可能已經注意到,碰撞形狀并沒有在精靈上居中。這是因為精靈本身沒有垂直居中。我們可以通過給AnimatedSprite添加一個小的偏移來解決這個問題。點擊節點,在檢查器中尋找Offset屬性。將其設置為(0,-5)。

當你完成后,你的Player場景應該是這樣的:

編寫Player腳本

現在,你已經準備好添加一個腳本了。腳本允許你添加內置節點沒有提供的額外功能。點擊Player節點,然后點擊添加腳本按鈕:

在 "腳本設置 "窗口中,你可以保持默認設置不變。如果你還記得保存場景(見前面的截圖),腳本將自動被命名為與場景的名稱相匹配。點擊創建,你會被帶到腳本窗口。你的腳本將包含一些默認的注釋和提示。你可以刪除注釋(以#開頭的行)。請參考以下代碼片段。

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

每個腳本的第一行將描述該腳本附加到的節點類型。 接下來,您將定義類變量:

extends Area2Dexport (int) var speed var velocity = Vector2() var screensize = Vector2(480, 720)

在speed變量上使用export關鍵字可以使您在Inspector中設置其值,并讓Inspector知道該變量應包含的數據類型。 這對于您想要能夠調整的值非常方便,就像調整節點的內置屬性一樣。 單擊Player節點,并將Speed屬性設置為350,如以下屏幕截圖所示:

velocity將包含角色當前的移動速度和方向,screensize將用于設置玩家的移動限制。稍后,游戲的主場景將設置這個變量,但現在你將手動設置它,以便你可以測試。

移動Player

接下來,你將使用_process()函數來定義player將做什么。_process()函數在每一幀都會被調用,所以你會用它來更新你的游戲中那些你期望經常變化的元素。你需要玩家做三件事。

  • 檢查鍵盤輸入
  • 向指定方向移動
  • 播放相應的動畫

首先,您需要檢查輸入。 對于此游戲,您需要檢查四個方向輸入(四個箭頭鍵)。 輸入動作在“輸入映射”選項卡下的項目設置中定義。 在此選項卡中,您可以定義自定義事件并為它們分配不同的鍵,鼠標操作或其他輸入。 默認情況下,Godot將事件分配給鍵盤箭頭,因此您可以將其用于此項目。
您可以使用Input.is_action_pressed()檢測是否按下了輸入,如果按住該鍵,則返回true;否則,返回false。 組合所有四個按鈕的狀態將為您提供最終的移動方向。 例如,如果您同時按住向右和向下,則所得的速度矢量將為(1,1)。 在這種情況下,由于我們同時添加了水平和垂直移動,因此player的移動速度要比水平移動的速度快。

你可以通過將速度歸一化來防止這種情況發生,也就是將它的長度設為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()函數中將所有這些代碼分組在一起,可以使以后更輕松地進行更改。 例如,您可以決定更改為模擬操縱桿或其他類型的控制器。 從_process()調用此函數,然后通過生成的速度更改玩家的位置。 為了防止player離開屏幕,可以使用clip()函數將位置限制為最小值和最大值:

func _process(delta): get_input()position += velocity * deltaposition.x = clamp(position.x, 0, screensize.x) position.y = clamp(position.y, 0, screensize.y)

單擊“播放編輯的場景”(F6),并確認您可以沿屏幕的所有方向移動player。

關于delta

_process()函數包含一個名為delta的參數,然后乘以速度。什么是delta?

游戲引擎嘗試以一致的每秒60幀的速度運行。然而,這可能會由于計算機速度變慢而改變,無論是在Godot還是從計算機本身。如果幀速率不一致,那么它將影響游戲對象的移動。例如,假設一個對象每幀移動10個像素。如果一切運行順利,這將轉化為在一秒內移動600像素。但是,如果其中一些幀需要更長的時間,那么在那一秒中可能只有50幀,所以對象只移動了500像素。

Godot,像大多數游戲引擎和框架一樣,通過傳遞給你delta來解決這個問題,delta是指自上一幀以來的經過時間。大多數情況下,這將是0.016秒左右(或16毫秒左右)。如果你把你想要的速度(600 px/s)乘以delta,你將得到一個正好是10的運動。然而,如果將delta增加到0.3,那么對象將移動18像素。總的來說,移動速度保持一致,并且不受幀率的影響。

還有一個好處是,你可以用px/s而不是px/frame的單位來表達你的運動,這樣更容易可視化。

選擇動畫

現在播放器可以移動了,您需要根據AnimatedSprite是移動還是靜止來更改AnimatedSprite正在播放哪個動畫。 運行動畫的圖面朝右,這意味著應將其水平翻轉(使用Flip H屬性)以向左移動。 將此添加到_process()函數的末尾:

if velocity.length() > 0:$AnimatedSprite.animation = "run"$AnimatedSprite.flip_h = velocity.x < 0 else:$AnimatedSprite.animation = "idle"

請注意,這段代碼走了一個小捷徑,flip_h是一個布爾屬性,這意味著它可以是真或假。布爾值也是像<這樣比較的結果,正因為如此,我們可以將屬性設置為等于比較的結果。這一行就相當于這樣寫出來:

if velocity.x < 0:$AnimatedSprite.flip_h = true else:$AnimatedSprite.flip_h = false

再次播放場景,并檢查每種情況下的動畫是否正確。確保AnimatedSprite中的Playing設置為On,這樣動畫就會播放。

開始和結束玩家的移動

當游戲開始時,主場景需要通知玩家游戲已經開始。添加start()函數如下,主場景將用它來設置玩家的開始動畫和位置:

func start(pos): set_process(true) position = pos$AnimatedSprite.animation = "idle"

die()函數會在玩家遇到障礙或超時時被調用:

func die():$AnimatedSprite.animation = "hurt" set_process(false)

設置set_process(false)會使這個節點不再調用_process()函數。這樣一來,當玩家已經死了,他們就不能再通過按鍵輸入來移動了。

準備碰撞

玩家應該檢測它何時碰到硬幣或障礙物,但你還沒有讓他們這樣做。沒關系,因為你可以使用Godot的信號功能來實現它。信號是節點發出信息的一種方式,其他節點可以檢測到并做出反應。許多節點都有內置的信號,例如,當一個物體碰撞時,或者當一個按鈕被按下時,會向你發出警報。你也可以為自己的目的定義自定義信號。

通過將信號連接到您要偵聽和響應的節點來使用信號。 可以在檢查器或代碼中建立此連接。 在項目的后面,您將學習如何以兩種方式連接信號。

在腳本的頂部添加以下內容(在擴展Area2D后):

signal pickup signal hurt

這些定義了自定義信號,當你的玩家觸摸硬幣或障礙物時,他們會發出(發送)信號。觸摸會被Area2D本身檢測到。選擇 "Player "節點,點擊 "檢查器 "旁邊的 "節點 "標簽,可以看到player可以發出的信號列表:

注意你的自定義信號也在那里。由于其他對象也將是Area2D節點,所以你需要area_entered()信號。選擇它,然后單擊 “連接”。在連接信號窗口中點擊連接–你不需要改變任何這些設置。Godot會自動在您的腳本中創建一個名為_on_Player_area_entered()的新函數。

連接信號時,除了讓Godot為您創建函數外,您還可以提供要將信號鏈接到的現有函數的名稱。 如果您不希望Godot為您創建功能,請將Make Function開關切換到Off。

在這個新函數中加入以下代碼:

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()

當檢測到另一個Area2D時,它將被傳遞到函數中(使用area變量)。 硬幣對象將具有Pickup()函數,該函數定義拾取后硬幣的行為(例如播放動畫或聲音)。 創建硬幣和障礙物時,將它們分配給適當的組,以便可以檢測到它們。

總結一下,這是目前完整的玩家腳本:

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部分-硬幣場景

在這部分,你將制作硬幣供玩家收集。這將是一個單獨的場景,描述單個硬幣的所有屬性和行為。一旦保存,主場景將加載硬幣場景并創建多個實例(即副本)。

節點設置

點擊場景|新建場景,然后添加以下節點。不要忘記設置子節點不被選中,就像你在Player場景中做的那樣。

  • Area2D (named Coin)
  • AnimatedSprite
  • CollisionShape2D

添加節點后一定要保存場景。

像在player場景中那樣設置AnimatedSprite。這一次,你只有一個動畫:一個閃亮/閃光效果,使硬幣看起來不那么平坦和無聊。添加所有的幀,并將速度(FPS)設置為12。圖片有點大,所以將AnimatedSprite的Scale設置為(0.5,0.5)。在CollisionShape2D中,使用CircleShape2D,并將其大小覆蓋硬幣圖像。不要忘記:在確定碰撞形狀的大小時,千萬不要使用比例手柄。圓圈形狀有一個手柄,可以調整圓圈的半徑。

使用組

組為節點提供了標記系統,使您可以識別相似的節點。 一個節點可以屬于任意數量的組。 您需要確保所有硬幣都在一個名為“硬幣”的組中,以便player腳本對觸摸硬幣做出正確反應。 選擇“硬幣”節點,然后單擊“節點”選項卡(找到信號的相同選項卡),然后選擇“組”。 在框中輸入硬幣,然后單擊添加,如以下屏幕截圖所示:

腳本

接下來,在Coin節點上添加一個腳本。如果你在模板設置中選擇 “空”,Godot會創建一個沒有任何注釋或建議的空腳本。幣的腳本的代碼比玩家的代碼短得多:

extends Area2Dfunc pickup(): queue_free()

Pickup()函數由Player腳本調用,并告訴硬幣被收集后該怎么做。 queue_free()是Godot的節點刪除方法。 它安全地從樹中刪除該節點,并將其及其所有子節點從內存中刪除。 稍后,您將在此處添加視覺效果,但是到目前為止,消失的硬幣已經足夠了。

queue_free()不會立即刪除該對象,而是將其添加到要在當前幀末尾刪除的隊列中。 這比立即刪除該節點更安全,因為游戲中運行的其他代碼可能仍需要該節點存在。 通過等待直到幀結束,Godot可以確保可以訪問該節點的所有代碼均已完成,并且可以安全地刪除該節點。

第三部分–主場景

主場景是將游戲的所有部分聯系在一起的。它將管理玩家、硬幣、計時器和游戲的其他部分。

節點設置

創建一個新場景,并添加一個名為Main的節點。要將Player添加到場景中,點擊實例按鈕并選擇您保存的Player.tscn:

現在,添加以下節點作為Main的子節點,命名如下:

  • TextureRect (命名為Background)–用于背景圖片。
  • Node(命名為CoinContainer)–用于存放所有硬幣。
  • Position2D(命名為PlayerStart)–標記Player的起始位置。
  • Timer(命名為GameTimer)–用于跟蹤時間限制。

確保Background是第一個子節點。 節點按所示順序繪制,因此在這種情況下背景將位于Player后面。 通過將grass.png圖像從資產文件夾拖動到Texture屬性中,將圖像添加到Background節點。

將拉伸模式更改為平鋪,然后單擊布局| Full Rect將幀調整為屏幕大小,如以下屏幕截圖所示:

將PlayerStart節點的Position設置為(240,350)。

您的場景布局應如下所示:

主腳本

在Main節點中添加一個腳本(使用Empty模板)并添加以下變量:

extends Nodeexport (PackedScene) var Coin export (int) var playtimevar level var score var time_left var screensize var playing = false

現在,當您單擊Main時,Coin和Playtime屬性將顯示在檢查器中。 從“文件系統”面板中拖動Coin.tscn并將其放在Coin屬性中。

將“playtime”設置為30(這是游戲將持續的時間)。 其余的變量將在以后的代碼中使用。

初始化

接下來,添加_ready()函數:

func _ready(): randomize()screensize = get_viewport().get_visible_rect().size$Player.screensize = screensize$Player.hide()

在GDScript中,可以使用$通過名稱引用特定的節點。 這使您可以找到屏幕的大小并將其分配給Player的screensize變量。 hide()使玩家開始時不可見(您將使它們在游戲實際開始時顯示)。

符號中,節點名稱是相對于運行腳本的節點而言的。例如,符號中,節點名稱是相對于運行腳本的節點而言的。例如,Node1/Node2指的是Node1的子節點(Node2),而Node1本身就是當前運行腳本的子節點。Godot的自動完成功能會在您輸入時從樹中推薦節點名稱。請注意,如果節點的名稱包含空格,您必須在它周圍加上引號,例如,$“My Node”。

如果希望每次運行場景時“隨機”數字的序列都不同,則必須使用randomize()。 從技術上講,這為隨機數生成器選擇了一個隨機種子。

開始新游戲

接下來,new_game()函數將初始化一個新游戲的一切。

func new_game(): playing = true level = 1score = 0time_left = playtime$Player.start($PlayerStart.position)$Player.show()$GameTimer.start() spawn_coins()

除了將變量設置為其初始值之外,此函數還調用Player的start()函數以確保其移至正確的起始位置。 啟動游戲計時器,它將倒計時游戲中的剩余時間。

您還需要一個函數,該函數將根據當前級別創建若干硬幣:

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))

在這個函數中,你創建了許多Coin對象的實例(這次是在代碼中,而不是通過點擊Instance a Scene按鈕),并將其添加為CoinContainer的子節點。每當你實例一個新的節點時,必須使用add_child()將其添加到樹中。最后,你為硬幣選擇一個隨機的位置。你會在每個關卡開始時調用這個函數,每次產生更多的硬幣。

最終,你會希望new_game()在玩家點擊開始按鈕時被調用。現在,為了測試一切是否正常,將new_game()添加到你的_ready()函數的結尾,然后點擊播放項目(F5)。當你被提示選擇一個主場景時,選擇Main.tscn。現在,每當你播放項目時,主場景就會被啟動。

這時,你應該看到你的玩家和五個硬幣出現在屏幕上。當玩家觸摸到一枚硬幣時,它就會消失。

檢查剩余硬幣

主腳本需要檢測玩家是否撿到了所有的硬幣。由于硬幣都是CoinCointainer的子節點,你可以在這個節點上使用get_child_count()來找出還剩下多少硬幣。把這個放在_process()函數中,這樣每一幀都會被檢查。

func _process(delta):if playing and $CoinContainer.get_child_count() == 0: level += 1time_left += 5 spawn_coins()

如果沒有更多的硬幣剩余,那么玩家就會進入下一關。

第4部分–用戶界面

你的游戲需要的最后一塊是用戶界面(UI)。這是一個用于顯示玩家在游戲過程中需要看到的信息的界面,在游戲中,這也被稱為抬頭顯示器(HUD),因為信息是以覆蓋在游戲視圖之上的方式出現的。在游戲中,這也被稱為抬頭顯示器(HUD),因為這些信息是以疊加的形式出現在游戲視圖之上的。你也會用這個場景來顯示一個開始按鈕。

HUD將顯示以下信息:

  • 分數
  • 剩余時間
  • 一個信息,如游戲結束
  • 一個啟動按鈕

節點設置

創建一個新場景,并添加一個名為HUD的CanvasLayer節點。CanvasLayer節點允許你在游戲的其他部分之上的一層上繪制你的UI元素,這樣它所顯示的信息就不會被任何游戲元素(如玩家或硬幣)所覆蓋。

Godot提供了各種各樣的UI元素,可以用來創建任何東西,從生命條等指標到庫存等復雜界面。事實上,你用來制作這個游戲的Godot編輯器就是使用這些元素在Godot中構建的。UI元素的基本節點是從Control擴展而來的,并在節點列表中以綠色圖標出現。為了創建你的UI,你將使用各種Control節點來定位、格式化和顯示信息。下面是HUD完成后的樣子。

錨和邊距

控制節點有一個位置和大小,但它們也有稱為錨和邊距的屬性。錨定義了節點的邊緣相對于父容器的原點或參考點。邊距表示從控制節點的邊緣到其相應錨點的距離。當您移動或調整控制節點的大小時,邊距會自動更新。

信息標簽

在場景中添加一個Label節點,并將其名稱改為MessageLabel。這個標簽將顯示游戲的標題,以及游戲結束時的Game Over。這個標簽應該在游戲屏幕上居中。你可以用鼠標拖動它,但為了精確地放置UI元素,你應該使用Anchor屬性。

選擇 “視圖”|"顯示助手 "來顯示有助于查看錨點位置的圖釘,然后單擊 "布局 "菜單并選擇 “HCenter Wide”。

現在,MessageLabel跨過屏幕的寬度并垂直居中。 檢查器中的“文本”屬性設置標簽顯示的文本。將其設置為 Coin Dash!,并將 Align 和 Valign 設置為 Center。

Label節點的默認字體非常小,所以下一步就是分配一個自定義字體。向下滾動到 "檢查器 "中的 "自定義字體 "部分,選擇 “新建動態字體”(New DynamicFont),如下截圖所示:

現在,點擊DynamicFont,您可以調整字體設置。從FileSystem dock中,拖動Kenney Bold.ttf字體,并將其放入字體數據屬性中。設置大小為48,如下截圖所示:

分數和時間顯示

HUD的頂部將顯示玩家的分數和剩余時間。這兩個都將是Label節點,位于游戲屏幕的相對兩側。你將使用容器節點來管理它們的位置,而不是將它們分開放置。

容器

UI容器會自動安排其子控件節點(包括其他容器)的位置。您可以使用它們在元素周圍添加padding,將它們居中,或將元素按行或列排列。每種類型的容器都有特殊的屬性來控制它們如何安排其子節點。您可以在 "檢查器 "的 "自定義常量 "部分看到這些屬性。

UI容器會自動安排其子控件節點(包括其他容器)的位置。您可以使用它們在元素周圍添加padding,將它們居中,或將元素按行或列排列。每種類型的容器都有特殊的屬性來控制它們如何安排其子節點。您可以在 "檢查器 "的 "自定義常量 "部分看到這些屬性。

要管理分數和時間標簽,在HUD中添加一個MarginContainer節點。使用 "布局 "菜單將錨點設置為頂部寬。在 "自定義常量 "部分,將 “右邊距”、"頂部邊距 "和 "左邊距 "設置為10。這將增加一些padding,這樣文字就不會貼著屏幕的邊緣了。

由于分數和時間標簽將使用與MessageLabel相同的字體設置,所以如果你復制它將節省時間。點擊MessageLabel并按Ctrl + D(macOS上為Cmd + D)兩次來創建兩個重復的標簽。將它們都拖拽到MarginContainer上,使其成為它的子節點。命名一個 ScoreLabel 和另一個 TimeLabel,并將兩個標簽的 Text 屬性設置為 0。將 ScoreLabel 的 Align 設置為 Left,TimeLabel 的 Align 設置為 Right。

通過GDScript更新UI

將腳本添加到HUD節點。 例如,此腳本將在需要更改UI元素的屬性時更新UI元素,例如,每當收集硬幣時都會更新分數文本。 請參考以下代碼:

extends CanvasLayer signal start_gamefunc update_score(value):$MarginContainer/ScoreLabel.text = str(value)func update_timer(value):$MarginContainer/TimeLabel.txt = str(value)

主場景的腳本將調用這些函數來更新顯示,每當值有變化時,就會更新。對于MessageLabel,你還需要一個定時器來使它在短暫的時間后消失。添加一個定時器節點,并將其名稱改為MessageTimer。在 "檢查器 "中,將其 "等待時間 "設置為2秒,并勾選 "One Shot "設置為 “On”。這樣可以確保在啟動時,定時器只運行一次,而不是重復運行。添加以下代碼。

func show_message(text):$MessageLabel.text = text$MessageLabel.show()$MessageTimer.start()

在這個函數中,你可以顯示消息并啟動定時器。要隱藏消息,連接MessageTimer的timeout()信號,并添加這個:

func _on_MessageTimer_timeout():$MessageLabel.hide()

使用按鈕

添加一個Button節點,并將其名稱改為StartButton。這個按鈕將在游戲開始前顯示,當點擊時,它將隱藏自己,并向主場景發送一個信號來啟動游戲。將Text屬性設置為Start,并像對MessageLabel那樣改變自定義字體。在 "布局 "菜單中,選擇 “中心底部”。這將使按鈕位于屏幕的最底部,因此可以通過按向上箭頭鍵或編輯頁邊距并將Top設置為-150,Bottom設置為-50來將其向上移動一點。

當一個按鈕被點擊時,會發出一個信號。在StartButton的節點標簽中,連接pressed()信號:

func _on_StartButton_pressed():$StartButton.hide()$MessageLabel.hide() emit_signal("start_game")

HUD發出start_game信號,通知Main該開始新游戲了。

游戲結束

你的UI的最后任務是對游戲結局做出反應:

func show_game_over(): show_message("Game Over") yield($MessageTimer, "timeout")$StartButton.show()$MessageLabel.text = "Coin Dash!"$MessageLabel.show()

在這個函數中,你需要Game Over消息顯示兩秒鐘,然后消失,這就是show_message()的作用。然而,你也希望在消息消失后顯示開始按鈕。yield()函數暫停執行函數,直到給定節點(MessageTimer)發出給定信號(超時)。一旦接收到信號,函數就會繼續,將你返回到初始狀態,這樣你就可以再次播放。

將HUD添加到Main

現在,你需要設置主場景和HUD之間的通信。在Main場景中添加一個HUD場景的實例。在Main場景中,連接GameTimer的timeout()信號并添加以下內容:

func _on_GameTimer_timeout(): time_left -= 1$HUD.update_timer(time_left) if time_left <= 0:game_over()

每當GameTimer超時(每秒鐘),剩余時間就會減少。接下來,連接Player的pickup()和hurt()信號:

func _on_Player_pickup(): score += 1$HUD.update_score(score)func _on_Player_hurt(): game_over()

當游戲結束時,需要發生幾件事,所以添加以下功能:

func game_over(): playing = false$GameTimer.stop()for coin in $CoinContainer.get_children(): coin.queue_free()$HUD.show_game_over()$Player.die()

此函數暫停游戲,還循環遍歷硬幣并刪除剩余的硬幣,以及調用HUD的show_game_over()函數。

最后,StartButton需要激活new_game()函數。點擊HUD實例,選擇其new_game()信號。在信號連接對話框中,單擊Make Function to Off,并在Method In Node字段中,鍵入new_game。這將把信號連接到現有的函數,而不是創建一個新的函數。請看下面的截圖:

從_ready()函數中刪除new_game()并將這兩行添加到new_game()函數中:

$HUD.update_score(score) $HUD.update_timer(time_left)

現在,你可以玩這個游戲了 確認所有的部分都能按預期工作:分數、倒計時、游戲結束和重新開始等。如果你發現有一個部分不工作,回去檢查你創建它的步驟,以及它與游戲其他部分連接的步驟。

第5部分-完成

您已經創建了一個可運行的游戲,但是仍然可以使它變得更加令人興奮。 游戲開發人員使用“juice”一詞來描述使游戲感覺良好的事物。 果汁可以包括聲音,視覺效果或其他任何可以增加玩家娛樂性的內容,而不必改變游戲性。

在本節中,您將添加一些小juice來完成游戲。

視覺效果

當您拾起硬幣時,它們就會消失,這并不是很吸引人。 添加視覺效果會使收集大量硬幣變得更加令人滿意。

首先在Coin場景中添加一個Tween節點。

什么是補間?

tween是一種使用特定函數對一些值進行插值(逐漸改變)的方法(從一個起始值到一個結束值)。例如,你可以選擇一個穩定地改變數值的函數,或者一個開始時很慢但速度逐漸加快的函數。Tweening有時也被稱為緩動。

當在Godot中使用Tween節點時,你可以指定它來改變一個節點的一個或多個屬性。在本例中,你將增加硬幣的Scale,同時使用Modulate屬性使其淡出。

在Coin的_ready()函數中添加這一行:

$Tween.interpolate_property($AnimatedSprite, 'scale',$AnimatedSprite.scale,$AnimatedSprite.scale * 3, 0.3,Tween.TRANS_QUAD,Tween.EASE_IN_OUT)

interpolate_property()函數使Tween改變一個節點的屬性。有七個參數:

  • 要影響的節點
  • 需要更改的屬性
  • 屬性的初始值
  • 屬性的最終值
  • 持續時間(以秒為單位)
  • 使用的功能
  • 方向

當玩家拿起硬幣時,tween應該開始播放。替換pickup()函數中的queue_free():

func pickup(): monitoring = false$Tween.start()

將監控設置為false,可以確保在tween動畫中,如果玩家觸摸到硬幣,則不會發出area_enter()信號。

最后,當動畫結束時,硬幣應該被刪除,所以連接Tween節點的tween_completed()信號:

func _on_Tween_tween_completed(object, key): queue_free()

現在,當你運行游戲時,你應該看到硬幣在撿到時越來越大。這是很好的,但是當同時應用于多個屬性時,tweens會更加有效。你可以添加另一個interpolate_property(),這次是為了改變精靈的不透明度。這是通過改變調制屬性來實現的,調制屬性是一個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)

聲音

聲音是游戲設計中最重要但經常被忽視的部分之一。好的聲音設計可以為你的游戲增加大量的juice,而你只需付出很小的努力.聲音可以給玩家反饋,將他們與角色的情感聯系起來,甚至成為游戲玩法的一部分。

在這個游戲中,你要添加三種音效。在主場景中,添加三個AudioStreamPlayer節點,并將它們命名為CoinSound、LevelSound和EndSound。將音頻文件夾中的每一個聲音(你可以在FileSystem dock中的assets下找到它)拖到每個節點的相應Stream屬性中。

要播放聲音,請在其上調用play()函數。 將$ CoinSound.play()添加到_on_Player_pickup()函數,將$ EndSound.play()添加到game_over()函數,將$ LevelSound.play()添加到spawn_coins()函數。

提升能力

物品有很多可能會給玩家帶來小小的優勢或力量。 在本部分中,您將添加一個能量道具,使玩家在收集時獲得少量時間加成。 偶爾會出現一小段時間,然后消失。

新的場景將與你已經創建的硬幣場景非常相似,所以點擊你的硬幣場景,選擇場景|另存為,并將其保存為Powerup.tscn。將根節點的名稱改為Powerup,并通過點擊清除腳本按鈕刪除腳本: 。你還應該斷開 area_entered 信號(你以后會重新連接)。在 "組 "選項卡中,通過點擊刪除按鈕(它看起來像一個垃圾桶)刪除硬幣組,并將其添加到一個名為Powerups的新組中代替。

在AnimatedSprite中,將硬幣的圖片改為powerup,你可以在res://assets/pow/文件夾中找到。

點擊添加一個新的腳本,并復制Coin.gd腳本中的代碼。將_on_Coin_area_entered的名稱改為_on_Powerup_area_entered,再將area_entered信號連接到它。記住,這個函數名將由信號連接窗口自動選擇。

接下來,添加一個名為Lifetime的Timer節點。這將限制對象在屏幕上停留的時間。將其等待時間設置為2,將One Shot和Autostart都設置為On。連接它的超時信號,這樣就可以在時間段結束時將其刪除:

func _on_Lifetime_timeout(): queue_free()

現在,轉到您的Main場景并添加另一個名為PowerupTimer的Timer節點。 將其“One Shot”屬性設置為“開”。 您可以使用另一個AudioStreamPlayer添加音頻文件夾中的Powerup.wav聲音。

連接超時信號,并添加以下代碼來生成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場景需要通過添加一個變量來鏈接,然后在Inspector中拖動場景到屬性中,就像你之前對Coin場景所做的那樣:

export (PackedScene) var Powerup

能量的出現應該是不可預知的,所以每當你開始一個新的關卡時,需要設置PowerupTimer的等待時間。在用 spawn_coins()產生新的硬幣后,將其添加到 _process()函數中:

$PowerupTimer.wait_time = rand_range(5, 10) $PowerupTimer.start()

現在,你將會有能量出現,最后一步就是當收集到一個加電時,給玩家一些獎勵時間。目前,玩家腳本假設它碰到的任何東西不是硬幣就是障礙物。修改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()

請注意,現在你發出的拾取信號帶有一個額外的參數,命名對象的類型。現在,Main.gd中的相應函數可以被修改為接受該參數,并使用匹配語句來決定采取什么行動:

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語句的有用替代方法,尤其是當您要測試大量可能的值時。

嘗試運行游戲并收集能量。 確保聲音播放并且計時器增加五秒鐘。

硬幣動畫

創建硬幣場景時,添加了AnimatedSprite,但尚未播放。 硬幣動畫顯示在硬幣表面傳播的閃光效果。 如果所有硬幣同時顯示,則看起來太規則了,因此每個硬幣在動畫中需要一個小的隨機延遲。

首先,點擊AnimatedSprite,然后點擊Frames資源。確保Loop被設置為Off,Speed被設置為12。

在Coin場景中添加一個Timer節點,并在_ready()中添加這段代碼:

$Timer.wait_time = rand_range(3, 8) $Timer.start()

現在,連接定時器的timeout()信號,并添加這個:

func _on_Timer_timeout():$AnimatedSprite.frame = 0$AnimatedSprite.play()

試著運行游戲,觀察硬幣的動畫。這是一個很好的視覺效果,只需要付出很小的努力。在專業游戲中,你會注意到很多這樣的效果。雖然非常微妙,但視覺上的吸引力讓人有更愉悅的體驗。

前面的Powerup對象有一個類似的動畫,你可以用同樣的方式添加。

障礙物

最后,可以通過引入一個玩家必須避開的障礙物,讓游戲更具挑戰性。碰到障礙物就會結束游戲。

為仙人掌創建一個新場景,并添加以下節點。

  • Area2D (named Cactus)
  • Sprite
  • CollisionShape2D

將仙人掌紋理從FileSystem停靠點拖到Sprite的Texture屬性。 將RectangleShape2D添加到碰撞形狀并調整其大小,以使其覆蓋圖像。 還記得您是否在播放器腳本中添加了area.is_in_group(“ obstacless”)嗎? 使用“節點”選項卡(“檢查器”旁邊)將仙人掌主體添加到障礙物組。

現在,將仙人掌實例添加到主場景中,并將其移動到屏幕上半部的某個位置(遠離Player生成的位置)。 玩游戲,看看碰到仙人掌會發生什么。

您可能已經發現了一個問題:硬幣會在仙人掌后面產生,因此無法撿起。 放置硬幣后,如果發現硬幣與障礙物重疊,則需要移動硬幣。 連接硬幣的area_entered()信號并添加以下內容:

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信號執行相同的操作。

總結

在本章中,您通過創建一個基本的2D游戲來學習Godot Engine的基礎知識。您設置了項目并創建了多個場景,使用精靈和動畫,捕獲用戶輸入,使用信號與事件通信,并使用控制節點創建了UI。你在這里學到的東西是重要的技能,你將在任何Godot項目中使用。

在進入下一章之前,先看一下這個項目。你明白每個節點在做什么嗎?是否有任何你不理解的代碼部分?如果有,請回過頭來復習本章的那部分內容。

另外,可以自由地對游戲進行實驗,改變一些東西。最好的方法之一是改變它們,看看會發生什么,從而很好地感受到游戲的不同部分正在做什么。

在下一章中,你將探索更多Godot的功能,并學習如何通過構建一個更復雜的游戲來使用更多的節點類型。

總結

以上是生活随笔為你收集整理的第二章-Coin Dash的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。