Godot4で開発するスマホ用ゲームで、Game用のWebサービスを使ってプレイヤー間でハイスコアを共有したい

　Godotで開発するスマホ用ゲームで、プレイヤー間でハイスコアを共有したいと思います。
　Game用のWebサービスを使います。
# Game用のWebサービス
　Game用のWebサービスはいくつか呼び方があるみたいですね。どれも同じものを指していると思います。
　・Game Backend As A Service（BAAS)
　・game backend platform
　・Game Server Service
　また、ハイスコアの共有サービスはleaderboardと呼ばれています。

　GodotのGDScriptから簡単に使えて、leaderboardサービスを提供し、無料で使えるBAASを探して、「SILENTWOLF」を使うことにしました。

# SILENTWOLF(2023/10/21時点)
　Cybergladというゲーム用に開発したサーバサイド処理を、BAASとして公開したものが、SILENTWOLFです。SILENTWOLFにアクセスするためのGDScript用Pluginを提供しているため、Godotに簡単に組み込んで使うことができます。
　下記の3つのサービスを提供しています。無料です。

＜SILENTWOLFが提供するサービス＞
1. ハイスコアを管理するLeaderboard
1. プレイヤーのアカウント管理
1. プレイヤーのデータ管理

:::note
　ただほど高いものは無いとも言いますので、ご自身の責任で利用するようにお願いします。
:::

　早速使ってみます。

# SilentWolf accountの登録
　ここの「Sign in」リンクからSilentWolf accountを登録しました。

https://silentwolf.com/

登録する項目は名前とメールアドレス、パスワードです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/de858d11-2e78-52e8-d27f-25ca67186a50.png)

　登録が完了するとAPI IDが発行されます。

# API IDとGame IDについて
　GDScriptでSILENTWOLFの初期設定APIを起動するときに、API IDとGame IDの２つのIDが必要になります。
　API IDはSilent Wolf Accountに対して発行されるIDで、英大文字、英小文字と数字を組み合わせた40字の文字列です。Game IDはGameごとに発行するIDです。

:::note
API IDとGame IDがわかると不正にハイスコアの登録や読み出しなど行うことができるので、厳重に管理しましょう
:::

# GAME IDの発行
　GAME名を登録して、GAME IDを発行します。

　上部の「+New Game」をクリックして、ゲームの名前を登録します。

:::note
 日本語のような２Byteコードには対応していないようなので、半角英数字で登録しましょう。日本語で登録はできますが、GDScriptからアクセスできませんでした。削除もできず...
:::

　下図の「Escape Bazooka」が私が入力したゲーム名です。
![2023-10-21 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/75c5d963-a53c-4b90-0ab8-188fce7c3876.png)

「Submit」ボタンを押下し、新しいGameの登録が完了すると、API IDとGame IDが表示されます。API IDは黒で塗りつぶして見えなくしました。Game IDはゲーム名から生成されるようです。（入力したGame名を小文字にして、スペースが削除されました）
![2023-10-21 (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ce7297d8-ccdd-3e3f-5065-ea4e8ba6ac70.png)

# Godot 4用のSILENTWOLF Pluginをダウンロードする
　上部の「Download」をクリック。Godot4.1.1で実行するので、For Godot 4の方の「Download Plugin」をクリックしてダウンロードしました。

![2023-10-21 (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/351da9fe-1c80-b2c3-2410-60e6e1119e86.png)

# GodotプロジェクトにSILENTWOLFを組み込む
　Godotプロジェクトを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ae45cb55-c1c7-f213-fdf5-7982ed5158dd.png)

　先ほどダウンロードしたSILENTWOLF Pluginを解凍して、Godotプロジェクト内に配置しますが、配置場所は決められています。addonsフォルダを作成してその中に、silent_wolfフォルダを配置します。
　「res://addons/silent_wolf/SilentWolf.gd」というパスで「SilentWolf.gd」にアクセスできることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/ba3c616e-ba0c-aaee-2869-ba9874a10788.png)

# SilentWolf.gdをSingletonにする
　先ほど確認した、res://addons/silent_wolf/SilentWolf.gdを「SilentWolf」の名称でSingletonにします。
　「プロジェクト」メニューの「プロジェクト設定...」を開き、パスに「res://addons/silent_wolf/SilentWolf.gd」、ノード名に「SilentWolf」を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/522534ab-7ebe-9106-4574-f60d14490d1b.png)
「追加」ボタンを押下して、正常に登録されることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/89cee4c7-bf90-66a8-18f2-49daa4f6836b.png)

# GDScriptからleadrboardサービスの使い方
　実際にハイスコアを登録して読み出します。
　「Set up your leadrboard」の「Get started」に詳細な説明があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/cbc0c2be-fd97-39b8-a03b-abb4e55a2718.png)

　サンプルコードはGodot 3.x用とGodot 4用が用意されているので、Godot 4を使います。
![2023-10-21 (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/f8edd11b-482b-8ba9-a9ef-bae5d1769cdb.png)

# GDScriptでSILENTWOLFの初期設定とハイスコアの登録、読み出しをする
　今回はノードは何でもよいので、「シーン」タブで「ユーザインタフェース」ボタンを押下して、Controlノードをトップノードとして追加します。
　controlを右クリックして「スクリプトをアタッチ」します。内容は下記のように修正しました。

:::note
本来は、自動ロードされたシングルトン (グローバル スクリプトなど) の _ready() 関数、またはスプラッシュ ページに関連付けられたスクリプトなど、ゲームの最初の方で実行されるように設計する必要があります。
:::

:::note
 API IDは「YOUR API ID」をご自身で取得したものを設定してください。
:::

```GDScript
extends Control

func _ready():
	# SILENTWOLF PluginにAPI IDとGame IDを設定する
	SilentWolf.configure({
		"api_key": "YOUR API ID",
		"game_id": "escapebazooka",
		"log_level": 1
	})

	# ハイスコアの登録
	SilentWolf.Scores.save_score("YUTARO", 1000)
	
	# ハイスコア情報の読み出し
	var sw_result: Dictionary = await SilentWolf.Scores.get_scores().sw_get_scores_complete
	print("Scores: " + str(sw_result.scores))
```

SilentWolf.configureにlog_levelを設定しています。数字の意味は下記のようになっています。
0 for errors only
1 for info-level logging
2 for debug logging

　実行すると、Godotの「出力」タブに下記のようにログが出力されます。

:::note
　ログにもAPI IDが表示されるので、共有する場合は注意しましょう。
　以下のログのAPI IDは「YOUR API ID」に置換しました）
:::

```
Godot Engine v4.1.1.stable.official.bd6af8e0e - https://godotengine.org
Vulkan API 1.3.217 - Forward Mobile - Using Vulkan Device #0: AMD - AMD Radeon(TM) Vega 8 Graphics
 
SW Init timestamp: 368
SW ready start timestamp: 414
SW ready end timestamp: 415
Calling SilentWolf backend to post new score...　★１
!metadata.empty(): false
POST headers: ["Content-Type: application/json", "x-api-key: YOUR API ID", "x-sw-game-id: escapebazooka", "x-sw-plugin-version: 0.9.7", "x-sw-godot-version: 4.1.1-stable (official)"]
Calling SilentWolf backend to get scores...　★３
GET headers: ["x-api-key: YOUR API ID", "x-sw-game-id: escapebazooka", "x-sw-plugin-version: 0.9.7", "x-sw-godot-version: 4.1.1-stable (official)"]
SilentWolf get scores success, found 0 scores.
Scores: []★４
SilentWolf save score success.　★２

```

★を付けたところに注目すると、ハイスコアを登録するために「SilentWolf.Scores.save_score」を実行した時点で★１のログが出力されますが、実際にサーバへの保存が完了したのは★２です。
その★１と★２の間に、SilentWolf.Scores.get_scores()でサーバからハイスコアの読み出しが実行されています。
つまり、ハイスコアの登録処理は、SILENTWOLFのAPIをコールした時点で終わらないということです。
ハイスコアを登録した直後に、ハイスコアの一覧を取得・表示する場合は、ハイスコア登録が完了するのを待つ必要があります。

# Silent WolfのDashboardでハイスコアを確認する
　下記の場所にあるリンクからSilent WolfのDashboardを表示し、先ほど登録したハイスコアを確認できます。

![2023-10-21 (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/170d74ca-9891-32d6-0020-3abb2afb687a.png)
DashBoardでleaderboardを表示したところです。
複数のゲームを登録している場合、右上の赤いボタンで、Dashboardに表示するゲームを選択します。
左側の「Scores」をクリックすると、leaderboardが表示されます。
初期表示は「＞main」とあるので、「＞」をクリックして、開くとハイスコアの一覧が表示されます。
クリックしてから表示されるまでタイムラグがあるので、表示されるまで待ちましょう。
![2023-10-21 (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/8271df6b-7a29-f878-0b35-2ea65e5ac5dc.png)

「YUTARO」の名前で、Scoreが1000で登録されています。

# ハイスコアを読み出す
　Godotをもう一度実行して、「出力」タブを確認しましょう。

```
Godot Engine v4.1.1.stable.official.bd6af8e0e - https://godotengine.org
Vulkan API 1.3.217 - Forward Mobile - Using Vulkan Device #0: AMD - AMD Radeon(TM) Vega 8 Graphics
 
SW Init timestamp: 362
SW ready start timestamp: 404
SW ready end timestamp: 405
Calling SilentWolf backend to post new score...
!metadata.empty(): false
POST headers: ["Content-Type: application/json", "x-api-key: YOUR API ID", "x-sw-game-id: escapebazooka", "x-sw-plugin-version: 0.9.7", "x-sw-godot-version: 4.1.1-stable (official)"]
Calling SilentWolf backend to get scores...
GET headers: ["x-api-key: YOUR API ID", "x-sw-game-id: escapebazooka", "x-sw-plugin-version: 0.9.7", "x-sw-godot-version: 4.1.1-stable (official)"]
SilentWolf save score success.
SilentWolf get scores success, found 1 scores.★５
Scores: [{ "score_id": "8bb47e11-d998-43a4-9b6d-7719735ed4f4", "score": 1000, "player_name": "YUTARO", "timestamp": 1697886456298.72 }] ★５
```

★５の読み出し結果にハイスコアが表示されていますね。
辞書型のリストです。
```
Scores: [
　{
    "score_id": "8bb47e11-d998-43a4-9b6d-7719735ed4f4",
    "score": 1000,
    "player_name": "YUTARO",
    "timestamp": 1697886456298.72 
  }
] 
```

# DashboardのScoresを確認
　DashboardのScoresを見ると、Godotを合計2回実行したので、ハイスコアが２つ登録されています。
![2023-10-21 (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/db37c3b4-7ffb-ea16-6095-41123d51aaf9.png)
　
# もう一度ハイスコアを読み出す
　Godotをもう一度実行して、「出力」タブを確認しましょう。
　合計3回実行しました。
　今回は、（おそらくたまたま？）ハイスコアの登録処理後に、ハイスコア読み出しができたので、ハイスコアが３つ読み出せていました。（★６のあたり）

```
Godot Engine v4.1.1.stable.official.bd6af8e0e - https://godotengine.org
Vulkan API 1.3.217 - Forward Mobile - Using Vulkan Device #0: AMD - AMD Radeon(TM) Vega 8 Graphics
 
SW Init timestamp: 497
SW ready start timestamp: 541
SW ready end timestamp: 541
Calling SilentWolf backend to post new score...　
!metadata.empty(): false
POST headers: ["Content-Type: application/json", "x-api-key: YOUR API ID", "x-sw-game-id: escapebazooka", "x-sw-plugin-version: 0.9.7", "x-sw-godot-version: 4.1.1-stable (official)"]
Calling SilentWolf backend to get scores...
GET headers: ["x-api-key: YOUR API ID", "x-sw-game-id: escapebazooka", "x-sw-plugin-version: 0.9.7", "x-sw-godot-version: 4.1.1-stable (official)"]
SilentWolf save score success.
SilentWolf get scores success, found 3 scores.★６
Scores: [{ "score_id": "8bb47e11-d998-43a4-9b6d-7719735ed4f4", "score": 1000, "player_name": "YUTARO", "timestamp": 1697886456298.72 }, { "score_id": "d55d08f5-0b96-41fe-94eb-c157c26f5ddb", "score": 1000, "player_name": "YUTARO", "timestamp": 1697890307194.37 }, { "score_id": "9d8a06c7-d1e0-4d65-be0c-5aed8eac449e", "score": 1000, "player_name": "YUTARO", "timestamp": 1697890491240.35 }]
```

# Androidアプリにエクスポートして実行する
　Androidアプリの場合、エクスポートのオプションで、インターネットアクセスを許可する必要があります。

https://silentwolf.com/mobileExport

![2023-10-21 (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/aeb5c232-f643-3161-770a-72e8a93af7e5.png)

　Androidスマホで実行するとハイスコアが追加されました。

# 終わりに
　SILENTWOLFを使って、ハイスコアの保存と読み出しが簡単にできました。
　GDScript用のPluginがあり、簡単に組み込めるのでうれしいです。

以上です。