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

191 lines
5.2 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'''此类为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
##是否需要输出错误
@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
if not _states_root is ReedPropStateManager:
push_error("[PropComponent:%s] States root is not ReedPropStateManager" % 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
# 创建 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")