实现游戏设置 UI

master
lightyears 2 years ago
parent 10d0008017
commit 9c7fc660cc
Signed by: lightyears
GPG Key ID: 98D80DDF26D4F2F9

@ -1,118 +1,16 @@
using Newtonsoft.Json;
namespace CMSGame
{
/// <summary>
/// 游戏设置持久化
/// </summary>
public partial class GameSettings : Node
{
protected Dictionary<Type, SettingsBase> CurrentSettings = new();
protected Dictionary<Type, SettingsBase> OriginalSettings = new();
protected Dictionary<Type, string> SettingsPaths = new();
public BattleSettings BattleSettings => GetSettings<BattleSettings>();
public VideoSettings VideoSettings => GetSettings<VideoSettings>();
public AudioSettings AudioSettings => GetSettings<AudioSettings>();
public GameSettings()
{
RegisterAllSettings();
}
public override void _Ready()
{
MakeDirectories();
LoadAllSettings();
}
protected void RegisterAllSettings()
{
RegisterSettings<BattleSettings>("BattleSettings.json");
RegisterSettings<VideoSettings>("VideoSettings.json");
RegisterSettings<AudioSettings>("AudioSettings.json");
}
protected void RegisterSettings<TSettings>(string filename) where TSettings : SettingsBase, new()
{
var defaultSettings = new TSettings();
CurrentSettings.Add(typeof(TSettings), defaultSettings);
SettingsPaths.Add(typeof(TSettings), new GodotPath($"user://Settings/{filename}"));
}
protected void LoadAllSettings()
{
foreach (var settingsType in SettingsPaths.Keys)
{
LoadSettings(settingsType);
}
}
public override void _ExitTree()
{
foreach (var settingsType in SettingsPaths.Keys)
{
if (!OriginalSettings.ContainsKey(settingsType) || OriginalSettings[settingsType] != CurrentSettings[settingsType])
{
SaveSettings(settingsType);
}
}
}
private static void MakeDirectories()
{
DirAccess.MakeDirRecursiveAbsolute("user://Settings/");
}
private void LoadSettings(Type settingsType)
{
string settingsText = ReadFileAsString(SettingsPaths[settingsType]);
var settings = JsonConvert.DeserializeObject(settingsText, settingsType);
if (settings != null)
{
OriginalSettings[settingsType] = (SettingsBase)settings;
CurrentSettings[settingsType] = (SettingsBase)settings;
}
}
public TSettings GetSettings<TSettings>() where TSettings : SettingsBase, new()
{
return (TSettings)CurrentSettings[typeof(TSettings)];
}
private static string ReadFileAsString(string path)
{
if (FileAccess.FileExists(path))
{
using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
return file.GetAsText();
}
return "null";
}
private void SaveSettings(Type settingsType)
{
string settingsText = JsonConvert.SerializeObject(CurrentSettings[settingsType]);
using var file = FileAccess.Open(SettingsPaths[settingsType], FileAccess.ModeFlags.Write);
file.StoreString(settingsText);
}
}
public record class SettingsBase
public record class GameSettings
{
}
public record class BattleSettings : SettingsBase
public record class BattleSettings : GameSettings
{ }
public record class VideoSettings : SettingsBase
public record class VideoSettings : GameSettings
{ }
public record class AudioSettings : SettingsBase
public record class AudioSettings : GameSettings
{
/// <summary>
/// 音乐音量

@ -0,0 +1,106 @@
using Newtonsoft.Json;
namespace CMSGame
{
/// <summary>
/// 游戏设置持久化节点
///
/// 在游戏开始时载入用户设置,并在游戏关闭时保存用户设置。
/// </summary>
public partial class GameSettingsNode : Node
{
protected Dictionary<Type, GameSettings> CurrentSettings = new();
protected Dictionary<Type, GameSettings> OriginalSettings = new();
protected Dictionary<Type, string> SettingsPaths = new();
public BattleSettings BattleSettings => GetSettings<BattleSettings>();
public VideoSettings VideoSettings => GetSettings<VideoSettings>();
public AudioSettings AudioSettings => GetSettings<AudioSettings>();
public GameSettingsNode()
{
RegisterAllSettings();
}
public override void _Ready()
{
MakeDirectories();
LoadAllSettings();
}
protected void RegisterAllSettings()
{
RegisterSettings<BattleSettings>("BattleSettings.json");
RegisterSettings<VideoSettings>("VideoSettings.json");
RegisterSettings<AudioSettings>("AudioSettings.json");
}
protected void RegisterSettings<TSettings>(string filename) where TSettings : GameSettings, new()
{
var defaultSettings = new TSettings();
CurrentSettings.Add(typeof(TSettings), defaultSettings);
SettingsPaths.Add(typeof(TSettings), new GodotPath($"user://Settings/{filename}"));
}
protected void LoadAllSettings()
{
foreach (var settingsType in SettingsPaths.Keys)
{
LoadSettings(settingsType);
}
}
public override void _ExitTree()
{
foreach (var settingsType in SettingsPaths.Keys)
{
if (!OriginalSettings.ContainsKey(settingsType) || OriginalSettings[settingsType] != CurrentSettings[settingsType])
{
SaveSettings(settingsType);
}
}
}
private static void MakeDirectories()
{
DirAccess.MakeDirRecursiveAbsolute("user://Settings/");
}
private void LoadSettings(Type settingsType)
{
string settingsText = ReadFileAsString(SettingsPaths[settingsType]);
var settings = JsonConvert.DeserializeObject(settingsText, settingsType);
if (settings != null)
{
OriginalSettings[settingsType] = (GameSettings)settings;
CurrentSettings[settingsType] = (GameSettings)settings;
}
}
public TSettings GetSettings<TSettings>() where TSettings : GameSettings, new()
{
return (TSettings)CurrentSettings[typeof(TSettings)];
}
private static string ReadFileAsString(string path)
{
if (FileAccess.FileExists(path))
{
using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
return file.GetAsText();
}
return "null";
}
private void SaveSettings(Type settingsType)
{
string settingsText = JsonConvert.SerializeObject(CurrentSettings[settingsType]);
using var file = FileAccess.Open(SettingsPaths[settingsType], FileAccess.ModeFlags.Write);
file.StoreString(settingsText);
}
}
}

@ -2,7 +2,7 @@ namespace CMSGame
{
public partial class AudioSettingsMenu : VBoxContainer
{
public GameSettings? GameSettings;
public GameSettingsNode? Settings;
public HSlider? MasterVolumeSlider;
@ -12,7 +12,7 @@ namespace CMSGame
public override void _Ready()
{
this.GetAutoloadNode(ref GameSettings, nameof(GameSettings));
this.GetAutoloadNode(ref Settings, nameof(GameSettingsNode));
MasterVolumeSlider = this.GetUniqueNode<HSlider>(nameof(MasterVolumeSlider));
MusicVolumeSlider = this.GetUniqueNode<HSlider>(nameof(MusicVolumeSlider));

@ -2,11 +2,11 @@ namespace CMSGame
{
public partial class SettingsMenu : TabContainer
{
public GameSettings? Settings;
public GameSettingsNode? Settings;
public override void _Ready()
{
this.GetAutoloadNode(ref Settings, nameof(GameSettings));
this.GetAutoloadNode(ref Settings, nameof(GameSettingsNode));
}
}
}

@ -16,7 +16,6 @@ script = ExtResource("1_av57b")
[node name="BattleSettingsMenu" type="VBoxContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
script = ExtResource("2_xtjo5")
@ -28,6 +27,7 @@ script = ExtResource("3_68iki")
[node name="AudioSettingsMenu" type="VBoxContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
script = ExtResource("4_ciy5h")
@ -39,7 +39,8 @@ columns = 2
layout_mode = 2
text = "主音量"
[node name="MasterVolumnSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
[node name="MasterVolumeSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
max_value = 1.0
step = 0.01
@ -49,7 +50,7 @@ value = 0.8
layout_mode = 2
text = "音乐音量"
[node name="MusicVolumnSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
[node name="MusicVolumeSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
@ -61,7 +62,7 @@ value = 0.8
layout_mode = 2
text = "音效音量"
[node name="SoundEffectVolumnSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
[node name="SoundEffectVolumeSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
max_value = 1.0

@ -2,7 +2,7 @@ namespace CMSGame
{
public partial class SettingsMenuPopup : Popup
{
public void On_ConfirmButton_Pressed()
public void On_HidePopupButton_Pressed()
{
Hide();
}

@ -4,20 +4,24 @@
[ext_resource type="PackedScene" uid="uid://blk2uswpo2a7k" path="res://Components/Settings/SettingsMenu.tscn" id="2_qc1kk"]
[node name="SettingsMenuPopup" type="Popup"]
size = Vector2i(172, 101)
size = Vector2i(800, 485)
visible = true
script = ExtResource("1_it4yp")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
offset_right = 40.0
offset_bottom = 40.0
offset_right = 800.0
offset_bottom = 66.0
alignment = 1
[node name="SettingsMenu" parent="VBoxContainer" instance=ExtResource("2_qc1kk")]
custom_minimum_size = Vector2(800, 450)
layout_mode = 2
[node name="ConfirmButton" type="Button" parent="VBoxContainer"]
[node name="HidePopupButton" type="Button" parent="VBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
text = "保存并退出"
size_flags_horizontal = 4
text = "关闭"
[connection signal="pressed" from="VBoxContainer/ConfirmButton" to="." method="On_ConfirmButton_Pressed"]
[connection signal="pressed" from="VBoxContainer/HidePopupButton" to="." method="On_HidePopupButton_Pressed"]

@ -4,7 +4,7 @@ namespace CMSGame
{
public void On_SettingsPopupButton_Pressed()
{
GetNode<Popup>("%SettingsPopup").PopupCentered();
GetNode<Popup>("%SettingsMenuPopup").PopupCentered();
}
public void On_QuitButton_Pressed()

@ -1,7 +1,8 @@
[gd_scene load_steps=3 format=3 uid="uid://c3ovsoq6o7y3t"]
[gd_scene load_steps=4 format=3 uid="uid://c3ovsoq6o7y3t"]
[ext_resource type="Script" path="res://Scenes/MainScene.cs" id="1_kso8c"]
[ext_resource type="Theme" uid="uid://cn55cr5w4yy3n" path="res://Themes/UI.tres" id="1_y72s3"]
[ext_resource type="PackedScene" uid="uid://cslqihnfw0me2" path="res://Components/Settings/SettingsMenuPopup.tscn" id="3_1d7q7"]
[node name="MainScene" type="Control"]
layout_mode = 3
@ -10,7 +11,6 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_y72s3")
script = ExtResource("1_kso8c")
[node name="BackgroundImage" type="Label" parent="."]
@ -34,6 +34,7 @@ offset_top = -65.5
offset_right = 287.0
offset_bottom = 65.5
grow_vertical = 2
theme = ExtResource("1_y72s3")
theme_override_constants/separation = 16
[node name="StartButton" type="Button" parent="StartMenu"]
@ -48,6 +49,12 @@ text = "设置"
layout_mode = 2
text = "退出"
[node name="SettingsMenuPopup" parent="." instance=ExtResource("3_1d7q7")]
unique_name_in_owner = true
title = "游戏设置"
visible = false
borderless = false
[connection signal="pressed" from="StartMenu/StartButton" to="." method="On_BattleDemo1Button_Pressed"]
[connection signal="pressed" from="StartMenu/SettingsButton" to="." method="On_SettingsPopupButton_Pressed"]
[connection signal="pressed" from="StartMenu/QuitButton" to="." method="On_QuitButton_Pressed"]

@ -22,7 +22,7 @@ config/icon="res://icon.svg"
[autoload]
GameSettings="*res://Autoloads/GameSettings.cs"
GameSettingsNode="*res://Autoloads/GameSettingsNode.cs"
BackgroundMusicPlayer="*res://Autoloads/BackgroundMusicPlayer.tscn"
[display]

Loading…
Cancel
Save