【Godot 4.0】スマホ３Dゲームを作るための勉強　その２３　ゲームシステムを作る　その８　設定を保存する

　ゲームエンジンGodot4.0で3Dスマホゲームを作りたいと思いますが、その前にお勉強しています。
　2023/3/1にstable版がリリースされました。
 [Godot_v4.0-stable_win64.exe.zip](https://godotengine.org/download/windows/)を使用しています。

# 目的
　ゲームシステムをつくっていきます。
　設定をPlayerの発射間隔に反映します。また保存できるようにします。

# ベースプロジェクト
　下記で作成したプロジェクトをベースに機能追加をします。
　【Godot 4.0】スマホ３Dゲームを作るための勉強　その２２　ゲームシステムを作る　その７　PopupPanelをデザインする
　https://qiita.com/FootInGlow/items/8dd07805f4c8f2f72cb2
　
　github(Godotのプロジェクトマネージャーからインポートして利用できます）
　https://github.com/footinglow/Godot4/tree/main/02_study/S22_GameSystem_007

# 設定を適用する
　PopupPanelで変更した発射間隔を保存して、Player.tscnに反映するようにします。

## 設定値を保存するスクリプトを追加する
　スクリプトを新規追加します。
　画面上部の「Script」をクリックして、ファイルメニューの「新規スクリプト」を実行します。
![スクリーンショット (128).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/1616cc6a-f325-7422-c0a2-6cb4f1807fde.png)
「global_settings.gd」という名前にして「作成」ボタンを押下します。
![スクリーンショット (129).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/6dc5a5fa-d0d8-20b7-260a-a96718a05744.png)
追加されました。
![スクリーンショット (130).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/a93ee555-1a16-2d20-fe38-0c7957d05c9d.png)

## シングルトン（自動読み込み）としてAutoloadに登録する
　シーン間（Title.tscn、game_system.tscn)で共通の設定変数を定義するスクリプトはシングルトン（自動読み込み）として登録します。

　プロジェクトメニューからプロジェクト設定を実行します。
　パスに「res://global_settings.gd」を登録します。設定されたノード名を確認して、「追加」ボタンを押下します。
![スクリーンショット (131).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/1349fb05-1615-d428-f256-cddac7268d46.png)
　追加されました。
　「グローバル変数」の「有効」にチェックがついていることを確認します。チェックが入っていると、どのスクリプトからでも名前"GlobalSettings"でアクセスできるようになります。
![スクリーンショット (132).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/2c4759a2-721f-77e0-691d-fe16112d8c7a.png)

## 設定値を保存する変数を追加する。
　res://global_settings.gdを開きます。
　下記のように修正して、設定値を保存する変数を定義します。

```GDScript
extends Node

var m_firing_timing_sec = 1.0
```

## PopupPanelで設定する
　PopupPanelのスライダーで変更し、「設定完了」ボタンを押下したら、m_firintiming_secを更新します。

　res://settings_popup_panel.gdを開きます。

```GDScript
extends PopupPanel

func open():
	get_tree().paused = true
	popup_centered()

func _on_h_slider_drag_ended(value_changed):
	$ControlBaseSize/VBoxContainer/MarginContainer/Label.text = "発射間隔 %.1f秒" % $ControlBaseSize/VBoxContainer/MarginContainer2/HSlider.value

func _on_button_pressed():
	GlobalSettings.m_firing_timing_sec = $ControlBaseSize/VBoxContainer/MarginContainer2/HSlider.value
	hide()

func _on_popup_hide():
	get_tree().paused = false
```

　HSlider.valueに設定された0.1～1.0の値を、_on_button_pressed()メソッドが呼ばれたときにGlobalSettings.m_firing_timing_secに設定する処理を追加しました。

## 発射間隔を適用する
　res://Characters/player.tscnを開きます。Timer_fireを選択してインスペクタを確認します。
　Timer_fireに設定したタイマーの間隔が発射間隔なので、スクリプトから変更します。
　![スクリーンショット (133).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/bcb4ab8b-0c23-3936-e3da-34b5f954790a.png)

　res://Characters/Player.gdを開きます。
　下記の処理を追加しましょう。

```GDScript
func _ready():
	$Timer_fire.wait_time = GlobalSettings.m_firing_timing_sec
```
　「Game Start!」ボタンが押下されるとGameSystem.tscnが新規に生成されて、その中でPlayer.tscnが生成、スクリプトの_ready()が呼ばれます。
　Timerノードの待ち時間はwait_timeに設定すると変更できます。

　実行します。発射間隔を0.1秒にするとたくさん発射されるようになりました。
![スクリーンショット (135).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/da679431-ecaa-736e-275c-8ec416e300f9.png)
![スクリーンショット (137).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/421c895c-9538-8a0c-7dbb-8c2b38ecd72c.png)
# 保存する
　ConfigFileクラスを使用して保存・読み出しをします。
　ConfigFileクラスを使用すると、カテゴリ＋キーに対して値を保存・読み出しできるようになります。今回はカテゴリを"Player"、キーを"firing_timing"にして、発射間隔0.1～1.0の値を保存・読み出しするようにします。
　あと、ファイル名を決める必要があるので、「config.cfg」にします。
　
　res://global_settings.gdを開きます。
　下記のように修正します。

```GDScript
extends Node

var m_firing_timing_sec = 1.0

const M_FILE_NAME = "user://config.cfg"

func _ready():
	# ConfigFile生成
	var config = ConfigFile.new()
	# ファイルから読み出し
	var err = config.load(M_FILE_NAME)
	# エラーの場合終了（最初の軌道の場合はファイルがない）
	if err != OK:
		return
	# クション"Player"のキー"firing_timing"に対する値を読み出す
	m_firing_timing_sec = config.get_value("Player", "firing_timing")

func save():
	# ConfigFile生成
	var config = ConfigFile.new()
	# セクション"Player"のキー"firing_timing"に対して、m_firing_timing_secを保存する
	config.set_value("Player", "firing_timing", m_firing_timing_sec)

	# ファイルに保存する (上書き).
	config.save(M_FILE_NAME)
```
　急に処理がたくさんありますが、カスタマイズするとしたら下記の部分です。

* **func _ready(): Configファイルの読み出し**
 　m_firing_timing_sec = config.get_value("Player", "firing_timing")
　カテゴリ"Player"＋キー"firing_timing"で保存している値をm_firing_timing_secに読み出します。

* **func save():: Configファイルの保存**
　config.set_value("Player", "firing_timing", m_firing_timing_sec)
　カテゴリ"Player"＋キー"firing_timing"に対して、m_firing_timing_secを値として保存するという指定です。


　次にPopupPanelの設定完了ボタンを押下したときに保存するようにします。
　res://settings_popup_panel.gdを開きます。
　func _on_button_pressed()メソッドに「GlobalSettings.save()」の1行を追加して、設定を保存するようにしました。
```GDScript
extends PopupPanel

func open():
	get_tree().paused = true
	popup_centered()

func _on_h_slider_drag_ended(value_changed):
	$ControlBaseSize/VBoxContainer/MarginContainer/Label.text = "発射間隔 %.1f秒" % $ControlBaseSize/VBoxContainer/MarginContainer2/HSlider.value

func _on_button_pressed():
	GlobalSettings.m_firing_timing_sec = $ControlBaseSize/VBoxContainer/MarginContainer2/HSlider.value
	GlobalSettings.save()
	hide()

func _on_popup_hide():
	get_tree().paused = false

```
　ファイルが保存されるパスはプラットフォームごとに異なっています。
> Godot Engine 4.0 documentation in English
> File and data I/O File paths in Godot projects
>https://docs.godotengine.org/en/latest/tutorials/io/data_paths.html#accessing-persistent-user-data-user

　Windowsで実行した場合は「Windows: %APPDATA%\Godot\app_userdata\[project_name]」にあるようです。
　C:\Users\......\AppData\Roaming\Godot\app_userdata\S23_GameSystem_008に「config.cfg」というファイルが生成されました。
　中を見るとテキストファイルで、下記のように保存されていました。
```
[Player]

firing_timing=0.1
```


　以上です。