升級了插件的API

This commit is contained in:
RedisTKey 2026-01-03 23:21:04 +08:00
parent 5a6ad57705
commit d56af0f0c6
18 changed files with 940 additions and 215 deletions

View File

@ -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="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"] [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://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://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="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"] [node name="Game" type="Node2D"]
script = ExtResource("1_2tycc") 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")] [node name="Avatar" parent="." instance=ExtResource("3_6jw57")]
position = Vector2(283, 253) position = Vector2(283, 253)
collision_mask = 4 collision_mask = 4
@ -24,3 +29,45 @@ debug_log = true
[node name="l1_s3" parent="." instance=ExtResource("6_xkd7q")] [node name="l1_s3" parent="." instance=ExtResource("6_xkd7q")]
debug_log = true 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

View File

@ -67,9 +67,6 @@ prop_state_map = Dictionary[int, ExtResource("6_fyfyw")]({
[sub_resource type="Resource" id="Resource_fyfyw"] [sub_resource type="Resource" id="Resource_fyfyw"]
script = ExtResource("12_fyfyw") script = ExtResource("12_fyfyw")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"reset_door_state_id" func_name = &"reset_door_state_id"
@ -78,9 +75,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_bco80"] [sub_resource type="Resource" id="Resource_bco80"]
script = ExtResource("12_fyfyw") script = ExtResource("12_fyfyw")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"door_close" func_name = &"door_close"
@ -88,9 +82,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_22pon"] [sub_resource type="Resource" id="Resource_22pon"]
script = ExtResource("12_fyfyw") script = ExtResource("12_fyfyw")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"reset_door_state_id" func_name = &"reset_door_state_id"
@ -99,9 +90,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_25twt"] [sub_resource type="Resource" id="Resource_25twt"]
script = ExtResource("12_fyfyw") script = ExtResource("12_fyfyw")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"door_open" func_name = &"door_open"

View File

@ -73,9 +73,6 @@ prop_state_map = Dictionary[int, ExtResource("6_d8y7x")]({
[sub_resource type="Resource" id="Resource_fyfyw"] [sub_resource type="Resource" id="Resource_fyfyw"]
script = ExtResource("12_jhhb1") script = ExtResource("12_jhhb1")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"reset_door_state_id" func_name = &"reset_door_state_id"
@ -84,9 +81,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_bco80"] [sub_resource type="Resource" id="Resource_bco80"]
script = ExtResource("12_jhhb1") script = ExtResource("12_jhhb1")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"door_close" func_name = &"door_close"
@ -94,9 +88,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_22pon"] [sub_resource type="Resource" id="Resource_22pon"]
script = ExtResource("12_jhhb1") script = ExtResource("12_jhhb1")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"reset_door_state_id" func_name = &"reset_door_state_id"
@ -105,9 +96,6 @@ metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_25twt"] [sub_resource type="Resource" id="Resource_25twt"]
script = ExtResource("12_jhhb1") script = ExtResource("12_jhhb1")
child_node_name = null
recursive_found = null
owned_node_only = null
effect_type = 1 effect_type = 1
value = null value = null
func_name = &"door_open" func_name = &"door_open"

View File

@ -1,96 +1,15 @@
@tool @tool
extends EditorInspectorPlugin extends EditorInspectorPlugin
# ✅ 由 EditorPlugin 注入 const SLOT_SCENE:= preload("res://addons/reedscene/act/StateDrapSlot.tscn")
var editor_interface: EditorInterface
func _can_handle(object) -> bool: func _can_handle(object) -> bool:
return object is ActManager return object is ActManager
func _parse_begin(object) -> void: func _parse_begin(object) -> void:
var act_manager := object as ActManager var root := VBoxContainer.new()
var reed_scene := _get_owner_scene(act_manager)
var text := "" var slot := SLOT_SCENE.instantiate()
text += "Act Table (Debug)\n" root.add_child(slot)
text += "Act Count: %d\n" % act_manager.prop_state_map.size()
text += "------------------\n"
if reed_scene == null: add_custom_control(root)
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 "<null>")
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

View File

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

View File

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

View File

@ -0,0 +1 @@
uid://4bujoy6crlat

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767424299812" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5075" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M513.024 65.536q93.184 0 175.616 35.84t143.872 97.28 97.28 143.872 35.84 175.616q0 94.208-35.84 176.64t-97.28 143.872-143.872 97.28-175.616 35.84q-94.208 0-176.64-35.84t-143.872-97.28-97.28-143.872-35.84-176.64q0-93.184 35.84-175.616t97.28-143.872 143.872-97.28 176.64-35.84zM513.024 909.312q80.896 0 152.064-30.72t124.416-83.968 83.968-124.416 30.72-152.064-30.72-152.064-83.968-124.416-124.416-83.968-152.064-30.72q-81.92 0-153.088 30.72t-124.416 83.968-83.968 124.416-30.72 152.064 30.72 152.064 83.968 124.416 124.416 83.968 153.088 30.72zM513.024 190.464q66.56 0 124.928 25.088t102.4 69.12 69.12 102.4 25.088 124.928-25.088 125.44-69.12 102.912-102.4 69.12-124.928 25.088-125.44-25.088-102.912-69.12-69.12-102.912-25.088-125.44 25.088-124.928 69.12-102.4 102.912-69.12 125.44-25.088z" p-id="5076" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

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

View File

@ -29,7 +29,8 @@ var _states_root: Node
var _state_map: Dictionary = {} var _state_map: Dictionary = {}
var _current: ReedPropState = null 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 const DEFAULT_STATE_ID := 0
@ -38,19 +39,14 @@ func _enter_tree() -> void:
_editor_ensure_states_root() _editor_ensure_states_root()
func _ready() -> void: func _ready() -> void:
if Engine.is_editor_hint():
_editor_ready()
_build_state_cache() _build_state_cache()
#if not Engine.is_editor_hint(): ## 只在編輯器模式的Ready裏Call的函數
#if should_wait_owner_ready: func _editor_ready() -> void:
#await owner.ready child_exiting_tree.connect(_on_child_exiting_tree)
#_init_states()
#if _current == null and initial_state_id >= 0 and overwrite_init_state:
#change_state(initial_state_id, false, {
#"reason": "INIT",
#"instant": true
#})
## 用于初始化状态 ## 用于初始化状态
func init() -> void: func init() -> void:
@ -64,17 +60,18 @@ func _init_state_check() ->void:
"instant": true "instant": true
}) })
##映射State到stateID ## 在構造時,映射State到stateID
func _build_state_cache() -> void: func _build_state_cache() -> void:
_state_map.clear() _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: if _states_root == null:
push_warning("[PropComponent:%s] States root not found" % prop_id) push_warning("[PropComponent:%s] States root not found" % prop_id)
return return
if not _states_root is ReedPropStateManager: ## 判斷子節點是否是StatesRoot節點此節點的脚本後續會輕量化所以不設置為一個單獨的類
push_error("[PropComponent:%s] States root is not ReedPropStateManager" % prop_id) if not _states_root.IS_PROP_STATES_ROOT:
push_error("[PropComponent:%s] States root missing PropStatesRoot script" % prop_id)
return return
for c in _states_root.get_children(): for c in _states_root.get_children():
@ -89,14 +86,6 @@ func _build_state_cache() -> void:
if debug_log: if debug_log:
print("[PropComponent:%s] States:", _state_map.keys()) 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: func _init_states() -> void:
var owner := get_parent() var owner := get_parent()
@ -110,6 +99,99 @@ func _init_states() -> void:
push_error("[PropComponent:%s] State init failed: %s" push_error("[PropComponent:%s] State init failed: %s"
% [prop_id, s.name]) % [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 ## 切换状态,如果 use_trans 为 true则会优先检查 next state 下是否存在可用的 Transition
func change_state(state_id: int, use_trans: bool, ctx: Dictionary = {}) -> bool: func change_state(state_id: int, use_trans: bool, ctx: Dictionary = {}) -> bool:
if not _state_map.has(state_id): 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 return true
## ============================== ## 获取ID的State的Name
## Act Sync Entry func get_state_name_by_id(id: int) -> String:
## ============================== var s : ReedPropState = get_state_by_id(id)
func sync_to_state(state_id: int, act_id: int, instant := false) -> bool: if not s:
var ctx := { return ""
"reason": "ACT_SYNC", return get_state_name(s)
"act_id": act_id,
"instant": instant
}
return change_state(state_id, false ,ctx)
## ============================== ## 获取State的Name
## Editor Tool func get_state_name(state: ReedPropState) -> String:
## ============================== if not state:
func _editor_ensure_states_root() -> void: return ""
var states := get_node_or_null(states_root_name)
if states != null:
return
# 创建 StateManager 而不是普通 Node var n: String = state.name
states = ReedPropStateManager.new() var extract_raw_name := func(name: String) -> String:
states.name = states_root_name return (
add_child(states) name.substr(name.find("]") + 2)
if name.begins_with("[ID:")
and name.find("]") != -1
and name.find("]") + 2 < name.length()
else name
)
# 让其成为 Scene 的一部分(非常重要) return extract_raw_name.call(n)
states.owner = get_tree().edited_scene_root
# 自动添加一个默认 State #endregion
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")

View File

@ -10,11 +10,12 @@ class_name ReedPropState extends Node
##预装的一些Effect可以用来对State进行一些初始化 ##预装的一些Effect可以用来对State进行一些初始化
@export var effects: Array[ReedPropEffect] = [] @export var effects: Array[ReedPropEffect] = []
const DEFAULT_NAME = "Default"
##State的拥有者 ##State的拥有者
var _owner var _owner
##State的prop comp ##State的prop comp
var _owner_prop_comp var _owner_prop_comp
## 是否已经初始化 ## 是否已经初始化
var _inited: bool = false var _inited: bool = false

View File

@ -1,8 +1,14 @@
'''
PropState
'''
@tool @tool
@icon("res://addons/reedscene/prop/icon/prop_states_manager.svg") @icon("res://addons/reedscene/prop/icon/prop_states_manager.svg")
class_name ReedPropStateManager #class_name ReedPropStateManager
extends Node extends Node
## FLAG
const IS_PROP_STATES_ROOT := true
@export var auto_refresh: bool = true @export var auto_refresh: bool = true
func _ready() -> void: func _ready() -> void:
@ -11,31 +17,45 @@ func _ready() -> void:
child_entered_tree.connect(_on_child_entered_tree) child_entered_tree.connect(_on_child_entered_tree)
child_exiting_tree.connect(_on_child_exiting_tree) child_exiting_tree.connect(_on_child_exiting_tree)
child_order_changed.connect(_on_child_order_changing)
_refresh_states() _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: func _on_child_entered_tree(node: Node) -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
return return
if not node.renamed.is_connected(_on_child_renamed):
node.renamed.connect(_on_child_renamed)
if node is ReedPropState and auto_refresh: if node is ReedPropState and auto_refresh:
call_deferred("_refresh_states") call_deferred("_refresh_states")
## 如果子節點離開管理器
func _on_child_exiting_tree(node: Node) -> void: func _on_child_exiting_tree(node: Node) -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
return return
if node.renamed.is_connected(_on_child_renamed):
node.renamed.disconnect(_on_child_renamed)
if node is ReedPropState and auto_refresh: if node is ReedPropState and auto_refresh:
call_deferred("_refresh_states") 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: func _refresh_states() -> void:
var index := 0 var index := 0
@ -62,6 +82,7 @@ func _rename_state_node(state: ReedPropState, id: int) -> void:
state.name = expected state.name = expected
##连续剥离Name防止错乱
func _strip_id_prefix(name: String) -> String: func _strip_id_prefix(name: String) -> String:
var result := name var result := name
@ -80,6 +101,7 @@ func _strip_id_prefix(name: String) -> String:
return result.strip_edges() return result.strip_edges()
## 提取原本的命名
func _extract_raw_name(name: String) -> String: func _extract_raw_name(name: String) -> String:
if name.begins_with("[ID:"): if name.begins_with("[ID:"):
var end := name.find("]") var end := name.find("]")

View File

@ -3,68 +3,117 @@ extends EditorPlugin
var inspector_plugins: Array[EditorInspectorPlugin] = [] var inspector_plugins: Array[EditorInspectorPlugin] = []
const AUTOLOAD_REED_SCENE_NAME: StringName = "ReedSceneRegistry" const AUTOLOAD_REED_SCENE_NAME: StringName = &"ReedSceneRegistry"
const AUTOLOAD_REED_SCENE_PATH:= "res://addons/reedscene/scene/SceneRegistry.gd" const AUTOLOAD_REED_SCENE_PATH := "res://addons/reedscene/scene/SceneRegistry.gd"
var toolbar_button: Button
var main_screen: Control var main_screen: Control
# ======================================================
# 生命周期
# ======================================================
func _enter_tree() -> void: func _enter_tree() -> void:
_load_mainscreen() _load_mainscreen()
_ensure_autoload()
_register_inspectors()
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)
# 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: func _exit_tree() -> void:
_unload_mainscreen() _unload_mainscreen()
_remove_autoload()
_unregister_inspectors()
##自动删除单例
if ProjectSettings.has_setting("autoload/%s" % AUTOLOAD_REED_SCENE_NAME):
remove_autoload_singleton(AUTOLOAD_REED_SCENE_NAME)
# 移除 InspectorPlugin # ======================================================
# Inspector Plugins
# ======================================================
func _register_inspectors() -> void:
# Inspector 1ActManager
var act_inspector := preload(
"res://addons/reedscene/act/ActManagerInspector.gd"
).new()
add_inspector_plugin(act_inspector)
inspector_plugins.append(act_inspector)
# Inspector 2ReedSceneID
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: for plugin in inspector_plugins:
remove_inspector_plugin(plugin) remove_inspector_plugin(plugin)
inspector_plugins.clear() 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: 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) EditorInterface.get_editor_main_screen().add_child(main_screen)
main_screen.visible = false main_screen.visible = false
##卸載主界面
func _unload_mainscreen() -> void: func _unload_mainscreen() -> void:
if main_screen: if main_screen:
main_screen.queue_free() main_screen.queue_free()
main_screen = null main_screen = null
func _has_main_screen() -> bool: func _has_main_screen() -> bool:
return true return true
func _make_visible(visible: bool) -> void: func _make_visible(visible: bool) -> void:
if not main_screen: return if not main_screen:
return
main_screen.visible = visible main_screen.visible = visible
if visible:
if visible and main_screen.has_method("on_activated"):
main_screen.on_activated() main_screen.on_activated()
# ======================================================
# Meta
# ======================================================
func _get_plugin_name() -> String: func _get_plugin_name() -> String:
return "Reed Scene" return "Reed Scene"
func _get_plugin_icon() -> Texture2D: func _get_plugin_icon() -> Texture2D:
return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons") return EditorInterface.get_editor_theme().get_icon(
"Node",
"EditorIcons"
)

View File

@ -19,6 +19,6 @@ V1.0
- Scene管理器全局ID功能 - Scene管理器全局ID功能
V1.1 V1.1
- 添加和规范API
- 删除不必要的ClassName - 删除不必要的ClassName
- 完善ActManager的编辑者友好界面 - 完善ActManager的编辑者友好界面