311 lines
9.2 KiB
GDScript
311 lines
9.2 KiB
GDScript
@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")
|
||
##所有的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()
|