6 min read

GodotSteaam 教學 #2:建立與加入大廳 (Lobby)

GodotSteaam 教學 #2:建立與加入大廳 (Lobby)

完整範例及程式碼點擊連結前往:

GitHub - peterho2022/godotsteam-tutorial
Contribute to peterho2022/godotsteam-tutorial development by creating an account on GitHub.

在上一篇文章中,我們成功初始化了 Steam API,讓 Godot 能讀取到我們的 Steam ID。 今天要做多人連線重要的一步:建立大廳 (Lobby)

「大廳」就像是一個聊天室或等候室。

  1. 房主 (Host) 建立一個大廳。
  2. 玩家 (Client) 搜尋並加入這個大廳。
  3. 一旦雙方都在大廳裡,他們就交換了彼此的 Steam ID,這時才能開始傳輸遊戲封包(移動、攻擊等資料)。

本篇目標:寫一個簡單的介面,讓 A 電腦按下「建立」,B 電腦可以搜尋到 A 電腦的大廳

第一步:介面準備 (UI)

首先要建立大廳的場景。新增一個 CanvasLayer 作為根節點,節點結構如下:

  • CanvasLayer (LobbyRoom)
    • Button (RefreshButton) : 按下刷新頁面
    • VBoxContainer (LobbyListContainer) : 用來放搜尋到的房間按鈕
    • Button (CreateLobbyButton) : 按下建立大廳
Godot 節點截圖

第二步:撰寫腳本

將腳本掛到根節點上,完整程式碼如下。

extends CanvasLayer
@onready var lobby_list_container: VBoxContainer = $LobbyListContainer
@onready var refresh_button: Button = $RefreshButton
@onready var create_lobby_button: Button = $CreateLobbyButton

func _ready() -> void:
	# 初始化 Steam (建議在專案啟動時就全域執行,但寫在這裡方便教學演示)
	# 設定 App ID,480 是開發用的 Spacewar
	OS.set_environment("SteamAppId", str(480))
	OS.set_environment("SteamGameId", str(480))
	var initialize_response: Dictionary = Steam.steamInitEx()
	
	Steam.lobby_match_list.connect(_on_lobby_match_list)
	Steam.lobby_created.connect(_on_lobby_created)
	
	refresh_button.pressed.connect(_find_lobbies)
	create_lobby_button.pressed.connect(_create_lobby)

func _process(_delta: float) -> void:
	Steam.run_callbacks()

func _find_lobbies():
	for child in lobby_list_container.get_children():
		child.queue_free()
	
	Steam.addRequestLobbyListDistanceFilter(Steam.LOBBY_DISTANCE_FILTER_WORLDWIDE)
	# 如果沒有過濾會出現所有正在測試的房間
    # Steam.addRequestLobbyListStringFilter("game", "Peter's Game", Steam.LOBBY_COMPARISON_EQUAL)
	Steam.requestLobbyList()

func _on_lobby_match_list(lobbies: Array):
	for lobby_id in lobbies:
		var lobby_name = Steam.getLobbyData(lobby_id, "name")
		var btn = Button.new()
		btn.text = "房間: %s" % lobby_name
		btn.pressed.connect(Steam.joinLobby.bind(lobby_id))
		lobby_list_container.add_child(btn)

func _create_lobby():
	Steam.createLobby(Steam.LOBBY_TYPE_PUBLIC, 4)

func _on_lobby_created(connect_result: int, lobby_id: int):
	if connect_result == 1:
		Steam.setLobbyData(lobby_id, "name", Steam.getPersonaName())
		Steam.setLobbyData(lobby_id, "game", "Peter's Game")

程式碼可以拆成四個部分來理解:初始化設定建立大廳搜尋大廳 以及 顯示與加入大廳

1. 初始化與訊號綁定 (_ready_process)

綁定訊號:當按鈕被按下或Steam 搜尋完成時,要執行哪個函式。

extends CanvasLayer
@onready var lobby_list_container: VBoxContainer = $LobbyListContainer
@onready var refresh_button: Button = $RefreshButton
@onready var create_lobby_button: Button = $CreateLobbyButton

func _ready() -> void:
	# 初始化 Steam (建議在專案啟動時就全域執行,但寫在這裡方便教學演示)
	# 設定 App ID,480 是開發用的 Spacewar
	OS.set_environment("SteamAppId", str(480))
	OS.set_environment("SteamGameId", str(480))
	var initialize_response: Dictionary = Steam.steamInitEx()
	
	Steam.lobby_match_list.connect(_on_lobby_match_list)
	Steam.lobby_created.connect(_on_lobby_created)
	
	refresh_button.pressed.connect(_find_lobbies)
	create_lobby_button.pressed.connect(_create_lobby)

func _process(_delta: float) -> void:
	Steam.run_callbacks()

2. 建立大廳與設定 (_create_lobby_on_lobby_created)

func _create_lobby():
	# 建立一個公開大廳,人數上限 4 人
	Steam.createLobby(Steam.LOBBY_TYPE_PUBLIC, 4)

func _on_lobby_created(connect_result: int, lobby_id: int):
	if connect_result == 1:
		Steam.setLobbyData(lobby_id, "name", Steam.getPersonaName())
		Steam.setLobbyData(lobby_id, "game", "Peter's Game")

當呼叫 Steam.createLobby 成功後,Steam 會觸發 _on_lobby_created

  • 關鍵步驟:我們使用 Steam.setLobbyData 給房間貼上標籤。
  • "name":顯示給玩家看的房名。
  • "game":這是一個隱藏標籤,用來標記「這是 Peter 的遊戲」。這在下一步搜尋時非常重要。

3. 搜尋大廳與過濾條件 (_find_lobbies)

func _find_lobbies():
	# 每次搜尋前,先把舊的清單清空
	for child in lobby_list_container.get_children():
		child.queue_free()
		
	Steam.addRequestLobbyListDistanceFilter(Steam.LOBBY_DISTANCE_FILTER_WORLDWIDE)
	# 如果沒有過濾會出現所有正在測試的房間
    # Steam.addRequestLobbyListStringFilter("game", "Peter's Game", Steam.LOBBY_COMPARISON_EQUAL)
	Steam.requestLobbyList()

這裡使用了 Steam.addRequestLobbyListStringFilter

  • 由於 480 是 Steam 官方的測試 ID。如果沒有過濾,可能搜尋出幾百個房間。
沒有加上過濾的結果

4. 動態生成 UI 與加入大廳 (_on_lobby_match_list)

func _on_lobby_match_list(lobbies: Array):
	for lobby_id in lobbies:
		var lobby_name = Steam.getLobbyData(lobby_id, "name")
		var btn = Button.new()
		btn.text = "房間: %s" % lobby_name
		btn.pressed.connect(Steam.joinLobby.bind(lobby_id))
		lobby_list_container.add_child(btn)

當搜尋結果回來 (_on_lobby_match_list),我們用 for 迴圈顯示出所有房間。

第三步:匯出和測試

  1. 準備環境: 需要兩台電腦。兩邊都要登入 Steam 帳號。
  2. 匯出專案:點選 Project -> Export
選擇匯出的作業系統
  1. 因為我們使用的是 GodotSteam Pre-compiled 版本,匯出時不能使用 Godot 內建的模板。請確認你已下載對應版本的 Export Templates(下載傳送門)。
  1. Custom Template 欄位手動選擇下載好的執行檔路徑。
  1. 匯出完成後,你會看到 exepck 檔案。需手動將 steam_api64.dll 複製到跟 exe 同個資料夾中。
複製 steam_api64.dll
  1. 將整包資料夾傳給另一台電腦。
    • 電腦 A:執行遊戲 -> 點擊「建立大廳」。
    • 電腦 B:執行遊戲 -> 點擊「搜尋大廳」。
    • 如果你在列表中看到了按鈕,並成功點擊加入,恭喜你!兩台電腦已經透過 Steam 成功連線了。

小結

在這次測試中時發現一個麻煩點:每次改一點程式碼,就必須重新匯出、複製檔案、傳給另一台電腦測試。這在開發初期非常浪費時間。

因此,下一篇文章我們將介紹 Godot High Level Networking 的架構。我們將學習如何利用 Godot 內建的網路功能,先在本地端快速開發與測試多人連線邏輯,等到功能完善後,再套上 Steam 進行遠端連線。