实现游戏设置 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 namespace CMSGame
{ {
/// <summary> public record class GameSettings
/// 游戏设置持久化
/// </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 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> /// <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 partial class AudioSettingsMenu : VBoxContainer
{ {
public GameSettings? GameSettings; public GameSettingsNode? Settings;
public HSlider? MasterVolumeSlider; public HSlider? MasterVolumeSlider;
@ -12,7 +12,7 @@ namespace CMSGame
public override void _Ready() public override void _Ready()
{ {
this.GetAutoloadNode(ref GameSettings, nameof(GameSettings)); this.GetAutoloadNode(ref Settings, nameof(GameSettingsNode));
MasterVolumeSlider = this.GetUniqueNode<HSlider>(nameof(MasterVolumeSlider)); MasterVolumeSlider = this.GetUniqueNode<HSlider>(nameof(MasterVolumeSlider));
MusicVolumeSlider = this.GetUniqueNode<HSlider>(nameof(MusicVolumeSlider)); MusicVolumeSlider = this.GetUniqueNode<HSlider>(nameof(MusicVolumeSlider));

@ -2,11 +2,11 @@ namespace CMSGame
{ {
public partial class SettingsMenu : TabContainer public partial class SettingsMenu : TabContainer
{ {
public GameSettings? Settings; public GameSettingsNode? Settings;
public override void _Ready() 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="."] [node name="BattleSettingsMenu" type="VBoxContainer" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
visible = false
layout_mode = 2 layout_mode = 2
script = ExtResource("2_xtjo5") script = ExtResource("2_xtjo5")
@ -28,6 +27,7 @@ script = ExtResource("3_68iki")
[node name="AudioSettingsMenu" type="VBoxContainer" parent="."] [node name="AudioSettingsMenu" type="VBoxContainer" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
visible = false
layout_mode = 2 layout_mode = 2
script = ExtResource("4_ciy5h") script = ExtResource("4_ciy5h")
@ -39,7 +39,8 @@ columns = 2
layout_mode = 2 layout_mode = 2
text = "主音量" 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 layout_mode = 2
max_value = 1.0 max_value = 1.0
step = 0.01 step = 0.01
@ -49,7 +50,7 @@ value = 0.8
layout_mode = 2 layout_mode = 2
text = "音乐音量" text = "音乐音量"
[node name="MusicVolumnSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"] [node name="MusicVolumeSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(200, 0) custom_minimum_size = Vector2(200, 0)
layout_mode = 2 layout_mode = 2
@ -61,7 +62,7 @@ value = 0.8
layout_mode = 2 layout_mode = 2
text = "音效音量" text = "音效音量"
[node name="SoundEffectVolumnSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"] [node name="SoundEffectVolumeSlider" type="HSlider" parent="AudioSettingsMenu/GridContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
max_value = 1.0 max_value = 1.0

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

@ -4,20 +4,24 @@
[ext_resource type="PackedScene" uid="uid://blk2uswpo2a7k" path="res://Components/Settings/SettingsMenu.tscn" id="2_qc1kk"] [ext_resource type="PackedScene" uid="uid://blk2uswpo2a7k" path="res://Components/Settings/SettingsMenu.tscn" id="2_qc1kk"]
[node name="SettingsMenuPopup" type="Popup"] [node name="SettingsMenuPopup" type="Popup"]
size = Vector2i(172, 101) size = Vector2i(800, 485)
visible = true visible = true
script = ExtResource("1_it4yp") script = ExtResource("1_it4yp")
[node name="VBoxContainer" type="VBoxContainer" parent="."] [node name="VBoxContainer" type="VBoxContainer" parent="."]
offset_right = 40.0 offset_right = 800.0
offset_bottom = 40.0 offset_bottom = 66.0
alignment = 1
[node name="SettingsMenu" parent="VBoxContainer" instance=ExtResource("2_qc1kk")] [node name="SettingsMenu" parent="VBoxContainer" instance=ExtResource("2_qc1kk")]
custom_minimum_size = Vector2(800, 450)
layout_mode = 2 layout_mode = 2
[node name="ConfirmButton" type="Button" parent="VBoxContainer"] [node name="HidePopupButton" type="Button" parent="VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(200, 0)
layout_mode = 2 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() public void On_SettingsPopupButton_Pressed()
{ {
GetNode<Popup>("%SettingsPopup").PopupCentered(); GetNode<Popup>("%SettingsMenuPopup").PopupCentered();
} }
public void On_QuitButton_Pressed() 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="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="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"] [node name="MainScene" type="Control"]
layout_mode = 3 layout_mode = 3
@ -10,7 +11,6 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
theme = ExtResource("1_y72s3")
script = ExtResource("1_kso8c") script = ExtResource("1_kso8c")
[node name="BackgroundImage" type="Label" parent="."] [node name="BackgroundImage" type="Label" parent="."]
@ -34,6 +34,7 @@ offset_top = -65.5
offset_right = 287.0 offset_right = 287.0
offset_bottom = 65.5 offset_bottom = 65.5
grow_vertical = 2 grow_vertical = 2
theme = ExtResource("1_y72s3")
theme_override_constants/separation = 16 theme_override_constants/separation = 16
[node name="StartButton" type="Button" parent="StartMenu"] [node name="StartButton" type="Button" parent="StartMenu"]
@ -48,6 +49,12 @@ text = "设置"
layout_mode = 2 layout_mode = 2
text = "退出" 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/StartButton" to="." method="On_BattleDemo1Button_Pressed"]
[connection signal="pressed" from="StartMenu/SettingsButton" to="." method="On_SettingsPopupButton_Pressed"] [connection signal="pressed" from="StartMenu/SettingsButton" to="." method="On_SettingsPopupButton_Pressed"]
[connection signal="pressed" from="StartMenu/QuitButton" to="." method="On_QuitButton_Pressed"] [connection signal="pressed" from="StartMenu/QuitButton" to="." method="On_QuitButton_Pressed"]

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

Loading…
Cancel
Save