Game1硬幣收集 - 第1部分 玩家場景 - Godot引擎游戲開發(fā)項目實踐

第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添加更多功能:

新建節(jié)點

在添加任何子項之前乳愉,最好確保不要通過單擊它們來移動或調(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

點擊<null> |New SpriteFrames

接下來,在同一位置嫡霞,點擊<SpriteFrames>以打開SpriteFrames面板:

SpriteFrames面板

左側(cè)是動畫列表瓶埋。單擊默認值并將其重命名為run。然后诊沪,單擊Add按鈕养筒,創(chuàng)建第二個名為idle的動畫,第三個名為hurt端姚。

在左側(cè)的FileSystem驮畏啵靠欄中,找到run(跑)渐裸,idle(待機)和hurt(受傷)的玩家圖像并將其拖動到相應(yīng)的動畫中:

Animation Frames

每個動畫的默認速度設(shè)置為每秒5幀巫湘。這有點太慢,因此請單擊每個動畫并將Speed(FPS)設(shè)置為8昏鹃。在Inspector中尚氛,選中Playing屬性旁邊的On,然后選擇Animation以查看動畫中的動畫:

設(shè)置動畫精靈屬性

稍后盆顾,您將編寫代碼以在這些動畫之間進行選擇怠褐,具體取決于玩家正在做什么畏梆。但首先您宪,您需要完成設(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場景

編寫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,如以下屏幕截圖所示:

設(shè)置Speed速度

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)將為您提供合成的移動方向褥紫。例如姜性,如果同時rightdown按住,則生成的速度矢量將為(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

文檔
Input
Input is_action_pressed
Vector2 normalized()

通過在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)

文檔
clamp
Node._process()

單擊運行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是一個布爾屬性殖妇,這意味著它可以是truefalse刁笙。布爾值也是像<*的比較結(jié)果。因此谦趣,我們可以將屬性設(shè)置為等于比較結(jié)果疲吸。這一行相當于寫出來像這樣:

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

再次啟動場景并檢查動畫在每種情況下是否正確。確保在AnimatedSprite中將Playing設(shè)置為On前鹅,以便播放動畫摘悴。

文檔
AnimatedSprite.flip_h


開始和結(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()

文檔
Object.emit_signal()

當檢測到另一個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()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末馁龟,一起剝皮案震驚了整個濱河市崩侠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坷檩,老刑警劉巖却音,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異矢炼,居然都是意外死亡系瓢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門句灌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夷陋,“玉大人,你說我怎么就攤上這事涯塔〖〉荆” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵匕荸,是天一觀的道長爹谭。 經(jīng)常有香客問我,道長榛搔,這世上最難降的妖魔是什么诺凡? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任东揣,我火速辦了婚禮,結(jié)果婚禮上腹泌,老公的妹妹穿的比我還像新娘嘶卧。我一直安慰自己,他們只是感情好凉袱,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布芥吟。 她就那樣靜靜地躺著,像睡著了一般专甩。 火紅的嫁衣襯著肌膚如雪钟鸵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天涤躲,我揣著相機與錄音棺耍,去河邊找鬼。 笑死种樱,一個胖子當著我的面吹牛蒙袍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嫩挤,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼害幅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了俐镐?” 一聲冷哼從身側(cè)響起矫限,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎佩抹,沒想到半個月后叼风,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡棍苹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年无宿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枢里。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡孽鸡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出栏豺,到底是詐尸還是另有隱情彬碱,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布奥洼,位于F島的核電站巷疼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏灵奖。R本人自食惡果不足惜嚼沿,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一估盘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧骡尽,春花似錦遣妥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辨图,卻和暖如春班套,著一層夾襖步出監(jiān)牢的瞬間肢藐,已是汗流浹背故河。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吆豹,地道東北人鱼的。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像痘煤,于是被迫代替她去往敵國和親凑阶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355