ETH官方钱包

前往
大廳
主題 達人專欄

【Godot 教學】製作你的第一個 2D 遊戲:主場景、敵人- EP7

Muryan | 2023-10-18 20:30:02 | 巴幣 2114 | 人氣 1013

前言


上一回我們讓飛船可以發(fā)射子彈。
這回我們要做得分為兩個部分——主場景和敵人。


開始之前讓我再稍微宣傳一下。
Godot 開拓者交流互助公會 無論是新手還是有經(jīng)驗的 Godot 使用者,都歡迎你的參與。

開拓者交流互助公會

主場景


建立主場景:
新建一個名為 "Main" 的 Node2D 作為主場景。


建立背景:
接著我們再開一個新的 Scene,增加 Sprite2D 將其命名為 "Background",並將 Space_BG (2 frames) (64 x 64).png 拉到 Texture


設定背景:

Background 的設定中,將 OffsetCentered 設置為 "off",使圖片的左上角從原點開始,而不是從其中心開始。


Region 中,啟用 Enabled,然後將 Rect 設置為寬度 240高度 320,使圖片拉伸到螢幕的大小。


Texture 中,將 Repeat 設置為 Enabled,使圖片在整個螢幕上重複。


背景增加動畫:

Background 節(jié)點下增加 AnimationPlayer 作為子節(jié)點。


點擊 Animation 按鈕,選擇 New Animation,命名為 "scroll",將 Length 設置為 2,開啟 LoopingAutoplay 選項。


Animation 面板中,透過點擊 Inspector 中屬性旁邊的鑰匙圖標,新增背景的 Region/Rect 屬性的關鍵幀。


在時間軸上的藍色指示器處於時間 0 時,選擇 Background 並點擊 Region/Rect 旁邊的鑰匙,創(chuàng)建新的軌道和關鍵幀。


將時間軸拉到時間 2,然後更改 Region/Rect 屬性的 y 值為 64,再次點擊鑰匙新增另一個關鍵幀。
按下動畫的播放按鈕,應該可以看到背景緩慢滾動。


把飛船和背景加到主場景:
選擇 Main 節(jié)點,點擊 Instantiate Child Scene 按鈕,新增飛船和背景到主場景。


敵人



設定敵人節(jié)點:
增加一個新的 Scene,命名為 Enemy,節(jié)點類型為 Area2D,用於偵測與玩家的子彈或玩家本身的碰撞。
Enemy 節(jié)點下,增加 Sprite2DCollisionShape2DAnimationPlayer、兩個 Timer
重新命名為 MoveTimer ShootTimer,後面會用到


Enemy 節(jié)點的 Node 中,在 Groups 底下,新增 "enemies",以便與玩家的子彈進行碰撞檢測。


回憶篇:
因為之前子彈的 Scirpt 有寫到,我們要偵測 "enemies"。


設定Sprite2D:

Sprite2DTexture 屬性中新增敵人的圖片(Bon_Bon (16 x 16).png),並將 Animation/Hframes 設定為 4。


新增一個矩形 CollisionShape2D,調(diào)整大小以符合敵人的形狀。
AnimationPlayer 中新增"bounce"名字的動畫,設定其循環(huán)播放自動播放


跟前面的背景一樣的方式,只是這次我們不是要讓它滾動。
是要切換 Sprite2D 中的 Animation/Frames 來達到敵人動起來的感覺。


新增另一個動畫 "explode",設定其持續(xù)時間為 0.4 秒,改變 Sprite2DTextureHframes 屬性。
跟前面一樣步驟,改成爆炸的圖案,然後按下旁邊的鑰匙圖示設定。


texturehframes 也要設定,
是因為我們切換敵人和爆炸的動畫,確保不會爆炸的時候變成敵人的樣子等等情況。



敵人腳本:
Enemy 新增一個腳本,宣告變數(shù) start_posspeed,以及螢幕大小的 screensize

extends Area2D

var start_pos = Vector2.ZERO
var speed = 0

@onready var screensize = get_viewport_rect().size
@onready var move_timer: Timer = $MoveTimer
@onready var shoot_timer: Timer = $ShootTimer
@onready var animation_player: AnimationPlayer = $AnimationPlayer

start() 中,設定初始位置、速度,以及隨機等待時間,然後使用 Tween 進行入場動畫。

func start(pos: Vector2) -> void:
    speed = 0
    position = Vector2(pos.x, -pos.y)
    start_pos = pos
    await get_tree().create_timer(randf_range(0.25, 0.55)).timeout
    var tween = create_tween().set_trans(Tween.TRANS_BACK)
    tween.tween_property(self, "position:y", start_pos.y, 1.4)
    await tween.finished
    moveTimer.wait_time = randf_range(5, 20)
    moveTimer.start()
    shootTimer.wait_time = randf_range(4, 20)
    shootTimer.start()

_process() 函數(shù)中,根據(jù)速度更新敵人的位置,並在敵人跑出螢幕底部時重新設置其位置。

func _process(delta: float) -> void:
    position.y += speed * delta
    if position.y > screensize.y + 32:
    start(start_pos)

宣告 died 信號,用於在敵人被銷毀時通知主場景。

extends Area2D

signal died

var start_pos = Vector2.ZERO
var speed = 0

@onready var screensize = get_viewport_rect().size
@onready var move_timer: Timer = $MoveTimer
@onready var shoot_timer: Timer = $ShootTimer
@onready var animation_player: AnimationPlayer = $AnimationPlayer

explode() 函數(shù)中,停止移動,播放爆炸動畫,禁用碰撞檢測,發(fā)射 died 信號,最後銷毀敵人。

func explode() -> void:
    speed = 0
    animation_player.play("explode")
    died.emit(5)
    await animation_player.animation_finished
    queue_free()


連接 MoveTimerShootTimertimeout 信號,以控制敵人的移動和射擊行為。


_timer_timeout() 函數(shù)中,隨機生成速度,所以時間到敵人就會往飛船移動。

func _on_move_timer_timeout() -> void:
speed = randf_range(75, 100)

_shoot_timer_timeout() 函數(shù)中,設定隨機的射擊冷卻時間,讓射擊時間不固定。

func _on_shoot_timer_timeout() -> void:
    shoot()
    shoot_timer.wait_time = randf_range(4, 20)
    shoot_timer.start()

最後我重新整理一下程式碼,這是敵人的腳本全部部分。

extends Area2D

var start_pos = Vector2.ZERO
var speed = 0

@onready var screensize = get_viewport_rect().size
@onready var move_timer: Timer = $MoveTimer
@onready var shoot_timer: Timer = $ShootTimer
@onready var animation_player: AnimationPlayer = $AnimationPlayer


func start(pos: Vector2) -> void:
    reset_enemy(pos)
    spawn_enemy()
    reset_move_timer()
    reset_shoot_timer()


func reset_enemy(pos: Vector2) -> void:
    speed = 0
    position = Vector2(pos.x, -pos.y)
    start_pos = pos


func reset_move_timer() -> void:
    move_timer.wait_time = randf_range(5, 20)
    move_timer.start()


func reset_shoot_timer() -> void:
    shoot_timer.wait_time = randf_range(4, 20)
    shoot_timer.start()


func spawn_enemy() -> void:
    await get_tree().create_timer(randf_range(0.25, 0.55)).timeout
    var tween = create_tween().set_trans(Tween.TRANS_BACK)
    tween.tween_property(self, "position:y", start_pos.y, 1.4)
    await tween.finished


func _process(delta: float) -> void:
    position.y += speed * delta
    if position.y > screensize.y + 32:
    start(start_pos)


func explode() -> void:
    speed = 0
    animation_player.play("explode")
    died.emit(5)
    await animation_player.animation_finished
    queue_free()


func shoot() -> void:
    pass

func _on_move_timer_timeout() -> void:
    speed = randf_range(75, 100)


func _on_shoot_timer_timeout() -> void:
    shoot()
    reset_shoot_timer()



生成敵人


主場景腳本:
Main 新增一個腳本,宣告兩個變數(shù)。
讓主場景可以先載入敵人的場景。

extends Node2D

var enemy_scene = preload("res://scenes/enemy/enemy.tscn")
var score = 0

接著新增 spawn_all_enemies(),按順序生成所有敵人。

func spawn_all_enemies() -> void:
    for x in range(9):
        for y in range(3):
            var enemy_scene_instance = enemy_scene.instantiate()
            var pos = Vector2(x * (16 + 8) + 24, 16 * 4 + y * 16)
            add_child(enemy_scene_instance)
            enemy_scene_instance.start(pos)
            enemy_scene_instance.died.connect(on_enemy_died)

這個迴圈可以生成多個敵人,這些敵人會在螢幕的上面,總共生成27個敵人。
接著設定敵人的初始位置 pos,然後將每個敵人加到主場景的子節(jié)點,
然後使用 start() 函數(shù)來設定每個敵人。
最後連接每個敵人的 died 信號到 on_enemy_died 函數(shù)。

func on_enemy_died(value) -> void:
    score += value

讓我們把它整合起來。

extends Node2D

var enemy_scene = preload("res://scenes/enemy/enemy.tscn")
var score = 0

func _ready() -> void:
    spawn_all_enemies()


func spawn_all_enemies() -> void:
    for x in range(9):
        for y in range(3):
            var enemy_scene_instance = enemy_scene.instantiate()
            var pos = Vector2(x * (16 + 8) + 24, 16 * 4 + y * 16)
            add_child(enemy_scene_instance)
            enemy_scene_instance.start(pos)
            enemy_scene_instance.died.connect(on_enemy_died)


func on_enemy_died(value) -> void:
    score += value




到這裡敵人算是完成了九成。
剩下的部分我們就留到下一回,我們要讓敵人也可以發(fā)射子彈。

題外話
無論你是直接拉到最後,還是從頭看到尾的的朋友。
如果覺得不夠,還想再尋找其他社群。
目前我所知道的,臺灣還有兩個社群。

當然最後我還是要再次宣傳我們的公會。

開拓者交流互助公會
送禮物贊助創(chuàng)作者 !
0
留言

創(chuàng)作回應

追蹤 創(chuàng)作集

作者相關創(chuàng)作

相關創(chuàng)作

更多創(chuàng)作