今天來學(xué)習(xí)游戲中的主題(Theme)和字體調(diào)整, 并添加一個能顯示輪次和倒計時的游戲UI
看看效果
像素字體加上, 瞬間有那感覺了! 有點(diǎn)那么回事了, 哈哈

實現(xiàn)過程
主題和字體設(shè)置
字體我是直接在網(wǎng)上找了一個開源字體, 覺得好看就用了: https://github.com/TakWolf/fusion-pixel-font
下載otf格式, 不帶woff/woff2后綴的字體, 導(dǎo)入Godot, 關(guān)閉抗鋸齒(Antialiasing), Hinting, Subpixel Positioning后重新導(dǎo)入, 像素游戲不需要抗鋸齒/高清/子像素等設(shè)置, 導(dǎo)入zh_hans(簡體中文版本)
新建一個Theme資源, 命名為default.tres, 將Default Font設(shè)置為導(dǎo)入的字體, 調(diào)整字號為20 (自己在預(yù)覽中覺得合適的大小)
設(shè)置默認(rèn)主題: Project -> Project Settings -> GUI -> Theme -> Custom, 設(shè)置為剛剛新建的Theme資源, 并且可以調(diào)整默認(rèn)字體導(dǎo)入選項
這樣, 默認(rèn)字體和主題都設(shè)置完畢
輪次數(shù)和輪次倒計時顯示
新建一個CanvasLayer為根節(jié)點(diǎn)的場景, UI節(jié)點(diǎn)樹如下
RoundTimerUI (CanvasLayer)
└── MarginContainer
└── VBoxContainer
├── RoundLabel
└── TimerLabel
調(diào)整對齊, Margin等屬性, 讓UI在靠頂居中的位置顯示
RoundTimerUI腳本如下
extends CanvasLayer
@export var enemy_spawn_component: EnemySpawnComponent
@onready var round_label: Label = %RoundLabel
@onready var timer_label: Label = %TimerLabel
func _ready() -> void:
enemy_spawn_component.round_changed.connect(_on_round_changed)
func _process(_delta: float) -> void:
var time_left := enemy_spawn_component.get_round_time_left()
timer_label.text = str(ceili(time_left))
func _on_round_changed(round_count: int) -> void:
round_label.text = "Round %s" % round_count
它監(jiān)聽外部EnemySpawnComponent組件的輪次變化事件, 以及實時獲取EnemySpawnComponent組件中的當(dāng)前輪次剩余時間, 轉(zhuǎn)換為整數(shù)顯示, 更新UI
之后就是數(shù)據(jù)的通知與同步, 在這個場景中, 數(shù)據(jù)的同步顯得稍微復(fù)雜一點(diǎn), 它涉及到跨組件的Timer處理
主要把握好兩個同步時間點(diǎn):
- Round變化
- 新的Peer加入
同步內(nèi)容: 主要同步Round計數(shù), 以及當(dāng)前剩余時間, 倒計時讓每個Peer自己的Timer執(zhí)行, 服務(wù)器只在關(guān)鍵點(diǎn)同步, 避免不必要的寬帶浪費(fèi)
這里主要看一下EnemySpawnComponent組件中提供的"同步"接口
func synchronize(peer_id: int = -1) -> void:
if not is_multiplayer_authority():
return
var data = {
"round_count": round_count,
"round_timer_time_left": round_timer.time_left,
"round_timer_running": not round_timer.is_stopped()
}
if peer_id < 0:
_synchronize.rpc(data)
elif peer_id > 1:
_synchronize.rpc_id(peer_id, data)
@rpc("authority", "call_remote", "reliable")
func _synchronize(data: Dictionary) -> void:
round_count = data.round_count
round_timer.wait_time = data.round_timer_time_left
if data.round_timer_running:
round_timer.start()
同步接口synchronize收集服務(wù)端信息, 并同步給其他的peer, 所以peer_id中過濾自身id(1)
synchronize在Round發(fā)生變化時與所有peer進(jìn)行RPC同步; 在新peer加入時, 僅與新peer同步(傳入peer_id)
對round_count字段添加getter和setter
var round_count: int = 0:
get:
return round_count
set(value):
round_count = value
round_changed.emit(value)
當(dāng)每個peer上的round_count發(fā)生變化時, 都會觸發(fā)各自的round_changed信號, 同步到UI界面, 保持?jǐn)?shù)據(jù)與UI顯示一致