godot-plateformer/addons/reedscene/prop/PropComponent.gd

179 lines
4.6 KiB
GDScript3
Raw Normal View History

'''此类为Prop的管理组件类任何的Prop其至少需要在此类的最上层的子层添加该组件
Tree时States节点作为其所有state的root
state作为其子节点
'''
@tool
@icon("res://addons/reedscene/prop/icon/prop_icon.svg")
class_name PropComponent extends Node
## ==============================
## Export
## ==============================
##此物件的描述ID
@export var prop_id: int
##初始的默认state_id
@export var initial_state_id: int = 0
##是否需要输出错误
@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 DEFAULT_STATE_ID := 0
func _enter_tree() -> void:
if Engine.is_editor_hint():
_editor_ensure_states_root()
func _ready() -> void:
_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:
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
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())
##检查当前是否存在当前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()
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])
## 切换状态,如果 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
## ==============================
## 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
states = Node.new()
states.name = states_root_name
add_child(states)
# ⚠️ 关键:让它成为 Scene 的一部分
states.owner = get_tree().edited_scene_root
if debug_log:
print("[PropComponent] Created States root")