'''此类为Prop的管理组件类,任何的Prop,其至少需要在此类的最上层的子层添加该组件 该管理器,在添加入Tree时,会自动的添加一个States节点作为其所有state的root, 并且添加一个默认的state作为其子节点。 ''' @tool @icon("res://addons/reedscene/prop/icon/prop_icon.svg") class_name PropComponent extends Node ## ============================== ## Export ## ============================== ##此物件的描述ID,无法主动修改,由PropManager发信 @export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY) var prop_id: int = -1 ##初始的默认state_id @export var initial_state_id: int = 0 ##如果为真,则无论ActManger的配置为任何,在Inital的时候都会使用自己定义的initial state @export var overwrite_init_state: bool = false ##是否需要输出错误 @export var debug_log := false ##是否等待 @export var should_wait_owner_ready :bool = true ##状态切换的信号 signal state_changed(from_state: int, to_state: int, ctx: Dictionary) ##state的根节点,所有的state都需要连在根节点上 var _states_root: Node var _state_map: Dictionary = {} var _current: ReedPropState = null const STATES_ROOT_NAME := "States" const STATES_ROOT_SCRIPT := preload("res://addons/reedscene/prop/StateManager.gd") const DEFAULT_STATE_ID := 0 func _enter_tree() -> void: if Engine.is_editor_hint(): _editor_ensure_states_root() func _ready() -> void: if Engine.is_editor_hint(): _editor_ready() _build_state_cache() ## 只在編輯器模式的Ready裏Call的函數 func _editor_ready() -> void: child_exiting_tree.connect(_on_child_exiting_tree) ## 用于初始化状态 func init() -> void: _init_states() ## 用于检查初始状态复写的Check func _init_state_check() ->void: if _current == null and initial_state_id >= 0 and overwrite_init_state: change_state(initial_state_id, false, { "reason": "INIT", "instant": true }) ## 在構造時,映射State到stateID func _build_state_cache() -> void: _state_map.clear() _states_root = get_node_or_null(STATES_ROOT_NAME) if _states_root == null: push_warning("[PropComponent:%s] States root not found" % prop_id) return ## 判斷子節點是否是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(): if c is ReedPropState: var s := c as ReedPropState if _state_map.has(s.state_id): push_error("[PropComponent:%s] Duplicate state_id: %d" % [prop_id, s.state_id]) continue _state_map[s.state_id] = s if debug_log: print("[PropComponent:%s] States:", _state_map.keys()) ##初始化状态 func _init_states() -> void: var owner := get_parent() if owner == null: push_error("[PropComponent:%s] Owner node not found." % prop_id) return for s in _state_map.values(): var ok :bool = s.init(owner, self) if not ok: 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): push_warning("[PropComponent:%s] State not found: %d" % [prop_id, state_id]) return false var next: ReedPropState = _state_map[state_id] if _current == next: return true if not next.can_enter(_current, ctx): if debug_log: print("[PropComponent:%s] can_enter rejected: %d" % [prop_id, state_id]) return false var from_state_id := get_current_state_id() var prev := _current # ---------- EXIT ---------- if _current: _current.on_exit(next, ctx) # ---------- SWITCH CURRENT ---------- _current = next # ---------- TRANSITION CHECK ---------- var transition_handled := false if use_trans: for child in _current.get_children(): if child is ReedTransition: if child.can_trigger(prev, ctx): if debug_log: print("[PropComponent:%s] Transition triggered: %s" % [prop_id, child.name]) transition_handled = child.execute(prev, _current, ctx) if transition_handled: break # ---------- ENTER ---------- if not transition_handled: _current.on_enter(prev, ctx) else: if debug_log: print("[PropComponent:%s] StateEnter skipped by Transition: %d" % [prop_id, state_id]) if debug_log: print("[PropComponent:%s] State %d -> %d | ctx=%s" % [prop_id, from_state_id, state_id, ctx]) return true ## 获取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) ## 获取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 ) return extract_raw_name.call(n) #endregion