简单建立ui
This commit is contained in:
parent
0e68c6e666
commit
c154b11ed2
|
|
@ -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)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cmyknlur1bk2d
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://i1nipeyuught
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bmnwylphnuio7
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cdf7xcwgtd7jb
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dytq5bvr0l2rq
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
|
|
@ -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
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[gd_resource type="TileSet" load_steps=5 format=3 uid="uid://cup1q1upvp18h"]
|
[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://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"]
|
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_u6jqb"]
|
||||||
texture = ExtResource("2_mucy5")
|
texture = ExtResource("2_mucy5")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
@onready var b_setting: Button = %B_Setting
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://d1gf5wcjkwong
|
||||||
|
|
@ -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"]
|
[ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="1_j8fyu"]
|
||||||
|
|
||||||
[node name="MainMenu" type="Control"]
|
[node name="MainMenu" type="Control"]
|
||||||
|
|
@ -9,6 +10,7 @@ anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_102h7")
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
[node name="TextureRect" type="TextureRect" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
|
@ -51,6 +53,12 @@ layout_mode = 2
|
||||||
text = "Continue
|
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"]
|
[node name="B_Option" type="Button" parent="MC_Options/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Option"
|
text = "Option"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -37,6 +37,7 @@ enabled=PackedStringArray("res://addons/reedcamera/plugin.cfg", "res://addons/re
|
||||||
[file_customization]
|
[file_customization]
|
||||||
|
|
||||||
folder_colors={
|
folder_colors={
|
||||||
|
"res://__settings/": "red",
|
||||||
"res://_shared/": "pink",
|
"res://_shared/": "pink",
|
||||||
"res://_tileset/": "green"
|
"res://_tileset/": "green"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue