简单建立ui

This commit is contained in:
Reed 2026-01-15 17:43:58 +08:00
parent 0e68c6e666
commit c154b11ed2
18 changed files with 381 additions and 2 deletions

108
__settings/SettingModel.gd Normal file
View File

@ -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)

View File

@ -0,0 +1 @@
uid://cmyknlur1bk2d

View File

@ -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,
}
}

View File

@ -0,0 +1 @@
uid://i1nipeyuught

View File

@ -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)

View File

@ -0,0 +1 @@
uid://bmnwylphnuio7

View File

@ -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,
}

View File

@ -0,0 +1 @@
uid://cdf7xcwgtd7jb

View File

@ -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

View File

@ -0,0 +1 @@
uid://dytq5bvr0l2rq

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -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

View File

@ -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")

View File

@ -0,0 +1,3 @@
extends Control
@onready var b_setting: Button = %B_Setting

View File

@ -0,0 +1 @@
uid://d1gf5wcjkwong

View File

@ -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"

View File

@ -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

View File

@ -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"
}