今天我給游戲添加了輪次, 大概是關(guān)卡的意思, 每一關(guān)有一個(gè)固定時(shí)間, 當(dāng)玩家殺完輪次內(nèi)生成的所有敵人, 則完成當(dāng)前輪次, 游戲進(jìn)入下一個(gè)輪次, 隨著輪次增加, 游戲難度也會(huì)逐漸增加
看看效果
當(dāng)前游戲輪次的調(diào)整不明顯, 主要是增加機(jī)制, 看它是否運(yùn)行正常 (可以觀察日志打印)

大致實(shí)現(xiàn)思路
輪次時(shí)間控制
在上一次實(shí)現(xiàn)的EnemySpawnComponent組件基礎(chǔ)上新增一個(gè)RoundTimer, 用于輪次計(jì)時(shí)
并且新增一些變量, 用于輪次的時(shí)間/參數(shù)調(diào)整
const BASE_ROUND_TIME: float = 10
const ROUND_TIME_GROWTH: float = 5
const BASE_MIN_SPAWN_INTERVAL: float = 2.0
const BASE_MAX_SPAWN_INTERVAL: float = 5.0
const SPAWN_INTERVAL_GROWTH: float = -0.2
var round_count: int = 0
var round_min_spawn_interval: float = BASE_MIN_SPAWN_INTERVAL
var round_max_spawn_interval: float = BASE_MAX_SPAWN_INTERVAL
var enemy_count: int = 0
新增的常量和變量名都比較有描述性
- 輪次初始時(shí)間為10秒, 每完成一個(gè)輪次, 下次增加5秒時(shí)間
- 初始的敵人生成隨機(jī)最小最大間隔為2-5秒, 每增加一個(gè)輪次, 間隔時(shí)間的最小最大值均減少0.2秒 (敵人生成加快)
- 當(dāng)前輪次計(jì)數(shù), 時(shí)間與配置基于輪次數(shù)調(diào)整
- 記錄敵人數(shù)量, 依據(jù)敵人數(shù)量判斷輪次是否完成
輪次開(kāi)啟與結(jié)束
開(kāi)啟時(shí)計(jì)數(shù)加1, 并且調(diào)整Timer時(shí)間與參數(shù), 同時(shí)開(kāi)啟敵人生成Timer
在ready中調(diào)用_start_round自動(dòng)開(kāi)啟第一個(gè)輪次
func _start_round() -> void:
round_count += 1
print("Round %s start" % round_count)
round_min_spawn_interval = BASE_MIN_SPAWN_INTERVAL + (round_count - 1) * SPAWN_INTERVAL_GROWTH
round_max_spawn_interval = BASE_MAX_SPAWN_INTERVAL + (round_count - 1) * SPAWN_INTERVAL_GROWTH
round_timer.start(BASE_ROUND_TIME + (round_count - 1) * ROUND_TIME_GROWTH)
spawn_timer.start(randf_range(round_min_spawn_interval, round_max_spawn_interval))
輪次的結(jié)束是round_timer計(jì)時(shí)停止, 停止后也同時(shí)停止敵人生成計(jì)時(shí)
func _on_round_timer_timeout() -> void:
print("Round %s end" % round_count)
spawn_timer.stop()
_check_round_completed()
輪次完成檢測(cè)
判斷輪次完成有兩個(gè)條件
- 輪次計(jì)時(shí)完畢
- 該輪次的敵人被全部消滅
func _check_round_completed() -> void:
if !round_timer.is_stopped():
return
if enemy_count == 0:
print("Round %s completed!" % round_count)
_start_round()
若檢測(cè)到輪次完成, 則自動(dòng)開(kāi)始下一輪次
在輪次Timer結(jié)束時(shí)已經(jīng)進(jìn)行過(guò)一次檢測(cè), 還應(yīng)該在每個(gè)敵人死亡時(shí)也進(jìn)行計(jì)數(shù)和檢測(cè)
func _on_enemy_died() -> void:
enemy_count -= 1
_check_round_completed()
這就涉及到一個(gè)問(wèn)題, 在該組件中怎樣獲取到敵人死亡的信號(hào)?
全局信號(hào)總線
有人堅(jiān)決反對(duì)使用信號(hào)總線, 說(shuō)這不符合Godot的設(shè)計(jì)哲學(xué)
但是我會(huì)以務(wù)實(shí)為主, 我認(rèn)為信號(hào)總線應(yīng)該在必要時(shí)使用, 但要避免亂用
在這里, 敵人實(shí)例與該組件實(shí)例不是簡(jiǎn)單的上下級(jí)或者兄弟級(jí), 如果通過(guò)祖先節(jié)點(diǎn)傳遞信號(hào), 會(huì)相當(dāng)麻煩
我認(rèn)為在這里使用信號(hào)總線是非常合適的
使用Autoload實(shí)現(xiàn)一個(gè)全局信號(hào)總線
新建一個(gè)腳本, 在ProjectSettings中的Globals中加載并啟用, 之后就可以全局訪問(wèn)
extends Node
signal enemy_died
func emit_enemy_died() -> void:
enemy_died.emit()
腳本很簡(jiǎn)單, 主要是為了跨模塊訪問(wèn)
敵人死亡通知
敵人的生命管理被我用一個(gè)HealthComponent組件管理了, 與之前的邏輯一樣, 生命值歸0之后發(fā)出信號(hào), 封裝組件都是為了方便后續(xù)復(fù)用
在生命值歸0后發(fā)出全局信號(hào), 并銷(xiāo)毀自身
func _on_health_ended() -> void:
GameEvents.emit_enemy_died()
queue_free()
全局信號(hào)監(jiān)聽(tīng)
與普通信號(hào)監(jiān)聽(tīng)一樣, 只是我們不用再找信號(hào)的源頭是誰(shuí)
GameEvents.enemy_died.connect(_on_enemy_died)
記得處理好服務(wù)器邏輯與客戶(hù)端邏輯
這樣我們的輪次結(jié)構(gòu)就完成了, 后續(xù)開(kāi)發(fā)中可以基于輪次讓游戲內(nèi)容逐漸產(chǎn)生一些變化, 慢慢變得好玩有趣起來(lái)