From d56af0f0c65aea5194b5f2caef8b3fa56a20944b Mon Sep 17 00:00:00 2001 From: RedisTKey Date: Sat, 3 Jan 2026 23:21:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=B4=9A=E4=BA=86=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=9A=84API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _game/LevelDemonstration.tscn | 55 ++++- _game/scenes/l_1_s_1.tscn | 12 -- _game/scenes/l_1_s_2.tscn | 12 -- addons/reedscene/act/ActManagerInspector.gd | 91 +-------- addons/reedscene/act/StateDrapSlot.tscn | 130 ++++++++++++ addons/reedscene/act/StateDropArea.gd | 90 ++++++++ addons/reedscene/act/StateDropArea.gd.uid | 1 + addons/reedscene/act/StateDropSlot.gd | 179 ++++++++++++++++ addons/reedscene/act/StateDropSlot.gd.uid | 1 + addons/reedscene/act/StateResolveUtils.gd | 193 ++++++++++++++++++ addons/reedscene/act/StateResolveUtils.gd.uid | 1 + addons/reedscene/act/icon/inform.svg | 1 + addons/reedscene/act/icon/inform.svg.import | 43 ++++ addons/reedscene/prop/PropComponent.gd | 186 +++++++++++------ addons/reedscene/prop/PropState.gd | 3 +- addons/reedscene/prop/StateManager.gd | 40 +++- addons/reedscene/reedscene.gd | 115 ++++++++--- addons/reedscene/update.md | 2 +- 18 files changed, 940 insertions(+), 215 deletions(-) create mode 100644 addons/reedscene/act/StateDrapSlot.tscn create mode 100644 addons/reedscene/act/StateDropArea.gd create mode 100644 addons/reedscene/act/StateDropArea.gd.uid create mode 100644 addons/reedscene/act/StateDropSlot.gd create mode 100644 addons/reedscene/act/StateDropSlot.gd.uid create mode 100644 addons/reedscene/act/StateResolveUtils.gd create mode 100644 addons/reedscene/act/StateResolveUtils.gd.uid create mode 100644 addons/reedscene/act/icon/inform.svg create mode 100644 addons/reedscene/act/icon/inform.svg.import diff --git a/_game/LevelDemonstration.tscn b/_game/LevelDemonstration.tscn index 1756ef3..ea96b93 100644 --- a/_game/LevelDemonstration.tscn +++ b/_game/LevelDemonstration.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=3 uid="uid://bj2318o3y68x2"] +[gd_scene load_steps=15 format=3 uid="uid://bj2318o3y68x2"] [ext_resource type="Script" uid="uid://ds6jy3s0hhmwt" path="res://_game/DemoScript.gd" id="1_2tycc"] [ext_resource type="PackedScene" uid="uid://cvqehvdjpoar4" path="res://_player/player_controller.tscn" id="2_gslp7"] @@ -6,13 +6,18 @@ [ext_resource type="PackedScene" uid="uid://bt55vmoc83l6g" path="res://_game/scenes/l_1_s_1.tscn" id="4_6jw57"] [ext_resource type="PackedScene" uid="uid://c6and5mqr3wv1" path="res://_game/scenes/l_1_s_2.tscn" id="5_2t6pm"] [ext_resource type="PackedScene" uid="uid://0sivr6aig7gm" path="res://_game/scenes/l_1_s_3.tscn" id="6_xkd7q"] +[ext_resource type="Script" uid="uid://5e157vdk6175" path="res://addons/reedscene/scene/ReedScene.gd" id="6_xotud"] +[ext_resource type="Script" uid="uid://bh066o84byplh" path="res://addons/reedscene/scene/ReedSceneID.gd" id="7_2tycc"] +[ext_resource type="Script" uid="uid://dn0ksjoswquf5" path="res://addons/reedscene/scene/SceneManager.gd" id="8_3ihdv"] +[ext_resource type="Script" uid="uid://dsgl7lbyjsiif" path="res://addons/reedscene/act/ActManager.gd" id="9_hc6q0"] +[ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="10_mwuv1"] +[ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="12_m325v"] +[ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="13_j8v5a"] +[ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="14_acyif"] [node name="Game" type="Node2D"] script = ExtResource("1_2tycc") -[node name="PlayerController" parent="." node_paths=PackedStringArray("auto_controlled_avatar") instance=ExtResource("2_gslp7")] -auto_controlled_avatar = NodePath("../Avatar") - [node name="Avatar" parent="." instance=ExtResource("3_6jw57")] position = Vector2(283, 253) collision_mask = 4 @@ -24,3 +29,45 @@ debug_log = true [node name="l1_s3" parent="." instance=ExtResource("6_xkd7q")] debug_log = true + +[node name="ReedScene" type="Node2D" parent="."] +script = ExtResource("6_xotud") +metadata/_custom_type_script = "uid://5e157vdk6175" + +[node name="[Invalid!]" type="Node" parent="ReedScene"] +script = ExtResource("7_2tycc") + +[node name="SceneManager" type="Node" parent="ReedScene"] +script = ExtResource("8_3ihdv") + +[node name="ActManager" type="Node" parent="ReedScene"] +script = ExtResource("9_hc6q0") + +[node name="Props" type="Node2D" parent="ReedScene"] +script = ExtResource("10_mwuv1") + +[node name="PlayerController" parent="ReedScene/Props" node_paths=PackedStringArray("auto_controlled_avatar") instance=ExtResource("2_gslp7")] +auto_controlled_avatar = NodePath("../../../Avatar") + +[node name="[Prop_0000]" type="Node" parent="ReedScene/Props/PlayerController"] +script = ExtResource("12_m325v") +prop_id = 0 + +[node name="States" type="Node" parent="ReedScene/Props/PlayerController/[Prop_0000]"] +script = ExtResource("13_j8v5a") + +[node name="[ID_0] 修改修改修改" type="Node" parent="ReedScene/Props/PlayerController/[Prop_0000]/States"] +script = ExtResource("14_acyif") +state_id = 0 + +[node name="[ID_1] 修改名稱" type="Node" parent="ReedScene/Props/PlayerController/[Prop_0000]/States"] +script = ExtResource("14_acyif") +state_id = 1 + +[node name="[ID_2] 修改名稱3" type="Node" parent="ReedScene/Props/PlayerController/[Prop_0000]/States"] +script = ExtResource("14_acyif") +state_id = 2 + +[node name="[ID_3] 修改名稱4" type="Node" parent="ReedScene/Props/PlayerController/[Prop_0000]/States"] +script = ExtResource("14_acyif") +state_id = 3 diff --git a/_game/scenes/l_1_s_1.tscn b/_game/scenes/l_1_s_1.tscn index 2ddd013..2c7b7b3 100644 --- a/_game/scenes/l_1_s_1.tscn +++ b/_game/scenes/l_1_s_1.tscn @@ -67,9 +67,6 @@ prop_state_map = Dictionary[int, ExtResource("6_fyfyw")]({ [sub_resource type="Resource" id="Resource_fyfyw"] script = ExtResource("12_fyfyw") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"reset_door_state_id" @@ -78,9 +75,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk" [sub_resource type="Resource" id="Resource_bco80"] script = ExtResource("12_fyfyw") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"door_close" @@ -88,9 +82,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk" [sub_resource type="Resource" id="Resource_22pon"] script = ExtResource("12_fyfyw") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"reset_door_state_id" @@ -99,9 +90,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk" [sub_resource type="Resource" id="Resource_25twt"] script = ExtResource("12_fyfyw") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"door_open" diff --git a/_game/scenes/l_1_s_2.tscn b/_game/scenes/l_1_s_2.tscn index 1668d8a..785e9f0 100644 --- a/_game/scenes/l_1_s_2.tscn +++ b/_game/scenes/l_1_s_2.tscn @@ -73,9 +73,6 @@ prop_state_map = Dictionary[int, ExtResource("6_d8y7x")]({ [sub_resource type="Resource" id="Resource_fyfyw"] script = ExtResource("12_jhhb1") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"reset_door_state_id" @@ -84,9 +81,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk" [sub_resource type="Resource" id="Resource_bco80"] script = ExtResource("12_jhhb1") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"door_close" @@ -94,9 +88,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk" [sub_resource type="Resource" id="Resource_22pon"] script = ExtResource("12_jhhb1") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"reset_door_state_id" @@ -105,9 +96,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk" [sub_resource type="Resource" id="Resource_25twt"] script = ExtResource("12_jhhb1") -child_node_name = null -recursive_found = null -owned_node_only = null effect_type = 1 value = null func_name = &"door_open" diff --git a/addons/reedscene/act/ActManagerInspector.gd b/addons/reedscene/act/ActManagerInspector.gd index 5ee3235..327e718 100644 --- a/addons/reedscene/act/ActManagerInspector.gd +++ b/addons/reedscene/act/ActManagerInspector.gd @@ -1,96 +1,15 @@ @tool extends EditorInspectorPlugin -# ✅ 由 EditorPlugin 注入 -var editor_interface: EditorInterface +const SLOT_SCENE:= preload("res://addons/reedscene/act/StateDrapSlot.tscn") func _can_handle(object) -> bool: return object is ActManager func _parse_begin(object) -> void: - var act_manager := object as ActManager - var reed_scene := _get_owner_scene(act_manager) + var root := VBoxContainer.new() - var text := "" - text += "Act Table (Debug)\n" - text += "Act Count: %d\n" % act_manager.prop_state_map.size() - text += "------------------\n" + var slot := SLOT_SCENE.instantiate() + root.add_child(slot) - if reed_scene == null: - text += "ERROR: ActManager must be a direct child of ReedScene.\n" - text += "Current parent: %s\n" % (act_manager.get_parent().name if act_manager.get_parent() else "") - - var label := Label.new() - label.text = text - label.autowrap_mode = TextServer.AUTOWRAP_WORD - add_custom_control(label) - return - - var props_root := reed_scene.get_node_or_null(reed_scene.props_root_path) - if props_root == null: - text += "ERROR: ReedScene Props root not found: %s\n" % str(reed_scene.props_root_path) - - var label2 := Label.new() - label2.text = text - label2.autowrap_mode = TextServer.AUTOWRAP_WORD - add_custom_control(label2) - return - - # ✅ 只扫描 Props root 下的 Prop - var prop_index := _scan_props_root_for_props(props_root) - - text += "Scene: %s\n" % reed_scene.name - text += "Props Root: %s\n" % str(props_root.get_path()) - text += "Prop Count: %d\n" % prop_index.size() - text += "------------------\n" - - for prop_id in prop_index.keys(): - var info: Dictionary = prop_index[prop_id] - text += "PropID: %d | %s\n" % [int(prop_id), str(info["path"])] - - var label := Label.new() - label.text = text - label.autowrap_mode = TextServer.AUTOWRAP_WORD - add_custom_control(label) - -##扫描Prop root -func _scan_props_root_for_props(props_root: Node) -> Dictionary: - var result := {} - - for prop in props_root.get_children(): - if not prop is Node: - continue - - # PropComponent 必须在 prop 的最上层子节点集中 - var prop_comp := _find_prop_component_in_top_children(prop) - if prop_comp == null: - continue - - var pid := prop_comp.prop_id - if result.has(pid): - var old_info: Dictionary = result[pid] - push_warning( - "[ActManagerInspector] Duplicate prop_id under Props root: %d\n A: %s\n B: %s" - % [pid, str(old_info["path"]), str(prop_comp.get_path())] - ) - else: - result[pid] = { - "node": prop_comp, - "path": prop_comp.get_path() - } - - return result - -##获取到Prop下的所有的PropComponent -func _find_prop_component_in_top_children(prop: Node) -> PropComponent: - for child in prop.get_children(): - if child is PropComponent: - return child as PropComponent - return null - -##获取到ActManager上方的Scene,应当自查为ReedScene -func _get_owner_scene(act_manager: ActManager) -> ReedScene: - var p := act_manager.get_parent() - if p is ReedScene: - return p as ReedScene - return null + add_custom_control(root) diff --git a/addons/reedscene/act/StateDrapSlot.tscn b/addons/reedscene/act/StateDrapSlot.tscn new file mode 100644 index 0000000..773a46b --- /dev/null +++ b/addons/reedscene/act/StateDrapSlot.tscn @@ -0,0 +1,130 @@ +[gd_scene load_steps=4 format=3 uid="uid://bueytdholvmhg"] + +[ext_resource type="Script" uid="uid://ctpxakdux3aej" path="res://addons/reedscene/act/StateDropSlot.gd" id="1_gcu1q"] +[ext_resource type="Texture2D" uid="uid://dyxm0rn8hqaa4" path="res://addons/reedscene/act/icon/inform.svg" id="2_lwr2w"] +[ext_resource type="Script" uid="uid://4bujoy6crlat" path="res://addons/reedscene/act/StateDropArea.gd" id="2_stte8"] + +[node name="Root" type="PanelContainer"] +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 15.0 +grow_horizontal = 2 +script = ExtResource("1_gcu1q") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="HBC_Main" type="HBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="Index" type="Label" parent="MarginContainer/HBC_Main"] +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 +text = "1" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBC_Main"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="HBC_Drop" type="HBoxContainer" parent="MarginContainer/HBC_Main/VBoxContainer"] +custom_minimum_size = Vector2(0, 15) +layout_mode = 2 + +[node name="DropArea" type="PanelContainer" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop"] +unique_name_in_owner = true +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +mouse_filter = 1 +script = ExtResource("2_stte8") + +[node name="TextureRect" type="TextureRect" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/DropArea"] +modulate = Color(0.7, 0.7, 0.7, 1) +layout_mode = 2 +mouse_filter = 2 +texture = ExtResource("2_lwr2w") +expand_mode = 1 +stretch_mode = 5 + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_right = 4 + +[node name="StateLabel" type="Label" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/MarginContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(80, 0) +layout_mode = 2 +text = "Drag state node to icon." + +[node name="HBC_Option" type="HBoxContainer" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop"] +layout_mode = 2 +size_flags_horizontal = 10 + +[node name="PanelContainer" type="PanelContainer" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option"] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option/PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option/PanelContainer/MarginContainer"] +layout_mode = 2 + +[node name="CB_UseTrans" type="CheckBox" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option/PanelContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="PanelContainer" type="PanelContainer" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option/PanelContainer/MarginContainer/HBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option/PanelContainer/MarginContainer/HBoxContainer/PanelContainer"] +layout_mode = 2 +text = "Use Trans" + +[node name="ChooseButton" type="Button" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option"] +layout_mode = 2 +text = "Choose" + +[node name="ClearButton" type="Button" parent="MarginContainer/HBC_Main/VBoxContainer/HBC_Drop/HBC_Option"] +layout_mode = 2 +text = "Clear" + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBC_Main/VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 2 + +[node name="Context" type="RichTextLabel" parent="MarginContainer/HBC_Main/VBoxContainer/MarginContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +bbcode_enabled = true +text = "Information will be show here." +scroll_active = false + +[node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/HBC_Main/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/HBC_Main/VBoxContainer/VBoxContainer2"] +layout_mode = 2 + +[node name="AddButton" type="Button" parent="MarginContainer/HBC_Main/VBoxContainer/VBoxContainer2/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "+" + +[node name="DeleteButton" type="Button" parent="MarginContainer/HBC_Main/VBoxContainer/VBoxContainer2/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "-" diff --git a/addons/reedscene/act/StateDropArea.gd b/addons/reedscene/act/StateDropArea.gd new file mode 100644 index 0000000..d2e8786 --- /dev/null +++ b/addons/reedscene/act/StateDropArea.gd @@ -0,0 +1,90 @@ +@tool +extends PanelContainer +class_name StateDropArea + +signal node_dropped(node: Node) + +@export var icon_path: NodePath = NodePath("TextureRect") + +const COLOR_IDLE: Color = Color(1, 1, 1, 1.0) +const COLOR_CAN_DROP: Color = Color(0.4, 0.7, 1.0, 1.0) +const COLOR_CANNOT_DROP: Color = Color(1.0, 0.35, 0.35, 1.0) +const COLOR_CHECKED: Color = Color.GREEN + +var _icon: TextureRect +var _state := 0 # 0 idle, 1 can, 2 cannot +var _current_drag_data: Variant = null + +signal node_path_dropped(node_path: NodePath) + +# ===================================================== +# Life cycle +# ===================================================== +func _ready() -> void: + _icon = get_node_or_null(icon_path) as TextureRect + + mouse_filter = Control.MOUSE_FILTER_PASS + _ignore_mouse_recursive(self) + + if _icon: + _icon.modulate = COLOR_IDLE + + if custom_minimum_size == Vector2.ZERO: + custom_minimum_size = Vector2(32, 32) + + #mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) + +# ===================================================== +# Drag & Drop +# ===================================================== +func _can_drop_data(_pos: Vector2, data) -> bool: + _current_drag_data = data + var r = StateResolveUtils.is_valid_state_drag(data) + _set_state(1 if r else 2) + return r + + +func _drop_data(_pos: Vector2, data) -> void: + var path := StateResolveUtils.get_state_path_from_drag(data) + if path.is_empty(): + return + node_path_dropped.emit(path) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_DRAG_END: + _current_drag_data = null + _set_state(0) + + +# ===================================================== +# Mouse events +# ===================================================== +func _on_mouse_exited() -> void: + _set_state(0) + +func _ignore_mouse_recursive(n: Node) -> void: + for c in n.get_children(): + if c is Control: + (c as Control).mouse_filter = Control.MOUSE_FILTER_IGNORE + _ignore_mouse_recursive(c) + + +func _set_state(s: int) -> void: + if s == _state: + return + _state = s + + if _icon == null: + return + + match _state: + 0: + _icon.modulate = COLOR_IDLE + 1: + _icon.modulate = COLOR_CAN_DROP + 2: + _icon.modulate = COLOR_CANNOT_DROP + 3: + _icon.modulate = COLOR_CHECKED diff --git a/addons/reedscene/act/StateDropArea.gd.uid b/addons/reedscene/act/StateDropArea.gd.uid new file mode 100644 index 0000000..b5872ab --- /dev/null +++ b/addons/reedscene/act/StateDropArea.gd.uid @@ -0,0 +1 @@ +uid://4bujoy6crlat diff --git a/addons/reedscene/act/StateDropSlot.gd b/addons/reedscene/act/StateDropSlot.gd new file mode 100644 index 0000000..768015c --- /dev/null +++ b/addons/reedscene/act/StateDropSlot.gd @@ -0,0 +1,179 @@ +@tool +class_name StateDrapSlot +extends Control + +@onready var drop_area: StateDropArea = %DropArea +@onready var state_label: Label = %StateLabel + +@onready var context: RichTextLabel = %Context + +@onready var cb_use_trans: CheckBox = %CB_UseTrans + +var _cached_single_act: SingleAct = null +var _cached_prop_id: int = -1 + +const NORMAL_STATE_LABLE: String = "Drag state node to icon." +const NORMAL_CONTEXT: String = "Information will be show here." + +const COLOR_IDLE: Color = Color.GRAY +const COLOR_CHECKED_1: Color = Color.GREEN +const COLOR_CHECKED_2: Color = Color.YELLOW +const COLOR_CHECKED_3: Color = Color.ORANGE_RED + +func _ready() -> void: + _reset_to_default() + _reset_cb_use_trans() + + if drop_area: + drop_area.node_path_dropped.connect(_on_node_path_dropped) + + +func _on_node_path_dropped(path: NodePath) -> void: + var state := StateResolveUtils.get_state_node_from_path(path) + if state == null: + _clear_cached_act() + _reset_to_default() + return + + var info := StateResolveUtils.resolve_state_display_info(state) + if info == null: + _clear_cached_act() + _reset_to_default() + return + + # 创建 / 更新 cached + _cached_single_act = SingleAct.new() + _cached_single_act.state_id = state.state_id + _cached_single_act.use_trans = false + _cached_single_act.context = {} + + _cached_prop_id = int(info.prop_id) + + # 启用 CheckBox + cb_use_trans.disabled = false + cb_use_trans.button_pressed = _cached_single_act.use_trans + + # 直接用 info + cached 刷 UI + _set_context(info) + +func _on_use_trans_toggled(pressed: bool) -> void: + if _cached_single_act == null: + cb_use_trans.button_pressed = false + return + + # ✅ 只改 cached + _cached_single_act.use_trans = pressed + + # 重新 resolve 一次 info(简单、直观) + var state := _resolve_state_from_cached() + if state == null: + return + + var info := StateResolveUtils.resolve_state_display_info(state) + _set_context(info) + + +func _resolve_state_from_path(path: NodePath) -> ReedPropState: + var root := EditorInterface.get_edited_scene_root() + if root == null: + return null + + var node := root.get_node_or_null(path) + if node is ReedPropState: + return node + + return null + +## 重置回默认 +func _reset_to_default() ->void: + state_label.text = NORMAL_STATE_LABLE + state_label.modulate = COLOR_IDLE + + context.text = NORMAL_CONTEXT + context.modulate = COLOR_IDLE + + _reset_cb_use_trans() + +## 初始化use trans 的 check box +func _reset_cb_use_trans() ->void: + cb_use_trans.disabled = true + cb_use_trans.button_pressed = false + + if not cb_use_trans.toggled.is_connected(_on_use_trans_toggled): + cb_use_trans.toggled.connect(_on_use_trans_toggled) + +## 初始化启用use trans +func _enable_cb_use_trans() -> void: + cb_use_trans.disabled = false + cb_use_trans.button_pressed = _cached_single_act.use_trans + + +## 创建一个CachedAct +func _build_cached_act(state: ReedPropState, info: Dictionary) -> void: + if _cached_single_act == null: + _cached_single_act = SingleAct.new() + + _cached_single_act.state_id = state.state_id + _cached_single_act.use_trans = false # 默认值,可后续切换 + _cached_single_act.context = {} # 预留 + + # Prop ID 缓存 + _cached_prop_id = int(info.prop_id) + +## 清空Cached Act +func _clear_cached_act() -> void: + _cached_single_act = null + _cached_prop_id = -1 + +## 设置文本 +func _set_context(info) ->void: + if _cached_single_act == null: + return + + context.clear() + + # —— 配置颜色变量 —— # + var prop_color_str := COLOR_CHECKED_1.to_html(false) + var state_color_str := COLOR_CHECKED_1.to_html(false) + var id_color_str := COLOR_CHECKED_2.to_html(false) + + var use_trans := _cached_single_act.use_trans + var use_mark := "\u2714" if use_trans else "\u2716" + var use_mark_color_str := ( + COLOR_CHECKED_1 if use_trans else COLOR_CHECKED_3 + ).to_html(false) + + var bbcode := "" + bbcode += "[table=1][cell]" + + # Prop + bbcode += "[b][color=%s]%s[/color][/b] [color=%s](ID_%s)[/color]" % [ + prop_color_str, + info.prop_name, + id_color_str, + info.prop_id + ] + + # 空白占位 + bbcode += "[color=#00000000] [/color]" + + # State + bbcode += "[b][color=%s]%s[/color][/b] [color=%s](ID_%s)[/color]" % [ + state_color_str, + info.state_name, + id_color_str, + info.state_id + ] + + # 再空白 + bbcode += "[color=#00000000] [/color]" + + # Use Trans 显示 + bbcode += "[color=%s]%s[/color] Use Trans" % [ + use_mark_color_str, + use_mark + ] + + bbcode += "[/cell][/table]" + + context.text = bbcode diff --git a/addons/reedscene/act/StateDropSlot.gd.uid b/addons/reedscene/act/StateDropSlot.gd.uid new file mode 100644 index 0000000..0c620a9 --- /dev/null +++ b/addons/reedscene/act/StateDropSlot.gd.uid @@ -0,0 +1 @@ +uid://ctpxakdux3aej diff --git a/addons/reedscene/act/StateResolveUtils.gd b/addons/reedscene/act/StateResolveUtils.gd new file mode 100644 index 0000000..7c3b61b --- /dev/null +++ b/addons/reedscene/act/StateResolveUtils.gd @@ -0,0 +1,193 @@ +'''工具类:用于解析 State / 路径 / 展示信息''' +@tool +class_name StateResolveUtils + + +# ===================================================== +# Section 1: Drag Data → State Path / State Node +# ===================================================== +static func is_valid_state_drag(data) -> bool: + return get_state_node_from_drag(data) != null + + +static func get_state_path_from_drag(data) -> NodePath: + var raw := _extract_node_path_from_drag(data) + if raw.is_empty(): + return NodePath() + + return _normalize_to_scene_root(raw) + + +static func get_state_node_from_drag(data) -> ReedPropState: + var path := get_state_path_from_drag(data) + if path.is_empty(): + return null + + return get_state_node_from_path(path) + + +# ===================================================== +# Section 2: NodePath → State Node +# ===================================================== +static func get_state_node_from_path(path: NodePath) -> ReedPropState: + if path.is_empty(): + return null + + var root := EditorInterface.get_edited_scene_root() + if root == null: + return null + + var node := root.get_node_or_null(path) + if node is ReedPropState: + return node + + return null + + +# ===================================================== +# Section 3: State Node → 展示信息 +# ===================================================== + +static func resolve_state_display_info(state: ReedPropState) -> Dictionary: + var result := { + "prop_name": "", + "prop_id": "", + "state_name": "", + "state_id": "" + } + + if state == null: + return result + + # -------------------- + # State 信息 + # -------------------- + result.state_name = state.name + result.state_id = str(state.state_id) + + # -------------------- + # PropComponent + # -------------------- + var prop_comp := _find_parent_prop_comp(state) + if prop_comp == null: + return result + + result.prop_id = str(prop_comp.prop_id) + + # -------------------- + # Prop 实体节点(PropComponent 的父节点) + # -------------------- + var prop_node := prop_comp.get_parent() + if prop_node: + result.prop_name = prop_node.name + + return result + + +# ===================================================== +# Section 4: Internal helpers +# ===================================================== + +static func _extract_node_path_from_drag(data) -> NodePath: + if typeof(data) != TYPE_DICTIONARY: + return NodePath() + + # SceneTree 标准:nodes = [NodePath / String] + if data.has("nodes") and data["nodes"] is Array and data["nodes"].size() > 0: + var first = data["nodes"][0] + if first is NodePath: + return first + if typeof(first) == TYPE_STRING: + return NodePath(first) + + # 少见兜底:直接给了 node + if data.has("node") and data["node"] is Node: + return (data["node"] as Node).get_path() + + return NodePath() + + +static func _normalize_to_scene_root(p: NodePath) -> NodePath: + if p.is_empty(): + return p + + var root := EditorInterface.get_edited_scene_root() + if root == null: + return p + + var root_path := String(root.get_path()) + var s := String(p) + + # 已经是相对路径 + if not s.begins_with("/"): + return p + + if s.begins_with(root_path): + s = s.substr(root_path.length()) + if s.begins_with("/"): + s = s.substr(1) + + return NodePath(s) + + +static func _find_parent_prop_comp(state: Node) -> PropComponent: + var cur := state.get_parent() + while cur: + if cur is PropComponent: + return cur + cur = cur.get_parent() + return null + +## 通过Path返回ID和State +static func resolve_ids_from_state_path(path: NodePath) -> Dictionary: + var result := { + "prop_id": -1, + "state_id": -1 + } + + var root := EditorInterface.get_edited_scene_root() + if root == null: + return result + + var node := root.get_node_or_null(path) + if not (node is ReedPropState): + return result + + result.state_id = node.state_id + + var prop_comp := _find_parent_prop_comp(node) + if prop_comp == null: + return result + + result.prop_id = prop_comp.prop_id + return result + +## 通过id 获取Name +static func resolve_names_from_ids( + prop_id: int, + state_id: int +) -> Dictionary: + var result := { + "prop_name": "", + "state_name": "" + } + + var root := EditorInterface.get_edited_scene_root() + if root == null: + return result + + # 遍历 PropComponent(你之后可以优化成表) + for prop_comp in root.get_tree().get_nodes_in_group("PropComponent"): + if prop_comp.prop_id != prop_id: + continue + + var prop_node := prop_comp.get_parent() + if prop_node: + result.prop_name = prop_node.name + + for child in prop_comp.get_children(): + if child is ReedPropState and child.state_id == state_id: + result.state_name = child.name + return result + + return result diff --git a/addons/reedscene/act/StateResolveUtils.gd.uid b/addons/reedscene/act/StateResolveUtils.gd.uid new file mode 100644 index 0000000..a569053 --- /dev/null +++ b/addons/reedscene/act/StateResolveUtils.gd.uid @@ -0,0 +1 @@ +uid://d1sqnplikvohe diff --git a/addons/reedscene/act/icon/inform.svg b/addons/reedscene/act/icon/inform.svg new file mode 100644 index 0000000..0632b45 --- /dev/null +++ b/addons/reedscene/act/icon/inform.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/reedscene/act/icon/inform.svg.import b/addons/reedscene/act/icon/inform.svg.import new file mode 100644 index 0000000..47a2e3d --- /dev/null +++ b/addons/reedscene/act/icon/inform.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dyxm0rn8hqaa4" +path="res://.godot/imported/inform.svg-c5339b427b2c0311215067373a3a7293.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/reedscene/act/icon/inform.svg" +dest_files=["res://.godot/imported/inform.svg-c5339b427b2c0311215067373a3a7293.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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/reedscene/prop/PropComponent.gd b/addons/reedscene/prop/PropComponent.gd index 9d3d924..0aa6684 100644 --- a/addons/reedscene/prop/PropComponent.gd +++ b/addons/reedscene/prop/PropComponent.gd @@ -29,7 +29,8 @@ var _states_root: Node var _state_map: Dictionary = {} var _current: ReedPropState = null -const states_root_name := "States" +const STATES_ROOT_NAME := "States" +const STATES_ROOT_SCRIPT := preload("res://addons/reedscene/prop/StateManager.gd") const DEFAULT_STATE_ID := 0 @@ -38,19 +39,14 @@ func _enter_tree() -> void: _editor_ensure_states_root() func _ready() -> void: + if Engine.is_editor_hint(): + _editor_ready() + _build_state_cache() - #if not Engine.is_editor_hint(): - #if should_wait_owner_ready: - #await owner.ready - - #_init_states() - - #if _current == null and initial_state_id >= 0 and overwrite_init_state: - #change_state(initial_state_id, false, { - #"reason": "INIT", - #"instant": true - #}) +## 只在編輯器模式的Ready裏Call的函數 +func _editor_ready() -> void: + child_exiting_tree.connect(_on_child_exiting_tree) ## 用于初始化状态 func init() -> void: @@ -64,17 +60,18 @@ func _init_state_check() ->void: "instant": true }) -##映射State到stateID +## 在構造時,映射State到stateID func _build_state_cache() -> void: _state_map.clear() - _states_root = get_node_or_null(states_root_name) + _states_root = get_node_or_null(STATES_ROOT_NAME) if _states_root == null: push_warning("[PropComponent:%s] States root not found" % prop_id) return - - if not _states_root is ReedPropStateManager: - push_error("[PropComponent:%s] States root is not ReedPropStateManager" % prop_id) + + ## 判斷子節點是否是StatesRoot節點,此節點的脚本後續會輕量化,所以不設置為一個單獨的類 + if not _states_root.IS_PROP_STATES_ROOT: + push_error("[PropComponent:%s] States root missing PropStatesRoot script" % prop_id) return for c in _states_root.get_children(): @@ -89,14 +86,6 @@ func _build_state_cache() -> void: if debug_log: print("[PropComponent:%s] States:", _state_map.keys()) -##检查当前是否存在当前Id的State -func has_state(state_id: int) -> bool: - return _state_map.has(state_id) - -##获取当前的状态ID -func get_current_state_id() -> int: - return _current.state_id if _current else -1 - ##初始化状态 func _init_states() -> void: var owner := get_parent() @@ -110,6 +99,99 @@ func _init_states() -> void: push_error("[PropComponent:%s] State init failed: %s" % [prop_id, s.name]) + + +## ============================== +## Act Sync Entry +## ============================== +#func sync_to_state(state_id: int, act_id: int, instant := false) -> bool: + #var ctx := { + #"reason": "ACT_SYNC", + #"act_id": act_id, + #"instant": instant + #} + #return change_state(state_id, false ,ctx) + +## ============================== +## Editor Tool +## ============================== +#func _editor_ensure_states_root() -> void: + #var states := get_node_or_null(STATES_ROOT_NAME) + #if states != null: + #return +# + ## 创建 StateManager 而不是普通 Node + #states = ReedPropStateManager.new() + #states.name = STATES_ROOT_NAME + #add_child(states) +# + ## 让其成为 Scene 的一部分(非常重要) + #states.owner = get_tree().edited_scene_root +# + ## 自动添加一个默认 State + #var default_state := ReedPropState.new() + #default_state.name = "Default" + #states.add_child(default_state) + #default_state.owner = get_tree().edited_scene_root +# + #if debug_log: + #print("[PropComponent] Created States(StateManager) + Default State") + +## 引擎層預知一個工具節點給予到State及其子節點。 +func _editor_ensure_states_root() -> void: + var states := get_node_or_null(STATES_ROOT_NAME) + if states: + return + + states = Node.new() + states.name = STATES_ROOT_NAME + states.set_script(STATES_ROOT_SCRIPT) + add_child(states) + states.owner = get_tree().edited_scene_root + + var default_state := ReedPropState.new() + default_state.name = "Default" + states.add_child(default_state) + default_state.owner = get_tree().edited_scene_root + +## 不允许StatesNode被删除,如果删了会自动补一个 +func _on_child_exiting_tree(child: Node) -> void: + if child.name == STATES_ROOT_NAME: + push_error("[PropComponent] 'States' node is required and cannot be removed.") + call_deferred("_editor_ensure_states_root") + +## ============================== +## External API +## ============================== +#region 外部接口函数 +## 使用PropComp獲取PropName,直接返回ParentName +func get_prop_name() -> String: + return get_parent().name + +## 獲取StatesRoot,返回緩存的StatesRoot +func get_states_root() -> Node: + return _states_root + +## 通過ID獲取PropState +func get_state_by_id(id: int) -> ReedPropState: + return _state_map.get(id) + +## 返回State的Size +func get_state_size() -> int: + return _state_map.keys().size() + +## 返回所有的States +func get_all_states() -> Array[ReedPropState]: + return _state_map.values() + +##检查是否存在Id的State +func has_state(state_id: int) -> bool: + return _state_map.has(state_id) + +##获取当前的状态ID +func get_current_state_id() -> int: + return _current.state_id if _current else -1 + ## 切换状态,如果 use_trans 为 true,则会优先检查 next state 下是否存在可用的 Transition func change_state(state_id: int, use_trans: bool, ctx: Dictionary = {}) -> bool: if not _state_map.has(state_id): @@ -165,38 +247,28 @@ func change_state(state_id: int, use_trans: bool, ctx: Dictionary = {}) -> bool: return true -## ============================== -## Act Sync Entry -## ============================== -func sync_to_state(state_id: int, act_id: int, instant := false) -> bool: - var ctx := { - "reason": "ACT_SYNC", - "act_id": act_id, - "instant": instant - } - return change_state(state_id, false ,ctx) +## 获取ID的State的Name +func get_state_name_by_id(id: int) -> String: + var s : ReedPropState = get_state_by_id(id) + if not s: + return "" + return get_state_name(s) -## ============================== -## Editor Tool -## ============================== -func _editor_ensure_states_root() -> void: - var states := get_node_or_null(states_root_name) - if states != null: - return +## 获取State的Name +func get_state_name(state: ReedPropState) -> String: + if not state: + return "" + + var n: String = state.name + var extract_raw_name := func(name: String) -> String: + return ( + name.substr(name.find("]") + 2) + if name.begins_with("[ID:") + and name.find("]") != -1 + and name.find("]") + 2 < name.length() + else name + ) - # 创建 StateManager 而不是普通 Node - states = ReedPropStateManager.new() - states.name = states_root_name - add_child(states) + return extract_raw_name.call(n) - # 让其成为 Scene 的一部分(非常重要) - states.owner = get_tree().edited_scene_root - - # 自动添加一个默认 State - var default_state := ReedPropState.new() - default_state.name = "Default" - states.add_child(default_state) - default_state.owner = get_tree().edited_scene_root - - if debug_log: - print("[PropComponent] Created States(StateManager) + Default State") +#endregion diff --git a/addons/reedscene/prop/PropState.gd b/addons/reedscene/prop/PropState.gd index c073be1..4222fee 100644 --- a/addons/reedscene/prop/PropState.gd +++ b/addons/reedscene/prop/PropState.gd @@ -10,11 +10,12 @@ class_name ReedPropState extends Node ##预装的一些Effect,可以用来对State进行一些初始化 @export var effects: Array[ReedPropEffect] = [] +const DEFAULT_NAME = "Default" + ##State的拥有者 var _owner ##State的prop comp var _owner_prop_comp - ## 是否已经初始化 var _inited: bool = false diff --git a/addons/reedscene/prop/StateManager.gd b/addons/reedscene/prop/StateManager.gd index 941b9ca..a4934f4 100644 --- a/addons/reedscene/prop/StateManager.gd +++ b/addons/reedscene/prop/StateManager.gd @@ -1,8 +1,14 @@ +''' + 輔助功能性脚本,可以用來管理所有的PropState,並添加一些幫助性的方法。 +''' @tool @icon("res://addons/reedscene/prop/icon/prop_states_manager.svg") -class_name ReedPropStateManager +#class_name ReedPropStateManager extends Node +## FLAG +const IS_PROP_STATES_ROOT := true + @export var auto_refresh: bool = true func _ready() -> void: @@ -11,31 +17,45 @@ func _ready() -> void: child_entered_tree.connect(_on_child_entered_tree) child_exiting_tree.connect(_on_child_exiting_tree) + child_order_changed.connect(_on_child_order_changing) _refresh_states() -func _notification(what: int) -> void: - if not Engine.is_editor_hint(): - return - - if what == NOTIFICATION_CHILD_ORDER_CHANGED: - if auto_refresh: - call_deferred("_refresh_states") - +## 如果子節點進入管理器 func _on_child_entered_tree(node: Node) -> void: if not Engine.is_editor_hint(): return + if not node.renamed.is_connected(_on_child_renamed): + node.renamed.connect(_on_child_renamed) + if node is ReedPropState and auto_refresh: call_deferred("_refresh_states") +## 如果子節點離開管理器 func _on_child_exiting_tree(node: Node) -> void: if not Engine.is_editor_hint(): return + + if node.renamed.is_connected(_on_child_renamed): + node.renamed.disconnect(_on_child_renamed) if node is ReedPropState and auto_refresh: call_deferred("_refresh_states") +## 如果子節點被重命名 +func _on_child_renamed() -> void: + if not Engine.is_editor_hint(): + return + + call_deferred("_refresh_states") + +## 如果子節點的順序修改 +func _on_child_order_changing() -> void: + if not Engine.is_editor_hint(): + return + call_deferred("_refresh_states") + func _refresh_states() -> void: var index := 0 @@ -62,6 +82,7 @@ func _rename_state_node(state: ReedPropState, id: int) -> void: state.name = expected +##连续剥离Name,防止错乱 func _strip_id_prefix(name: String) -> String: var result := name @@ -80,6 +101,7 @@ func _strip_id_prefix(name: String) -> String: return result.strip_edges() +## 提取原本的命名 func _extract_raw_name(name: String) -> String: if name.begins_with("[ID:"): var end := name.find("]") diff --git a/addons/reedscene/reedscene.gd b/addons/reedscene/reedscene.gd index 1169117..a3a55e9 100644 --- a/addons/reedscene/reedscene.gd +++ b/addons/reedscene/reedscene.gd @@ -3,68 +3,117 @@ extends EditorPlugin var inspector_plugins: Array[EditorInspectorPlugin] = [] -const AUTOLOAD_REED_SCENE_NAME: StringName = "ReedSceneRegistry" -const AUTOLOAD_REED_SCENE_PATH:= "res://addons/reedscene/scene/SceneRegistry.gd" +const AUTOLOAD_REED_SCENE_NAME: StringName = &"ReedSceneRegistry" +const AUTOLOAD_REED_SCENE_PATH := "res://addons/reedscene/scene/SceneRegistry.gd" -var toolbar_button: Button var main_screen: Control + +# ====================================================== +# 生命周期 +# ====================================================== + func _enter_tree() -> void: _load_mainscreen() - - if not ProjectSettings.has_setting("autoload/%s" % AUTOLOAD_REED_SCENE_NAME): - add_autoload_singleton(AUTOLOAD_REED_SCENE_NAME, AUTOLOAD_REED_SCENE_PATH) - - # Inspector 1:你已有的 - var act_inspector := preload( - "res://addons/reedscene/act/ActManagerInspector.gd" - ).new() - act_inspector.editor_interface = get_editor_interface() - add_inspector_plugin(act_inspector) - inspector_plugins.append(act_inspector) + _ensure_autoload() + _register_inspectors() - # Inspector 2:新的(比如 ReedSceneID) - var scene_id_inspector := preload( - "res://addons/reedscene/scene/ReedSceneIDInspector.gd" - ).new() - add_inspector_plugin(scene_id_inspector) - inspector_plugins.append(scene_id_inspector) func _exit_tree() -> void: _unload_mainscreen() - - ##自动删除单例 - if ProjectSettings.has_setting("autoload/%s" % AUTOLOAD_REED_SCENE_NAME): - remove_autoload_singleton(AUTOLOAD_REED_SCENE_NAME) - - # 移除 InspectorPlugin + _remove_autoload() + _unregister_inspectors() + + +# ====================================================== +# Inspector Plugins +# ====================================================== +func _register_inspectors() -> void: + # Inspector 1:ActManager + var act_inspector := preload( + "res://addons/reedscene/act/ActManagerInspector.gd" + ).new() + + add_inspector_plugin(act_inspector) + inspector_plugins.append(act_inspector) + + # Inspector 2:ReedSceneID + var scene_id_inspector := preload( + "res://addons/reedscene/scene/ReedSceneIDInspector.gd" + ).new() + + add_inspector_plugin(scene_id_inspector) + inspector_plugins.append(scene_id_inspector) + + +func _unregister_inspectors() -> void: for plugin in inspector_plugins: remove_inspector_plugin(plugin) inspector_plugins.clear() -##加載主界面 + +# ====================================================== +# Autoload +# ====================================================== + +func _ensure_autoload() -> void: + var key := "autoload/%s" % AUTOLOAD_REED_SCENE_NAME + if not ProjectSettings.has_setting(key): + add_autoload_singleton( + AUTOLOAD_REED_SCENE_NAME, + AUTOLOAD_REED_SCENE_PATH + ) + + +func _remove_autoload() -> void: + var key := "autoload/%s" % AUTOLOAD_REED_SCENE_NAME + if ProjectSettings.has_setting(key): + remove_autoload_singleton(AUTOLOAD_REED_SCENE_NAME) + + +# ====================================================== +# Main Screen +# ====================================================== + func _load_mainscreen() -> void: - main_screen = preload("res://addons/reedscene/dock/SceneIDMainPanel.tscn").instantiate() + main_screen = preload( + "res://addons/reedscene/dock/SceneIDMainPanel.tscn" + ).instantiate() + EditorInterface.get_editor_main_screen().add_child(main_screen) main_screen.visible = false -##卸載主界面 + func _unload_mainscreen() -> void: if main_screen: main_screen.queue_free() main_screen = null + func _has_main_screen() -> bool: return true + func _make_visible(visible: bool) -> void: - if not main_screen: return + if not main_screen: + return + main_screen.visible = visible - if visible: - main_screen.on_activated() + + if visible and main_screen.has_method("on_activated"): + main_screen.on_activated() + + +# ====================================================== +# Meta +# ====================================================== func _get_plugin_name() -> String: return "Reed Scene" + func _get_plugin_icon() -> Texture2D: - return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons") + return EditorInterface.get_editor_theme().get_icon( + "Node", + "EditorIcons" + ) diff --git a/addons/reedscene/update.md b/addons/reedscene/update.md index a3a3199..5b7a55d 100644 --- a/addons/reedscene/update.md +++ b/addons/reedscene/update.md @@ -19,6 +19,6 @@ V1.0 - Scene管理器全局ID功能 V1.1 - +- 添加和规范API - 删除不必要的ClassName - 完善ActManager的编辑者友好界面