diff --git a/__settings/SettingModel.gd b/__settings/SettingModel.gd new file mode 100644 index 0000000..fb6880e --- /dev/null +++ b/__settings/SettingModel.gd @@ -0,0 +1,108 @@ +# scripts/settings/SettingsModel.gd +extends Node +class_name SettingsModel + +## =============================== +## Dependencies +## =============================== +const SettingsSchema = preload("res://__settings/SettingSchema.gd") +const SettingsStorage = preload("res://__settings/SettingStorage.gd") + +## =============================== +## Signals +## =============================== +signal setting_changed(section: String, key: String, value) +signal settings_applied +signal settings_reverted + +## =============================== +## Internal State +## =============================== +var _data_committed : Dictionary = {} # 已生效 / 已保存 +var _data_working : Dictionary = {} # UI 正在修改 +var _storage := SettingsStorage.new() + +## =============================== +## Lifecycle +## =============================== +func _ready() -> void: + _load() + +## =============================== +## Public API (Read) +## =============================== +func get_setting(section: String, key: String) -> Variant: + return _data_working.get(section, {}).get(key) + +func get_section(section: String) -> Dictionary: + return _data_working.get(section, {}).duplicate(true) + +## =============================== +## Public API (Write - Working Only) +## =============================== +func set_setting(section: String, key: String, value: Variant) -> void: + if not _data_working.has(section): + _data_working[section] = {} + + _data_working[section][key] = value + emit_signal("setting_changed", section, key, value) + +## =============================== +## Typed Getters +## =============================== +func get_bool(section: String, key: String) -> bool: + var value = get_setting(section, key) + return value if typeof(value) == TYPE_BOOL else false + +func get_int(section: String, key: String) -> int: + var value = get_setting(section, key) + return value if typeof(value) == TYPE_INT else 0 + +func get_float(section: String, key: String) -> float: + var value = get_setting(section, key) + if typeof(value) == TYPE_FLOAT or typeof(value) == TYPE_INT: + return float(value) + return 0.0 + +func get_string(section: String, key: String) -> String: + var value = get_setting(section, key) + return value if typeof(value) == TYPE_STRING else "" + +## =============================== +## Transaction Control +## =============================== +func apply() -> void: + _data_committed = _data_working.duplicate(true) + + for section in _data_committed.keys(): + for key in _data_committed[section].keys(): + _storage.set_value(section, key, _data_committed[section][key]) + + _storage.save() + emit_signal("settings_applied") + +func revert() -> void: + _data_working = _data_committed.duplicate(true) + emit_signal("settings_reverted") + +func has_unsaved_changes() -> bool: + return _data_working != _data_committed + +## =============================== +## Internal +## =============================== +func _load() -> void: + _storage.load() + + # 1. Load defaults + _data_committed = SettingsSchema.defaults().duplicate(true) + + # 2. Override with saved values + for section in _data_committed.keys(): + for key in _data_committed[section].keys(): + var default_value = _data_committed[section][key] + var value = _storage.get_value(section, key, default_value) + _data_committed[section][key] = value + + # 3. Init working copy + _data_working = _data_committed.duplicate(true) diff --git a/__settings/SettingModel.gd.uid b/__settings/SettingModel.gd.uid new file mode 100644 index 0000000..bdea12e --- /dev/null +++ b/__settings/SettingModel.gd.uid @@ -0,0 +1 @@ +uid://cmyknlur1bk2d diff --git a/__settings/SettingSchema.gd b/__settings/SettingSchema.gd new file mode 100644 index 0000000..613d1d7 --- /dev/null +++ b/__settings/SettingSchema.gd @@ -0,0 +1,22 @@ +class_name SettingSchema +extends RefCounted + +## =============================== +## 默认设置定义 +## =============================== +static func defaults() -> Dictionary: + return { + "audio": { + "master_volume": 1.0, + "music_volume": 0.8, + "sfx_volume": 0.8, + }, + "graphics": { + "quality": 2, # 0=low,1=mid,2=high + "fullscreen": true, + }, + "input": { + # 这里只是占位,后面可扩展 + "mouse_sensitivity": 1.0, + } + } diff --git a/__settings/SettingSchema.gd.uid b/__settings/SettingSchema.gd.uid new file mode 100644 index 0000000..fd3ce85 --- /dev/null +++ b/__settings/SettingSchema.gd.uid @@ -0,0 +1 @@ +uid://i1nipeyuught diff --git a/__settings/SettingStorage.gd b/__settings/SettingStorage.gd new file mode 100644 index 0000000..57386ec --- /dev/null +++ b/__settings/SettingStorage.gd @@ -0,0 +1,22 @@ +# scripts/settings/SettingsStorage.gd +extends RefCounted +class_name SettingsStorage + +const FILE_PATH := "user://settings.cfg" + +var _config := ConfigFile.new() + +func load() -> void: + _config.load(FILE_PATH) + +func save() -> void: + _config.save(FILE_PATH) + +func has(section: String, key: String) -> bool: + return _config.has_section_key(section, key) + +func get_value(section: String, key: String, default): + return _config.get_value(section, key, default) + +func set_value(section: String, key: String, value) -> void: + _config.set_value(section, key, value) diff --git a/__settings/SettingStorage.gd.uid b/__settings/SettingStorage.gd.uid new file mode 100644 index 0000000..5b40849 --- /dev/null +++ b/__settings/SettingStorage.gd.uid @@ -0,0 +1 @@ +uid://bmnwylphnuio7 diff --git a/__settings/input/InputSchema.gd b/__settings/input/InputSchema.gd new file mode 100644 index 0000000..8df38ea --- /dev/null +++ b/__settings/input/InputSchema.gd @@ -0,0 +1,35 @@ +extends RefCounted +class_name InputSchema + +static func defaults() -> Dictionary: + return { + "move_left": [ + _key(KEY_A), + _key(KEY_LEFT), + ], + "move_right": [ + _key(KEY_D), + _key(KEY_RIGHT), + ], + "jump": [ + _key(KEY_SPACE), + ], + "attack": [ + _mouse(MOUSE_BUTTON_LEFT), + ], + } + +## =============================== +## Helpers (序列化友好) +## =============================== +static func _key(keycode: int) -> Dictionary: + return { + "type": "key", + "keycode": keycode, + } + +static func _mouse(button: int) -> Dictionary: + return { + "type": "mouse", + "button": button, + } diff --git a/__settings/input/InputSchema.gd.uid b/__settings/input/InputSchema.gd.uid new file mode 100644 index 0000000..901abca --- /dev/null +++ b/__settings/input/InputSchema.gd.uid @@ -0,0 +1 @@ +uid://cdf7xcwgtd7jb diff --git a/__settings/input/InputSettingModel.gd b/__settings/input/InputSettingModel.gd new file mode 100644 index 0000000..048f93e --- /dev/null +++ b/__settings/input/InputSettingModel.gd @@ -0,0 +1,121 @@ +extends Node +class_name InputSettingsModel + +## =============================== +## Dependencies +## =============================== +const InputSchema = preload("res://__settings/input/InputSchema.gd") +const SettingsStorage = preload("res://__settings/SettingStorage.gd") + +## =============================== +## Signals +## =============================== +signal binding_changed(action: String) +signal bindings_applied +signal bindings_reverted + +## =============================== +## Internal State +## =============================== +var _bindings_committed : Dictionary = {} +var _bindings_working : Dictionary = {} +var _storage := SettingsStorage.new() + +const SECTION := "input" + +## =============================== +## Lifecycle +## =============================== +func _ready() -> void: + _load() + +## =============================== +## Public API (Read) +## =============================== +func get_bindings(action: String) -> Array: + return _bindings_working.get(action, []).duplicate(true) + +func get_all_bindings() -> Dictionary: + return _bindings_working.duplicate(true) + +## =============================== +## Public API (Write) +## =============================== +func set_bindings(action: String, bindings: Array) -> void: + _bindings_working[action] = bindings + emit_signal("binding_changed", action) + +func add_binding(action: String, binding: Dictionary) -> void: + if not _bindings_working.has(action): + _bindings_working[action] = [] + _bindings_working[action].append(binding) + emit_signal("binding_changed", action) + +func clear_bindings(action: String) -> void: + _bindings_working[action] = [] + emit_signal("binding_changed", action) + +## =============================== +## Transaction +## =============================== +func apply() -> void: + _bindings_committed = _bindings_working.duplicate(true) + _save_to_storage() + _apply_to_input_map() + emit_signal("bindings_applied") + +func revert() -> void: + _bindings_working = _bindings_committed.duplicate(true) + emit_signal("bindings_reverted") + +func has_unsaved_changes() -> bool: + return _bindings_working != _bindings_committed + +## =============================== +## Internal +## =============================== +func _load() -> void: + _storage.load() + + # 1. defaults + _bindings_committed = InputSchema.defaults().duplicate(true) + + # 2. override from storage + for action in _bindings_committed.keys(): + var saved = _storage.get_value(SECTION, action, null) + if saved != null: + _bindings_committed[action] = saved + + _bindings_working = _bindings_committed.duplicate(true) + + # 3. apply to engine at startup + _apply_to_input_map() + +func _save_to_storage() -> void: + for action in _bindings_committed.keys(): + _storage.set_value(SECTION, action, _bindings_committed[action]) + _storage.save() + +func _apply_to_input_map() -> void: + for action in _bindings_committed.keys(): + if not InputMap.has_action(action): + InputMap.add_action(action) + + InputMap.action_erase_events(action) + + for binding in _bindings_committed[action]: + var event = _binding_to_event(binding) + if event: + InputMap.action_add_event(action, event) + +func _binding_to_event(binding: Dictionary) -> InputEvent: + match binding.get("type"): + "key": + var e := InputEventKey.new() + e.keycode = binding.get("keycode", 0) + return e + "mouse": + var e := InputEventMouseButton.new() + e.button_index = binding.get("button", 0) + return e + return null diff --git a/__settings/input/InputSettingModel.gd.uid b/__settings/input/InputSettingModel.gd.uid new file mode 100644 index 0000000..347352e --- /dev/null +++ b/__settings/input/InputSettingModel.gd.uid @@ -0,0 +1 @@ +uid://dytq5bvr0l2rq diff --git a/_asset/ksw/未命名作品(4).png b/_asset/ksw/未命名作品(4).png new file mode 100644 index 0000000..69ce227 Binary files /dev/null and b/_asset/ksw/未命名作品(4).png differ diff --git a/_asset/ksw/未命名作品(4).png.import b/_asset/ksw/未命名作品(4).png.import new file mode 100644 index 0000000..97d3cc1 --- /dev/null +++ b/_asset/ksw/未命名作品(4).png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cacik4t6grtq8" +path="res://.godot/imported/未命名作品(4).png-151765b68adadae09c88456ac264362b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://_asset/ksw/未命名作品(4).png" +dest_files=["res://.godot/imported/未命名作品(4).png-151765b68adadae09c88456ac264362b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/_tileset/level1.tres b/_tileset/level1.tres index ff0c99b..bd4909e 100644 --- a/_tileset/level1.tres +++ b/_tileset/level1.tres @@ -1,7 +1,7 @@ [gd_resource type="TileSet" load_steps=5 format=3 uid="uid://cup1q1upvp18h"] [ext_resource type="Texture2D" uid="uid://dd622t4mw5vva" path="res://_asset/ksw/basicTile01.png" id="2_mucy5"] -[ext_resource type="Texture2D" uid="uid://cwet2kw1mngmf" path="res://_asset/ksw/basicTile02.png" id="3_u6jqb"] +[ext_resource type="Texture2D" uid="uid://dufe0liirugbw" path="res://_asset/ksw/basicTile02.png" id="3_u6jqb"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_u6jqb"] texture = ExtResource("2_mucy5") diff --git a/_ui/main_menu/main_menu.gd b/_ui/main_menu/main_menu.gd new file mode 100644 index 0000000..84eba95 --- /dev/null +++ b/_ui/main_menu/main_menu.gd @@ -0,0 +1,3 @@ +extends Control + +@onready var b_setting: Button = %B_Setting diff --git a/_ui/main_menu/main_menu.gd.uid b/_ui/main_menu/main_menu.gd.uid new file mode 100644 index 0000000..3107c82 --- /dev/null +++ b/_ui/main_menu/main_menu.gd.uid @@ -0,0 +1 @@ +uid://d1gf5wcjkwong diff --git a/_ui/main_menu/main_menu.tscn b/_ui/main_menu/main_menu.tscn index fd1483e..84deb12 100644 --- a/_ui/main_menu/main_menu.tscn +++ b/_ui/main_menu/main_menu.tscn @@ -1,5 +1,6 @@ -[gd_scene load_steps=2 format=3 uid="uid://b4ojkr2fq8xjm"] +[gd_scene load_steps=3 format=3 uid="uid://b4ojkr2fq8xjm"] +[ext_resource type="Script" uid="uid://d1gf5wcjkwong" path="res://_ui/main_menu/main_menu.gd" id="1_102h7"] [ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="1_j8fyu"] [node name="MainMenu" type="Control"] @@ -9,6 +10,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +script = ExtResource("1_102h7") [node name="TextureRect" type="TextureRect" parent="."] layout_mode = 1 @@ -51,6 +53,12 @@ layout_mode = 2 text = "Continue " +[node name="B_Setting" type="Button" parent="MC_Options/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Setting +" + [node name="B_Option" type="Button" parent="MC_Options/VBoxContainer"] layout_mode = 2 text = "Option" diff --git a/_ui/setting/AudioSettingPanel.tscn b/_ui/setting/AudioSettingPanel.tscn new file mode 100644 index 0000000..6bfa669 --- /dev/null +++ b/_ui/setting/AudioSettingPanel.tscn @@ -0,0 +1,13 @@ +[gd_scene format=3 uid="uid://dlxtqk6hbqu1"] + +[node name="AudioSettingPanel" type="MarginContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +size_flags_horizontal = 4 diff --git a/project.godot b/project.godot index 093486a..05b6267 100644 --- a/project.godot +++ b/project.godot @@ -37,6 +37,7 @@ enabled=PackedStringArray("res://addons/reedcamera/plugin.cfg", "res://addons/re [file_customization] folder_colors={ +"res://__settings/": "red", "res://_shared/": "pink", "res://_tileset/": "green" }