godot-plateformer/addons/reedscene/scene/ReedScene.gd

311 lines
9.2 KiB
GDScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

@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 -> State2state1是关闭状态state2是打开状态那么
1. 我们在State1中定义门是关的State2中定义门是开的。简单的实现比如我们去设置门的坐标
2. 我们会做一个State1 -> State2的流转函数其定义了State1->State2的具体行为比如播放一个门打开的动画。
对于任意的添加到Scene的Prop如果其自身的最上层子节点集中不含有PropComponentScene会默认的给他发一个PropComponent
注意我并不推荐在Prop上直接挂载PropComponent通过Scene来添加更为合适。
对于SceneScene通过PropComponent上的ID通过ID获取到PropComponent所挂载的组件这也是为什么我强制要求
PropComponent必须是处于Prop的最上层子节点集。
Scene自身也有SceneIDSceneID是实现跨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")
##所有的Scene都会在进入Tree时被添加进入这个节点。
const SCENE_GROUP: StringName = "REED_SCENE"
## ==============================
## 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 not self.is_in_group(SCENE_GROUP):
self.add_to_group(SCENE_GROUP)
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 if _act_manager.init_act_id < 0 else _act_manager.init_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
func _get_default_act_id() -> int:
return 0
## ==============================
## Externel API
## ==============================
##外部可以通过此函数将Scene重置回默认状态_get_default_act_id的逻辑将在未来可以和其他脚本联动并重写。
func reset_to_default_act() -> void:
self.switch_act_by_id(self._get_default_act_id())
## 外部取得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()