@tool @icon("uid://p0oxphym6oqg") ''' 此类为一个level的最小单元,其可以承载任意多的prop和act 你可以这样理解,一个level(大关卡),带有n个小关卡,n = scene的数量 Scene 必须带有: 1.SceneManager 2.Act Manager 3.复数个Prop 其中: ActManager下,带有复数个Act,其主要用于记录各个Prop的不同state,当Act切换时,Prop对应的状态也会切换。 ActManager的主要作用就是管理这些Act的切换。 Prop可以简单的理解为场景中的非地形碰撞的,与玩家可交互的道具,AI,等。 所有的Prop会自带一个PropComponent,其核心功能是负责管理Prop自身的state和state的切换 举例:一个门,从State1 -> State2,state1是关闭状态,state2是打开状态,那么: 1. 我们在State1中定义,门是关的,State2中定义,门是开的。简单的实现比如我们去设置门的坐标 2. 我们会做一个State1 -> State2的流转函数,其定义了,State1->State2的具体行为,比如,播放一个门打开的动画。 对于任意的添加到Scene的Prop,如果其自身的最上层子节点集中不含有PropComponent,Scene会默认的给他发一个PropComponent 注意,我并不推荐在Prop上直接挂载PropComponent,通过Scene来添加更为合适。 对于Scene,Scene通过PropComponent上的ID,通过ID获取到PropComponent所挂载的组件,这也是为什么我强制要求 PropComponent必须是处于Prop的最上层子节点集。 Scene自身也有SceneID,SceneID是实现跨Scene修改的核心,SceneID是插件内置的系统主动生成的,且理论上不允许修改, 对于每个Scene来说,SceneID是唯一且绝对的,这个涉及到后续的异步加载问题,虽然2D游戏可能没有这些问题。 比如说,我们可以让玩家在Scene1的时候,触发Scene9的一个ActChange,发射一个火焰流星雨,但是实际上这个东西是可以被在Scene的玩家所 观察的。 ''' class_name ReedScene extends Node2D ## ============================== ## Const Config ## ============================== ##Act管理器的命名 const ACT_MANAGER_NAME := "ActManager" ##Prop的根节点命名 const PROPS_ROOT_NAME := "Props" ##场景管理器的根节点命名 const SCENE_MANAGER_NAME := "SceneManager" ##Prop的路徑 const PROPS_ROOT_PATH: NodePath = ^"Props" ##PropManager脚本的路徑 const PROPS_MANAGER_SCRIPT: Script = preload("res://addons/reedscene/prop/PropManager.gd") ## ============================== ## Export Config ## ============================== ##是否要自動的為Props下的Node添加PropComponent @export var auto_attach_prop_component := true ##是否要打印DebugLog @export var debug_log := false ## ============================== ## Internal State ## ============================== var _scene_id_comp: ReedSceneID var _act_manager: ActManager var _props_root: Node var _prop_map: Dictionary = {} # prop_id -> PropComponent var _scene_manager: SceneManager ## ============================== ## Build In ## ============================== func _enter_tree() -> void: if Engine.is_editor_hint(): _editor_ensure_scene_nodes() ##进入场景树,自动添加ActManager,PropsRoot,和SceneManager _editor_ensure_scene_id_comp() ##進入場景樹,則自動添加一個IDComp func _notification(what): if what == NOTIFICATION_PARENTED: if get_parent() != null and not (get_parent() is ReedScene): push_warning("ReedSceneID must stay under ReedScene.") func _ready() -> void: _resolve_scene_id_comp() _resolve_scene_manager() _resolve_act_manager() _resolve_props_root() _collect_props() _bind_act_events() if Engine.is_editor_hint(): return ## 初始化Prop var pcs : Array = _prop_map.values() for pc in pcs: pc.init() ## act manger 切换一次 _act_manager.switch_act_with_id(_act_manager.DEFAULT_ACT_ID) ## 如果prop有复写init state的选项,则overwrite for pc in pcs: pc._init_state_check() ReedSceneRegistry.register_scene(self) ## ============================== ## Resolve References ## ============================== func _resolve_scene_id_comp() -> void: _scene_id_comp = null for child in get_children(): if child is ReedSceneID: _scene_id_comp = child break if _scene_id_comp == null: push_error("[ReedScene] ReedSceneID not found.") func _resolve_act_manager() -> void: _act_manager = get_node_or_null("ActManager") if _act_manager == null: push_error("[ReedScene] ActManager not found.") return func _resolve_props_root() -> void: _props_root = get_node_or_null(PROPS_ROOT_PATH) if _props_root == null: push_error("[ReedScene] Props root not found: %s" % PROPS_ROOT_PATH) func _resolve_scene_manager() -> void: _scene_manager = get_node_or_null(SCENE_MANAGER_NAME) if _scene_manager == null: push_error("[ReedScene] SceneManager not found.") ## ============================== ## Prop Collection ## ============================== func _collect_props() -> void: _prop_map.clear() if _props_root == null: push_warning("[ReedScene]:Scene[ID:%s] dont have a prop root" % _scene_id_comp.get_scene_id()) return for prop in _props_root.get_children(): if not prop is Node: continue var prop_comp := _find_prop_component(prop) if prop_comp == null: continue var prop_id := prop_comp.prop_id if prop_id < 0: push_warning("[ReedScene] Prop has invaild ID: %s" % prop.name) continue if _prop_map.has(prop_id): push_error("[ReedScene] Duplicate Prop ID: %s" % prop_id) continue _prop_map[prop_id] = prop_comp if debug_log: print("[ReedScene] Registered Prop:", prop_id) ##獲取到一個Prop的PropComp func _find_prop_component(prop: Node) -> PropComponent: for child in prop.get_children(): if child is PropComponent: return child return null ## ============================== ## Act Binding ## ============================== func _bind_act_events() -> void: if _act_manager == null: return _act_manager.act_changed.connect(_on_act_changed) func _on_act_changed(from_act: int, to_act: int) -> void: if debug_log: print("[ReedScene] Act changed:", from_act, "->", to_act) ##进入场景树,自动添加ActManager,PropsRoot,和SceneManager func _editor_ensure_scene_nodes() -> void: _editor_ensure_node(SCENE_MANAGER_NAME, SceneManager) _editor_ensure_node(ACT_MANAGER_NAME, ActManager) _editor_ensure_node_from_script(PROPS_ROOT_NAME,PROPS_MANAGER_SCRIPT) ##添加命名和节点 func _editor_ensure_node(name: String, type: Variant) -> Node: var node := get_node_or_null(name) if node != null: return node node = type.new() node.name = name add_child(node) node.owner = get_tree().edited_scene_root if debug_log: print("[ReedScene][Editor] Created node:", name) return node ##通過脚本添加節點 func _editor_ensure_node_from_script( name: String, script: Script ) -> Node: assert(script != null) var node := get_node_or_null(name) if node != null: return node node = script.new() node.name = name add_child(node) node.owner = get_tree().edited_scene_root if debug_log: print("[ReedScene][Editor] Created node from script:", name) return node ##保證存在ID節點 func _editor_ensure_scene_id_comp() -> ReedSceneID: # 1️. 查找已有的 ReedSceneID(只找直接子节点) var id_comp: ReedSceneID = null for child in get_children(): if child is ReedSceneID: id_comp = child break # 2️. 不存在就创建 if id_comp == null: id_comp = ReedSceneID.new() id_comp.name = "SceneID" add_child(id_comp) # 关键:设 owner,才能被保存到场景 id_comp.owner = get_tree().edited_scene_root if debug_log: print("[ReedScene][Editor] Created ReedSceneID") # 3️. 确保在子节点最上层(index = 0) if get_child(0) != id_comp: move_child(id_comp, 0) return id_comp ## ============================== ## Externel API ## ============================== ## 外部取得Prop映射對 func get_prop_map() -> Dictionary: return _prop_map ## 切換Act func switch_act_by_id(act_id: int) -> void: if not _act_manager: push_warning("[ReedScene] ActManager requested before ready.") _act_manager.switch_act_with_id(act_id) ## 獲取關卡管理器 func get_scene_manager() -> SceneManager: if _scene_manager == null: push_warning("[ReedScene] SceneManager requested before ready.") return _scene_manager ## 獲取SceneID Comp func get_scene_id_comp() -> ReedSceneID: if _scene_id_comp == null: push_warning("[ReedScene] Scene not has a ID Comp.") return _scene_id_comp ## 獲取Prop的根節點 func get_props_root() -> Node2D: return _props_root ## 獲取SceneID func get_scene_id() -> int: if get_scene_id_comp().has_id(): return get_scene_id_comp().scene_id return -1 ## Prop的数量 func get_prop_count() -> int: return get_prop_map().values().size()