第1部分 - 玩家場景
您將要制作的第一個場景定義了Player對象曹货。創(chuàng)建單獨的玩家場景的好處之一是,您可以獨立測試它婴谱,甚至在您創(chuàng)建游戲的其他部分之前甜橱。隨著項目規(guī)模和復(fù)雜性的增加,游戲?qū)ο蟮倪@種分離將變得越來越有用癣缅。保持單個游戲?qū)ο蟊舜朔蛛x使得它們更容易進行故障排除厨剪,修改甚至完全替換,而不會影響游戲的其他部分友存。它還可以使您的Player重復(fù)使用 - 您可以將Player場景放入完全不同的游戲中祷膳,它也可以正常工作。
Player場景將顯示您的角色及其動畫屡立,通過相應(yīng)地移動角色來響應(yīng)用戶輸入直晨,并檢測與游戲中其他對象的碰撞。
創(chuàng)建場景
首先單擊Add/Create創(chuàng)建新節(jié)點按鈕膨俐,然后選擇Area2D勇皇。然后,單擊其名稱并將其更改為Player焚刺。單擊Sence|Save Sence以保存場景敛摘。這是場景的root根節(jié)點或top-level頂級節(jié)點。您可以通過向此節(jié)點添加子項來向Player添加更多功能:
在添加任何子項之前乳愉,最好確保不要通過單擊它們來移動或調(diào)整它們的大小兄淫。選擇Player節(jié)點,然后單擊旁邊的鎖圖標:
工具提示將說明確保對象的子項不可選蔓姚,如上面的屏幕截圖所示捕虽。
提示
在創(chuàng)建新場景時始終執(zhí)行此操作是個好主意。如果主體的碰撞形狀或子畫面變得偏移或縮放坡脐,則可能導(dǎo)致意外錯誤并且難以修復(fù)泄私。使用此選項,節(jié)點及其所有子節(jié)點將始終一起移動。
精靈動畫
使用Area2D挖滤,您可以檢測其他對象何時重疊或運行到player中崩溪,但Area2D本身沒有外觀,因此單擊Player節(jié)點并將AnimatedSprite節(jié)點添加為子節(jié)點斩松。 AnimatedSprite將處理player的外觀和動畫伶唯。請注意,節(jié)點旁邊有一個警告符號惧盹。 AnimatedSprite 需要一個SpriteFrames資源乳幸,該資源包含它可以顯示的動畫。要創(chuàng)建一個钧椰,請在Inspector中找到Frames屬性粹断,然后單擊<null> |New SpriteFrames:
接下來,在同一位置嫡霞,點擊<SpriteFrames>以打開SpriteFrames面板:
左側(cè)是動畫列表瓶埋。單擊默認值并將其重命名為run。然后诊沪,單擊Add按鈕养筒,創(chuàng)建第二個名為idle的動畫,第三個名為hurt端姚。
在左側(cè)的FileSystem驮畏啵靠欄中,找到run(跑)渐裸,idle(待機)和hurt(受傷)的玩家圖像并將其拖動到相應(yīng)的動畫中:
每個動畫的默認速度設(shè)置為每秒5幀巫湘。這有點太慢,因此請單擊每個動畫并將Speed(FPS)設(shè)置為8昏鹃。在Inspector中尚氛,選中Playing屬性旁邊的On,然后選擇Animation以查看動畫中的動畫:
稍后盆顾,您將編寫代碼以在這些動畫之間進行選擇怠褐,具體取決于玩家正在做什么畏梆。但首先您宪,您需要完成設(shè)置player的節(jié)點。
碰撞形狀
當使用Area2D或Godot中的其他碰撞對象之一時奠涌,它需要定義一個形狀宪巨。碰撞形狀定義對象占據(jù)的區(qū)域,并用于檢測重疊或碰撞溜畅。形狀由Shape2D定義捏卓,包括矩形,圓形,多邊形和其他類型的形狀怠晴。
為方便起見遥金,當您需要向區(qū)域或物理主體添加形狀時,可以將CollisionShape2D添加為子項蒜田。然后稿械,您可以選擇所需的形狀類型,并可以在編輯器中編輯其大小冲粤。
添加CollisionShape2D作為Player的子節(jié)點(確保不將其添加為AnimatedSprite的子節(jié)點)美莫。這將允許您確定玩家的hitbox或碰撞區(qū)域的邊界。在Inspector中梯捕,單擊Shape旁邊的< null >并選擇New RectangleShape2D倒得。調(diào)整形狀的大小以覆蓋精靈:
提示
小心不要縮放形狀的輪廓驹暑!只能使用尺寸手柄(紅圈)來調(diào)整形狀!對于縮放的碰撞形狀,碰撞將無法正常工作惨恭。
您可能已經(jīng)注意到碰撞形狀不是以精靈為中心。那是因為精靈本身不是垂直居中的登舞。我們可以通過向AnimatedSprite添加一個小偏移來解決這個問題菲驴。單擊節(jié)點并在Inspector中查找Offset屬性。將其設(shè)置為( 0错英,-5 )入撒。
完成后,您的Player場景應(yīng)如下所示:
編寫Player腳本
現(xiàn)在椭岩,您已準備好添加腳本茅逮。腳本允許您添加內(nèi)置節(jié)點未提供的其他功能。單擊Player節(jié)點判哥,然后單擊Add Script按鈕:
在Script Settings窗口中献雅,您可以保留默認設(shè)置。如果您記得保存場景(請參見上面的屏幕截圖)塌计,腳本將自動命名以匹配場景的名稱挺身。單擊Create,您將進入腳本窗口锌仅。您的腳本將包含一些默認注釋和提示章钾。您可以刪除注釋(以#開頭的行)。請參閱以下代碼段:
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 here
pass
#func _process(delta):
# # Called every frame. Delta is time since last frame.
# # Update game logic here.
# pass
每個腳本的第一行將描述它所附加的節(jié)點類型热芹。接下來贱傀,您將定義類變量:
extends Area2D
export (int) var speed
var velocity = Vector2()
var screensize = Vector2(480, 720)
文檔
Vector2
在speed變量上使用export關(guān)鍵字允許您在Inspector中設(shè)置其值,并讓Inspector知道變量應(yīng)包含哪種類型的數(shù)據(jù)伊脓。這對于您希望能夠調(diào)整的值非常方便府寒,就像調(diào)整節(jié)點的內(nèi)置屬性一樣。單擊Player節(jié)點并將Speed屬性設(shè)置為350,如以下屏幕截圖所示:
velocity將包含角色當前的移動速度和方向株搔,screenize將用于設(shè)置玩家移動的極限(屏幕大衅实怼)。之后纤房,游戲的主場景將設(shè)置此變量祷蝌,但是現(xiàn)在您將手動設(shè)置它以便進行測試。
Player移動
接下來帆卓,您將使用_process()
函數(shù)來定義player將執(zhí)行的操作巨朦。每個幀都會調(diào)用_process()
函數(shù),因此您將使用它來更新您希望經(jīng)常更改的游戲元素剑令。你需要玩家做三件事:
- 監(jiān)測鍵盤輸入
- 沿指定方向移動
- 播放相應(yīng)的動畫
首先糊啡,您需要監(jiān)測輸入。對于這個游戲吁津,你有四個方向輸入來監(jiān)測(四個箭頭鍵)棚蓄。輸入操作在Project Settings | Input Map選項卡下的項目設(shè)置中定義。在此選項卡中碍脏,您可以定義自定義事件并為其分配不同的鍵梭依,鼠標操作或其他輸入。默認情況下典尾,Godot將事件分配給鍵盤箭頭役拴,因此您可以將它們用于此項目。
您可以使用Input.is_action_pressed()
檢測是否按下了輸入钾埂,如果按住鍵則返回true河闰,否則返回false。結(jié)合所有四個按鈕的狀態(tài)將為您提供合成的移動方向褥紫。例如姜性,如果同時right和down按住,則生成的速度矢量將為(1,1)髓考。在這種情況下部念,由于我們一起添加水平和垂直移動,因此玩家移動的速度比他們剛剛水平移動的速度快氨菇。
您可以通過標準化速度來防止這種情況儡炼,這意味著將其長度設(shè)置為1,然后將其乘以所需的速度:
func get_input():
velocity = Vector2()
if Input.is_action_pressed("ui_left"):
velocity.x -= 1
if Input.is_action_pressed("ui_right"):
velocity.x += 1
if Input.is_action_pressed("ui_up"):
velocity.y -= 1
if Input.is_action_pressed("ui_down"):
velocity.y += 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
通過在get_input()
函數(shù)中將所有這些代碼組合在一起门驾,您可以更輕松地在以后更改內(nèi)容射赛。例如,您可以決定更改為模擬操縱桿或其他類型的控制器奶是。從_process()
調(diào)用此函數(shù),然后通過生成的velocity更改player的position。為防止player離開屏幕聂沙,您可以使用clamp()
函數(shù)將位置限制為最小值和最大值:
func _process(delta):
get_input()
position += velocity * delta
position.x = clamp(position.x, 0, screensize.x)
position.y = clamp(position.y, 0, screensize.y)
單擊運行Edited Scene(F6)并確認您可以在所有方向上圍繞屏幕移動player秆麸。
關(guān)于delta
_process()
函數(shù)包含一個名為delta的參數(shù),然后乘以速度及汉。什么是delta沮趣?
游戲引擎嘗試以每秒60幀的速度運行。但是坷随,由于計算機速度降低房铭,無論是在godot還是從計算機本身,這都會發(fā)生變化温眉。如果幀速率不一致缸匪,則會影響游戲?qū)ο蟮囊苿印@缋嘁纾紤]將對象設(shè)置為每幀移動10個像素凌蔬。如果一切順利進行,這將轉(zhuǎn)化為在一秒鐘內(nèi)移動600像素闯冷。但是砂心,如果這些幀中的某些幀需要更長時間,那么在那一秒中可能只有50幀蛇耀,因此對象僅移動了500個像素辩诞。
和大多數(shù)游戲引擎和框架一樣,Godot通過傳遞delta來解決這個問題纺涤,delta是自上一幀以來經(jīng)過的時間躁倒。大多數(shù)情況下,這將是大約0.016秒(或大約16毫秒)洒琢。如果你然后采取你想要的速度(600 px / s)并乘以delta秧秉,你將獲得正好10的運動。但是衰抑,如果增量增加到0.3象迎,則對象將移動18個像素∏河唬總的來說砾淌,移動速度保持一致并且與幀速率無關(guān)。
作為附帶好處谭网,您可以以px/s為單位表示移動汪厨,而不是px/frame,這更容易可視化愉择。
動畫選擇
現(xiàn)在玩家可以移動劫乱,你需要根據(jù)它是移動還是靜止來改變AnimatedSprite正在播放的動畫织中。run動畫面向右側(cè),這意味著它向左移動(使用Flip H屬性)應(yīng)該水平翻轉(zhuǎn)衷戈。將其添加到_process()
函數(shù)的末尾:
if velocity.length() > 0:
$AnimatedSprite.animation = "run"
$AnimatedSprite.flip_h = velocity.x < 0
else:
$AnimatedSprite.animation = "idle"
請注意狭吼,此代碼有一點縮減。* flip_h是一個布爾屬性殖妇,這意味著它可以是true或false刁笙。布爾值也是像<*的比較結(jié)果。因此谦趣,我們可以將屬性設(shè)置為等于比較結(jié)果疲吸。這一行相當于寫出來像這樣:
if velocity.x < 0:
$AnimatedSprite.flip_h = true
else:
$AnimatedSprite.flip_h = false
再次啟動場景并檢查動畫在每種情況下是否正確。確保在AnimatedSprite中將Playing設(shè)置為On前鹅,以便播放動畫摘悴。
開始和結(jié)束玩家的運動
當游戲開始時,主場景將需要通知玩家游戲已經(jīng)開始嫡纠。添加start()
函數(shù)如下烦租,主場景將用于設(shè)置玩家的起始動畫和位置:
func start(pos):
set_process(true)
position = pos
$AnimatedSprite.animation = "idle"
當玩家遇到障礙物或時間不足時,將調(diào)用die()
函數(shù):
func die():
$AnimatedSprite.animation = "hurt"
set_process(false)
設(shè)置set_process(false)
會導(dǎo)致不再為此節(jié)點調(diào)用_process()
函數(shù)除盏。這樣叉橱,當玩家死亡時,他們?nèi)匀粺o法通過鍵輸入來移動者蠕。
準備碰撞
玩家應(yīng)該檢測到它何時觸碰硬幣或障礙物窃祝,但是你還沒有讓它們這樣做。沒關(guān)系踱侣,因為你可以使用Godot的signal(信號)功能來使它工作粪小。Signal是節(jié)點發(fā)送其他節(jié)點可以檢測和響應(yīng)的消息的一種方式。例如抡句,當按下按鈕時探膊,許多節(jié)點都有內(nèi)置信號來提醒您身體發(fā)生碰撞時。您還可以為自己的目的定義自定義信號待榔。
通過將信號連接到您想要監(jiān)聽和響應(yīng)的節(jié)點來使用信號逞壁。可以在Inspector或代碼中進行此連接锐锣。在項目的后期腌闯,您將學(xué)習(xí)如何以兩種方式連接信號。
將以下內(nèi)容添加到腳本的頂部(在extends Area2D之后):
signal pickup
signal hurt
這些定義了玩家在觸摸硬幣或障礙物時會觸發(fā)(發(fā)出)自定義信號雕憔。觸摸將由Area2D本身檢測到姿骏。選擇Player節(jié)點,然后單擊Inspector旁邊的Node選項卡以查看player可以觸發(fā)的信號列表:
請注意您的自定義信號也在那里斤彼。由于其他對象也將是Area2D節(jié)點分瘦,因此您需要area_entered()
信號蘸泻。選擇它并單擊Connect。單擊Connecting Signal窗口上的Connect - 您無需更改任何這些設(shè)置擅腰。 Godot會在你的腳本中自動創(chuàng)建一個名為_on_Player_area_entered()
的新函數(shù)蟋恬。
提示
連接信號時翁潘,您也可以給出要將信號鏈接到現(xiàn)有函數(shù)的名稱趁冈,而不是讓Godot為您創(chuàng)建默認函數(shù)。如果您不希望Godot為您創(chuàng)建功能拜马,請將Make Function開關(guān)切換為Off渗勘。
將以下代碼添加到此新函數(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()
當檢測到另一個Area2D時,它將被傳遞給該函數(shù)(使用area變量)俩莽。硬幣對象將具有pick()
功能旺坠,該功能定義拾取時硬幣的行為(例如,播放動畫或聲音)扮超。當您創(chuàng)建硬幣和障礙物時取刃,您將它們分配給相應(yīng)的組,以便可以檢測到它們出刷。
總而言之璧疗,到目前為止,這是完整的player腳本
extends Area2D
signal pickup
signal hurt
export (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 -= 1
if Input.is_action_pressed("ui_right"):
velocity.x += 1
if Input.is_action_pressed("ui_up"):
velocity.y -= 1
if Input.is_action_pressed("ui_down"):
velocity.y += 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
func _process(delta):
get_input()
position += velocity * delta
position.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()