关卡管理器组件完成

This commit is contained in:
Reed 2026-01-02 18:37:09 +08:00
parent bc848b8526
commit 46f69e2336
43 changed files with 1271 additions and 541 deletions

View File

@ -1,21 +1,3 @@
extends Node2D extends Node2D
@onready var act_manager: ActManager = $ReedScene/ActManager
var s := 1 var s := 1
func _unhandled_input(event: InputEvent) -> void:
if act_manager == null:
return
if event.is_action_pressed("ui_right"):
s = clampi(s+1,0,2)
act_manager.switch_act_with_id(s)
elif event.is_action_pressed("ui_left"):
s = clampi(s-1,0,2)
act_manager.switch_act_with_id(s)
elif event.is_action_pressed("ui_up"):
act_manager.switch_act_with_id(3)
elif event.is_action_pressed("ui_down"):
act_manager.switch_act_with_id(4)

View File

@ -1,8 +1,9 @@
[gd_scene load_steps=4 format=4 uid="uid://3vc8ojbiyy5w"] [gd_scene load_steps=5 format=4 uid="uid://3vc8ojbiyy5w"]
[ext_resource type="PackedScene" uid="uid://gwhff4qaouxy" path="res://_player/Avatar.tscn" id="1_fdx6o"] [ext_resource type="PackedScene" uid="uid://gwhff4qaouxy" path="res://_player/Avatar.tscn" id="1_fdx6o"]
[ext_resource type="PackedScene" uid="uid://1l06de041i40" path="res://_levels/l_level_1.tscn" id="1_pvnxo"] [ext_resource type="PackedScene" uid="uid://1l06de041i40" path="res://_levels/l_level_1.tscn" id="1_pvnxo"]
[ext_resource type="PackedScene" uid="uid://cvqehvdjpoar4" path="res://_player/player_controller.tscn" id="2_j2xwq"] [ext_resource type="PackedScene" uid="uid://cvqehvdjpoar4" path="res://_player/player_controller.tscn" id="2_j2xwq"]
[ext_resource type="PackedScene" uid="uid://c3mievyfhx6ni" path="res://_props/coin/coin.tscn" id="4_j2xwq"]
[node name="Game" type="Node2D"] [node name="Game" type="Node2D"]
@ -13,5 +14,14 @@ tile_map_data = PackedByteArray("AAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAIAAAAAAAA
auto_controlled_avatar = NodePath("../Avatar") auto_controlled_avatar = NodePath("../Avatar")
[node name="Avatar" parent="." instance=ExtResource("1_fdx6o")] [node name="Avatar" parent="." instance=ExtResource("1_fdx6o")]
position = Vector2(312, 195) position = Vector2(349, -271)
collision_mask = 4 collision_mask = 4
[node name="Coin" parent="." instance=ExtResource("4_j2xwq")]
position = Vector2(479, -182)
[node name="Coin2" parent="." instance=ExtResource("4_j2xwq")]
position = Vector2(585, -184)
[node name="Coin3" parent="." instance=ExtResource("4_j2xwq")]
position = Vector2(695, -185)

File diff suppressed because one or more lines are too long

184
_game/scenes/l_1_s_1.tscn Normal file
View File

@ -0,0 +1,184 @@
[gd_scene load_steps=28 format=4 uid="uid://bt55vmoc83l6g"]
[ext_resource type="Script" uid="uid://5e157vdk6175" path="res://addons/reedscene/scene/ReedScene.gd" id="1_wiqi4"]
[ext_resource type="Script" uid="uid://bh066o84byplh" path="res://addons/reedscene/scene/ReedSceneID.gd" id="2_1t0dx"]
[ext_resource type="Script" uid="uid://43ula8nd3yl7" path="res://_game/scenes/scripts/s_l1_s1.gd" id="3_1t0dx"]
[ext_resource type="Script" uid="uid://dsgl7lbyjsiif" path="res://addons/reedscene/act/ActManager.gd" id="4_a224g"]
[ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="5_a224g"]
[ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="5_fyfyw"]
[ext_resource type="PackedScene" uid="uid://b5nx4dntm0gyn" path="res://_props/door_manager/event_trigger_door.tscn" id="6_8hwae"]
[ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="6_fyfyw"]
[ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="7_s6kod"]
[ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="8_1dh04"]
[ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="9_648qh"]
[ext_resource type="PackedScene" uid="uid://1l06de041i40" path="res://_levels/l_level_1.tscn" id="10_25twt"]
[ext_resource type="PackedScene" uid="uid://c3mievyfhx6ni" path="res://_props/coin/coin.tscn" id="11_1t0dx"]
[ext_resource type="Script" uid="uid://cdvgq0xqdbagk" path="res://addons/reedscene/prop/ReedPropEffect.gd" id="12_fyfyw"]
[ext_resource type="Script" uid="uid://jeybblac0kg2" path="res://addons/reedscene/prop/ReedTransition.gd" id="13_8hwae"]
[sub_resource type="Resource" id="Resource_8hwae"]
script = ExtResource("6_fyfyw")
[sub_resource type="Resource" id="Resource_s6kod"]
script = ExtResource("6_fyfyw")
[sub_resource type="Resource" id="Resource_1dh04"]
script = ExtResource("5_a224g")
prop_state_map = Dictionary[int, ExtResource("6_fyfyw")]({
0: SubResource("Resource_8hwae"),
1: SubResource("Resource_s6kod")
})
[sub_resource type="Resource" id="Resource_gu0l6"]
script = ExtResource("6_fyfyw")
state_id = 1
use_trans = true
metadata/_custom_type_script = "uid://baqgorvlumyju"
[sub_resource type="Resource" id="Resource_648qh"]
script = ExtResource("5_a224g")
prop_state_map = Dictionary[int, ExtResource("6_fyfyw")]({
0: SubResource("Resource_gu0l6")
})
[sub_resource type="Resource" id="Resource_onwc0"]
script = ExtResource("6_fyfyw")
use_trans = true
metadata/_custom_type_script = "uid://baqgorvlumyju"
[sub_resource type="Resource" id="Resource_7tspm"]
script = ExtResource("6_fyfyw")
state_id = 1
use_trans = true
metadata/_custom_type_script = "uid://baqgorvlumyju"
[sub_resource type="Resource" id="Resource_jh33u"]
script = ExtResource("5_a224g")
prop_state_map = Dictionary[int, ExtResource("6_fyfyw")]({
0: SubResource("Resource_onwc0"),
1: SubResource("Resource_7tspm")
})
[sub_resource type="Resource" id="Resource_fyfyw"]
script = ExtResource("12_fyfyw")
effect_type = 1
value = null
func_name = &"reset_door_state_id"
func_args = [0]
metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_bco80"]
script = ExtResource("12_fyfyw")
effect_type = 1
value = null
func_name = &"door_close"
metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_22pon"]
script = ExtResource("12_fyfyw")
effect_type = 1
value = null
func_name = &"reset_door_state_id"
func_args = [1]
metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_25twt"]
script = ExtResource("12_fyfyw")
effect_type = 1
value = null
func_name = &"door_open"
metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[node name="l1_s1" type="Node2D"]
script = ExtResource("1_wiqi4")
metadata/_custom_type_script = "uid://5e157vdk6175"
[node name="[ID_ 9900001]" type="Node" parent="."]
script = ExtResource("2_1t0dx")
scene_id = 9900001
[node name="SceneManager" type="Node" parent="."]
script = ExtResource("3_1t0dx")
[node name="ActManager" type="Node" parent="."]
script = ExtResource("4_a224g")
prop_state_map = Dictionary[int, ExtResource("5_a224g")]({
0: SubResource("Resource_1dh04"),
1: SubResource("Resource_648qh"),
2: SubResource("Resource_jh33u")
})
[node name="Props" type="Node2D" parent="."]
script = ExtResource("5_fyfyw")
[node name="EventTriggerDoor" parent="Props" instance=ExtResource("6_8hwae")]
position = Vector2(656, 268)
[node name="[Prop_0000]" type="Node" parent="Props/EventTriggerDoor"]
script = ExtResource("7_s6kod")
prop_id = 0
[node name="States" type="Node" parent="Props/EventTriggerDoor/[Prop_0000]"]
script = ExtResource("8_1dh04")
[node name="[ID_0] Close" type="Node" parent="Props/EventTriggerDoor/[Prop_0000]/States"]
script = ExtResource("9_648qh")
state_id = 0
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_fyfyw")])
[node name="From1" type="Node" parent="Props/EventTriggerDoor/[Prop_0000]/States/[ID_0] Close"]
script = ExtResource("13_8hwae")
from_state_id = 1
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_bco80")])
metadata/_custom_type_script = "uid://jeybblac0kg2"
[node name="[ID_1] Open" type="Node" parent="Props/EventTriggerDoor/[Prop_0000]/States"]
script = ExtResource("9_648qh")
state_id = 1
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_22pon")])
[node name="From0" type="Node" parent="Props/EventTriggerDoor/[Prop_0000]/States/[ID_1] Open"]
script = ExtResource("13_8hwae")
from_state_id = 0
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_25twt")])
metadata/_custom_type_script = "uid://jeybblac0kg2"
[node name="EventTriggerDoor2" parent="Props" instance=ExtResource("6_8hwae")]
position = Vector2(15, 270)
[node name="[Prop_0001]" type="Node" parent="Props/EventTriggerDoor2"]
script = ExtResource("7_s6kod")
prop_id = 1
[node name="States" type="Node" parent="Props/EventTriggerDoor2/[Prop_0001]"]
script = ExtResource("8_1dh04")
[node name="[ID_0] Close" type="Node" parent="Props/EventTriggerDoor2/[Prop_0001]/States"]
script = ExtResource("9_648qh")
state_id = 0
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_fyfyw")])
[node name="From1" type="Node" parent="Props/EventTriggerDoor2/[Prop_0001]/States/[ID_0] Close"]
script = ExtResource("13_8hwae")
from_state_id = 1
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_bco80")])
metadata/_custom_type_script = "uid://jeybblac0kg2"
[node name="[ID_1] Open" type="Node" parent="Props/EventTriggerDoor2/[Prop_0001]/States"]
script = ExtResource("9_648qh")
state_id = 1
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_22pon")])
[node name="From0" type="Node" parent="Props/EventTriggerDoor2/[Prop_0001]/States/[ID_1] Open"]
script = ExtResource("13_8hwae")
from_state_id = 0
effects = Array[ExtResource("12_fyfyw")]([SubResource("Resource_25twt")])
metadata/_custom_type_script = "uid://jeybblac0kg2"
[node name="l1_s1_tile" parent="." instance=ExtResource("10_25twt")]
tile_map_data = PackedByteArray("AAAAAAQAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAkAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAwAAAAAAAAAAAAAABQAAAAAAAAAAAAAABUAAAAAAAAAAAAAABYAAAAAAAAAAAABAAQAAAAAAAAAAAABAAUAAAAAAAAAAAABAAYAAAAAAAAAAAABAAcAAAAAAAAAAAABAAgAAAAAAAAAAAABAAkAAAAAAAAAAAABAAoAAAAAAAAAAAABAAsAAAAAAAAAAAABAAwAAAAAAAAAAAABABQAAAAAAAAAAAABABUAAAAAAAAAAAABABYAAAAAAAAAAAACABQAAAAAAAAAAAACABUAAAAAAAAAAAACABYAAAAAAAAAAAADAAAAAAAAAAAAAAADAAEAAAAAAAAAAAADAAIAAAAAAAAAAAADABQAAAAAAAAAAAADABUAAAAAAAAAAAADABYAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAEAAIAAAAAAAAAAAAEABQAAAAAAAAAAAAEABUAAAAAAAAAAAAEABYAAAAAAAAAAAAFAAAAAAAAAAAAAAAFAAEAAAAAAAAAAAAFAAIAAAAAAAAAAAAFABQAAAAAAAAAAAAFABUAAAAAAAAAAAAFABYAAAAAAAAAAAAGABQAAAAAAAAAAAAGABUAAAAAAAAAAAAGABYAAAAAAAAAAAAHABQAAAAAAAAAAAAHABUAAAAAAAAAAAAHABYAAAAAAAAAAAAIABQAAAAAAAAAAAAIABUAAAAAAAAAAAAIABYAAAAAAAAAAAAJABQAAAAAAAAAAAAJABUAAAAAAAAAAAAJABYAAAAAAAAAAAAKABQAAAAAAAAAAAAKABUAAAAAAAAAAAAKABYAAAAAAAAAAAALABQAAAAAAAAAAAALABUAAAAAAAAAAAALABYAAAAAAAAAAAAMABQAAAAAAAAAAAAMABUAAAAAAAAAAAAMABYAAAAAAAAAAAANABQAAAAAAAAAAAANABUAAAAAAAAAAAANABYAAAAAAAAAAAAOABQAAAAAAAAAAAAOABUAAAAAAAAAAAAOABYAAAAAAAAAAAAPABQAAAAAAAAAAAAPABUAAAAAAAAAAAAPABYAAAAAAAAAAAAQABQAAAAAAAAAAAAQABUAAAAAAAAAAAAQABYAAAAAAAAAAAARABQAAAAAAAAAAAARABUAAAAAAAAAAAARABYAAAAAAAAAAAASABQAAAAAAAAAAAASABUAAAAAAAAAAAASABYAAAAAAAAAAAATABQAAAAAAAAAAAATABUAAAAAAAAAAAATABYAAAAAAAAAAAAUABQAAAAAAAAAAAAUABUAAAAAAAAAAAAUABYAAAAAAAAAAAAVABQAAAAAAAAAAAAVABUAAAAAAAAAAAAVABYAAAAAAAAAAAAGAAAAAAAAAAAAAAAGAAEAAAAAAAAAAAAGAAIAAAAAAAAAAAAHAAAAAAAAAAAAAAAHAAEAAAAAAAAAAAAHAAIAAAAAAAAAAAAIAAAAAAAAAAAAAAAIAAEAAAAAAAAAAAAIAAIAAAAAAAAAAAAJAAAAAAAAAAAAAAAJAAEAAAAAAAAAAAAJAAIAAAAAAAAAAAAKAAAAAAAAAAAAAAAKAAEAAAAAAAAAAAAKAAIAAAAAAAAAAAALAAAAAAAAAAAAAAALAAEAAAAAAAAAAAALAAIAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAEAAAAAAAAAAAAMAAIAAAAAAAAAAAANAAAAAAAAAAAAAAANAAEAAAAAAAAAAAANAAIAAAAAAAAAAAAOAAAAAAAAAAAAAAAOAAEAAAAAAAAAAAAOAAIAAAAAAAAAAAAcABQAAAAAAAAAAAAcABUAAAAAAAAAAAAcABYAAAAAAAAAAAAdABQAAAAAAAAAAAAdABUAAAAAAAAAAAAdABYAAAAAAAAAAAAeABQAAAAAAAAAAAAeABUAAAAAAAAAAAAeABYAAAAAAAAAAAAfABQAAAAAAAAAAAAfABUAAAAAAAAAAAAfABYAAAAAAAAAAAAgABQAAAAAAAAAAAAgABUAAAAAAAAAAAAgABYAAAAAAAAAAAAhABQAAAAAAAAAAAAhABUAAAAAAAAAAAAhABYAAAAAAAAAAAAiABQAAAAAAAAAAAAiABUAAAAAAAAAAAAiABYAAAAAAAAAAAAjABQAAAAAAAAAAAAjABUAAAAAAAAAAAAjABYAAAAAAAAAAAAkABQAAAAAAAAAAAAkABUAAAAAAAAAAAAkABYAAAAAAAAAAAAlABQAAAAAAAAAAAAlABUAAAAAAAAAAAAlABYAAAAAAAAAAAAmABQAAAAAAAAAAAAmABUAAAAAAAAAAAAmABYAAAAAAAAAAAAnABQAAAAAAAAAAAAnABUAAAAAAAAAAAAnABYAAAAAAAAAAAAoABQAAAAAAAAAAAAoABUAAAAAAAAAAAAoABYAAAAAAAAAAAApABQAAAAAAAAAAAApABUAAAAAAAAAAAApABYAAAAAAAAAAAAWABQAAAAAAAAAAAAWABUAAAAAAAAAAAAWABYAAAAAAAAAAAAbABQAAAAAAAAAAAAbABUAAAAAAAAAAAAbABYAAAAAAAAAAAAXABQAAAAAAAAAAAAXABUAAAAAAAAAAAAXABYAAAAAAAAAAAAYABQAAAAAAAAAAAAYABUAAAAAAAAAAAAYABYAAAAAAAAAAAAZABQAAAAAAAAAAAAZABUAAAAAAAAAAAAZABYAAAAAAAAAAAAaABQAAAAAAAAAAAAaABUAAAAAAAAAAAAaABYAAAAAAAAAAAAPAAAAAAAAAAAAAAAPAAEAAAAAAAAAAAAPAAIAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAEAAAAAAAAAAAAQAAIAAAAAAAAAAAARAAAAAAAAAAAAAAARAAEAAAAAAAAAAAARAAIAAAAAAAAAAAASAAAAAAAAAAAAAAASAAEAAAAAAAAAAAASAAIAAAAAAAAAAAATAAAAAAAAAAAAAAATAAEAAAAAAAAAAAATAAIAAAAAAAAAAAAUAAAAAAAAAAAAAAAUAAEAAAAAAAAAAAAUAAIAAAAAAAAAAAAVAAAAAAAAAAAAAAAVAAEAAAAAAAAAAAAVAAIAAAAAAAAAAAAWAAAAAAAAAAAAAAAWAAEAAAAAAAAAAAAWAAIAAAAAAAAAAAAXAAAAAAAAAAAAAAAXAAEAAAAAAAAAAAAXAAIAAAAAAAAAAAAYAAAAAAAAAAAAAAAYAAEAAAAAAAAAAAAYAAIAAAAAAAAAAAAZAAAAAAAAAAAAAAAZAAEAAAAAAAAAAAAZAAIAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAEAAAAAAAAAAAAaAAIAAAAAAAAAAAAbAAAAAAAAAAAAAAAbAAEAAAAAAAAAAAAbAAIAAAAAAAAAAAAcAAAAAAAAAAAAAAAcAAEAAAAAAAAAAAAcAAIAAAAAAAAAAAAdAAAAAAAAAAAAAAAdAAEAAAAAAAAAAAAdAAIAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAEAAAAAAAAAAAAeAAIAAAAAAAAAAAAfAAAAAAAAAAAAAAAfAAEAAAAAAAAAAAAfAAIAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAEAAAAAAAAAAAAgAAIAAAAAAAAAAAAhAAAAAAAAAAAAAAAhAAEAAAAAAAAAAAAhAAIAAAAAAAAAAAAiAAAAAAAAAAAAAAAiAAEAAAAAAAAAAAAiAAIAAAAAAAAAAAAjAAAAAAAAAAAAAAAjAAEAAAAAAAAAAAAjAAIAAAAAAAAAAAAkAAAAAAAAAAAAAAAkAAEAAAAAAAAAAAAkAAIAAAAAAAAAAAAlAAAAAAAAAAAAAAAlAAEAAAAAAAAAAAAlAAIAAAAAAAAAAAAmAAAAAAAAAAAAAAAmAAEAAAAAAAAAAAAmAAIAAAAAAAAAAAAnAAAAAAAAAAAAAAAnAAEAAAAAAAAAAAAnAAIAAAAAAAAAAAAoAAAAAAAAAAAAAAAoAAEAAAAAAAAAAAAoAAIAAAAAAAAAAAApAAAAAAAAAAAAAAApAAEAAAAAAAAAAAApAAIAAAAAAAAAAAAoAAMAAAAAAAAAAAAoAAQAAAAAAAAAAAAoAAUAAAAAAAAAAAAoAAYAAAAAAAAAAAAoAAcAAAAAAAAAAAAoAAgAAAAAAAAAAAAoAAkAAAAAAAAAAAAoAAoAAAAAAAAAAAAoAAsAAAAAAAAAAAAoAAwAAAAAAAAAAAApAAMAAAAAAAAAAAApAAQAAAAAAAAAAAApAAUAAAAAAAAAAAApAAYAAAAAAAAAAAApAAcAAAAAAAAAAAApAAgAAAAAAAAAAAApAAkAAAAAAAAAAAApAAoAAAAAAAAAAAApAAsAAAAAAAAAAAApAAwAAAAAAAAAAAA=")
[node name="Coin" parent="." instance=ExtResource("11_1t0dx")]
position = Vector2(596, 291)
[connection signal="collected" from="Coin" to="SceneManager" method="_on_coin_collected"]

117
_game/scenes/l_1_s_2.tscn Normal file

File diff suppressed because one or more lines are too long

105
_game/scenes/l_1_s_3.tscn Normal file

File diff suppressed because one or more lines are too long

24
_game/scenes/l_1_s_4.tscn Normal file
View File

@ -0,0 +1,24 @@
[gd_scene load_steps=6 format=3 uid="uid://jd5d3i4xqj2m"]
[ext_resource type="Script" uid="uid://5e157vdk6175" path="res://addons/reedscene/scene/ReedScene.gd" id="1_r4clk"]
[ext_resource type="Script" uid="uid://bh066o84byplh" path="res://addons/reedscene/scene/ReedSceneID.gd" id="2_oqwc4"]
[ext_resource type="Script" uid="uid://dn0ksjoswquf5" path="res://addons/reedscene/scene/SceneManager.gd" id="3_jbgha"]
[ext_resource type="Script" uid="uid://dsgl7lbyjsiif" path="res://addons/reedscene/act/ActManager.gd" id="4_w6ro2"]
[ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="5_qmrnk"]
[node name="l1_s4" type="Node2D"]
script = ExtResource("1_r4clk")
metadata/_custom_type_script = "uid://5e157vdk6175"
[node name="[ID_ 9900004]" type="Node" parent="."]
script = ExtResource("2_oqwc4")
scene_id = 9900004
[node name="SceneManager" type="Node" parent="."]
script = ExtResource("3_jbgha")
[node name="ActManager" type="Node" parent="."]
script = ExtResource("4_w6ro2")
[node name="Props" type="Node2D" parent="."]
script = ExtResource("5_qmrnk")

View File

@ -0,0 +1,5 @@
extends SceneManager
func _on_coin_collected(collector: Node2D) -> void:
_act_manager.switch_act_with_id(1)

View File

@ -0,0 +1 @@
uid://43ula8nd3yl7

View File

@ -0,0 +1,8 @@
extends SceneManager
func _on_coin_collected(collector: Node2D) -> void:
_act_manager.switch_act_with_id(1)
var l1_s1: ReedScene = ReedSceneRegistry.get_scene(9900001)
if l1_s1:
l1_s1.switch_act_by_id(2)

View File

@ -0,0 +1 @@
uid://bna42bay8yt1m

View File

@ -0,0 +1,5 @@
extends SceneManager
func _on_coin_collected(collector: Node2D) -> void:
_act_manager.switch_act_with_id(1)

View File

@ -0,0 +1 @@
uid://c4gk6pcjk8no3

View File

@ -38,8 +38,11 @@ class_name CelesteLocomotionComponent extends JumpLocomotionComponent
#region Custom Move (拉扯移動) #region Custom Move (拉扯移動)
@export_category("Custom Move") @export_category("Custom Move")
##進行自定義運動時,移動的拉力
@export var custom_move_force: float = 25.0 @export var custom_move_force: float = 25.0
##進行自定義運動時,移動的最大速度
@export var custom_move_max_speed: float = 420.0 @export var custom_move_max_speed: float = 420.0
##進行自定義運動時,如果于目標點小於此距離,則會解除自定義運動
@export var custom_move_stop_distance: float = 4.0 @export var custom_move_stop_distance: float = 4.0
var _custom_move_target_node: Node = null var _custom_move_target_node: Node = null

View File

@ -0,0 +1,19 @@
extends Area2D
signal collected(collector: Node2D)
func _ready() -> void:
self.body_entered.connect(_on_collected)
self.area_entered.connect(_on_collected)
func _on_collected(collection: Node2D) -> void:
var collector := collection.owner
if collector == null:
return
## 如果收集者存在收集接口,那麽就把自己傳過去
if collector.has_method("collect_item"):
collector.collect_item(self)
collected.emit(collector)
self.queue_free()

View File

@ -0,0 +1 @@
uid://b7o5456qrwgbf

View File

@ -0,0 +1,8 @@
[gd_scene load_steps=2 format=3 uid="uid://d20u8tfktepxg"]
[ext_resource type="Script" uid="uid://b7o5456qrwgbf" path="res://_props/_prefabs/collection_prefab.gd" id="1_8ns1b"]
[node name="CollectionPrefab" type="Area2D"]
collision_layer = 16
collision_mask = 2
script = ExtResource("1_8ns1b")

View File

@ -7,6 +7,8 @@
size = Vector2(20, 128) size = Vector2(20, 128)
[node name="Door" type="AnimatableBody2D"] [node name="Door" type="AnimatableBody2D"]
collision_layer = 4
collision_mask = 0
script = ExtResource("1_gt1uq") script = ExtResource("1_gt1uq")
[node name="Sprite2D" type="Sprite2D" parent="."] [node name="Sprite2D" type="Sprite2D" parent="."]

16
_props/coin/coin.tscn Normal file
View File

@ -0,0 +1,16 @@
[gd_scene load_steps=4 format=3 uid="uid://c3mievyfhx6ni"]
[ext_resource type="PackedScene" uid="uid://d20u8tfktepxg" path="res://_props/_prefabs/collection_prefab.tscn" id="1_r0qyb"]
[ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="2_igeyo"]
[sub_resource type="CircleShape2D" id="CircleShape2D_r0qyb"]
radius = 23.0
[node name="Coin" instance=ExtResource("1_r0qyb")]
[node name="CollisionShape2D" type="CollisionShape2D" parent="." index="0"]
shape = SubResource("CircleShape2D_r0qyb")
[node name="Sprite2D" type="Sprite2D" parent="." index="1"]
scale = Vector2(0.33, 0.33)
texture = ExtResource("2_igeyo")

View File

@ -35,8 +35,11 @@ const FALL_SPEED_EXCEED_TOLERANCE_THRESHOLD = 40
@export_category("Locomotion Properties") @export_category("Locomotion Properties")
@export_subgroup("Move") @export_subgroup("Move")
##存在輸入時,向最大移動輸入運動的加速度
@export var run_accel : float = 1200 @export var run_accel : float = 1200
##不存在輸入時向Vector.ZERO運動的加速度
@export var run_reduce : float = 1600 @export var run_reduce : float = 1600
##移動時的最大速度
@export var move_speed_max : float = 280 @export var move_speed_max : float = 280
var characterbody: CharacterBody2D var characterbody: CharacterBody2D

View File

@ -1,10 +1,32 @@
[gd_resource type="Resource" script_class="SceneIDDatabase" load_steps=2 format=3 uid="uid://4c26uejocdos"] [gd_resource type="Resource" script_class="SceneIDDatabase" load_steps=7 format=3 uid="uid://4c26uejocdos"]
[ext_resource type="Script" uid="uid://delj6tsrxv8s3" path="res://addons/reedscene/scene/SceneIDDatabase.gd" id="1_47k6c"] [ext_resource type="Script" uid="uid://delj6tsrxv8s3" path="res://addons/reedscene/scene/SceneIDDatabase.gd" id="1_47k6c"]
[ext_resource type="Script" uid="uid://u5521mvu4rsw" path="res://addons/reedscene/scene/SceneIDEntry.gd" id="1_qlo8y"]
[sub_resource type="Resource" id="Resource_qlo8y"]
script = ExtResource("1_qlo8y")
scene_path = "res://_game/scenes/l_1_s_1.tscn"
scene_uid = "uid://bt55vmoc83l6g"
scene_id = 9900001
[sub_resource type="Resource" id="Resource_nf1f2"]
script = ExtResource("1_qlo8y")
scene_path = "res://_game/scenes/l_1_s_2.tscn"
scene_uid = "uid://c6and5mqr3wv1"
scene_id = 9900002
[sub_resource type="Resource" id="Resource_ej86d"]
script = ExtResource("1_qlo8y")
scene_path = "res://_game/scenes/l_1_s_3.tscn"
scene_uid = "uid://0sivr6aig7gm"
scene_id = 9900003
[sub_resource type="Resource" id="Resource_0yfd7"]
script = ExtResource("1_qlo8y")
scene_path = "res://_game/scenes/l_1_s_4.tscn"
scene_uid = "uid://jd5d3i4xqj2m"
scene_id = 9900004
[resource] [resource]
script = ExtResource("1_47k6c") script = ExtResource("1_47k6c")
next_id = 10001 entries = Array[ExtResource("1_qlo8y")]([SubResource("Resource_qlo8y"), SubResource("Resource_nf1f2"), SubResource("Resource_ej86d"), SubResource("Resource_0yfd7")])
scene_map = {
"res://_game/LevelDemonstration.tscn": 10000
}

View File

@ -0,0 +1,137 @@
@tool
extends Control
@onready var grid: GridContainer = %GC_Rows
@onready var refresh_btn: Button = %BRefresh
const DB_PATH := "res://addons/reedscene/demo/data_base/GLOBAL_SCENE_ID.tres"
func _ready() -> void:
refresh_btn.pressed.connect(_reload)
#_reload()
func _notification(what: int) -> void:
# 切回该主屏时自动刷新(可选,但很实用)
if what == NOTIFICATION_VISIBILITY_CHANGED and visible:
pass
#_reload()
func _reload() -> void:
_clear_grid()
_build_header()
var db = load(DB_PATH)
if db == null:
# 没 DB 就只显示表头,避免空白
return
# 假设 db.entries: Array[SceneIDEntry],且 entry.scene_uid / entry.scene_id 存在
for entry in db.entries:
_add_row(entry)
func _clear_grid() -> void:
for c in grid.get_children():
c.queue_free()
func _build_header() -> void:
grid.columns = 5
_add_header_cell("ID")
_add_header_cell("Name")
_add_header_cell("UID")
_add_header_cell("Path")
_add_header_cell("Function")
func _add_header_cell(text: String) -> void:
var l := Label.new()
l.text = text
l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
l.clip_text = true
grid.add_child(l)
func _add_row(entry) -> void:
var uid_text: String = entry.scene_uid
var uid_id := ResourceUID.text_to_id(uid_text)
var path := ""
if uid_id != ResourceUID.INVALID_ID:
path = ResourceUID.get_id_path(uid_id)
var name := path.get_file().get_basename() if path != "" else "(missing)"
# 1) ID
grid.add_child(_cell_label(str(entry.scene_id)))
# 2) Name
grid.add_child(_cell_label(name))
# 3) UID
grid.add_child(_cell_label(uid_text))
# 4) Path先显示后面你想隐藏我们再做
grid.add_child(_cell_label(path))
# 5) Function真按钮
grid.add_child(_function_cell(uid_text))
func _cell_label(text: String) -> Control:
var l := Label.new()
l.text = text
l.clip_text = true
l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
return l
func _function_cell(uid_text: String) -> Control:
var box := HBoxContainer.new()
box.size_flags_horizontal = Control.SIZE_EXPAND_FILL
## 打開場景所使用的函數
var reveal_btn := Button.new()
reveal_btn.text = "Show"
reveal_btn.tooltip_text = "Reveal in FileSystem"
reveal_btn.pressed.connect(func():
_reveal_scene_in_filesystem_by_uid(uid_text)
)
box.add_child(reveal_btn)
# Delete 先做占位,下一步再落地到 Resource 修改/保存
var del_btn := Button.new()
del_btn.text = "Delete"
del_btn.pressed.connect(func():
print("[SceneID] TODO delete: ", uid_text)
)
box.add_child(del_btn)
return box
func _open_scene_by_uid(uid_text: String) -> void:
var uid_id := ResourceUID.text_to_id(uid_text)
if uid_id == ResourceUID.INVALID_ID:
push_warning("[SceneID] Invalid UID: %s" % uid_text)
return
var path := ResourceUID.get_id_path(uid_id)
if path == "" or not ResourceLoader.exists(path):
push_warning("[SceneID] Scene not found for UID: %s" % uid_text)
return
EditorInterface.open_scene_from_path(path)
## 打開文件管理系統裏的Scene
func _reveal_in_filesystem(path: String) -> void:
# FileSystemDock 才有 navigate_to_path
var dock := EditorInterface.get_file_system_dock()
dock.navigate_to_path(path)
## 通過UID打開文件管理系統裏的Scene
func _reveal_scene_in_filesystem_by_uid(uid_text: String) -> void:
var uid_id := ResourceUID.text_to_id(uid_text)
if uid_id == ResourceUID.INVALID_ID:
push_warning("[SceneID] Invalid UID: %s" % uid_text)
return
var path := ResourceUID.get_id_path(uid_id)
if path == "" or not ResourceLoader.exists(path):
push_warning("[SceneID] Scene not found for UID: %s" % uid_text)
return
var dock := EditorInterface.get_file_system_dock()
dock.navigate_to_path(path)
func on_activated() -> void:
_reload()

View File

@ -0,0 +1,69 @@
[gd_scene load_steps=2 format=3 uid="uid://detpkhtovi6bp"]
[ext_resource type="Script" uid="uid://bi8dmt4ypmbhq" path="res://addons/reedscene/dock/SceneIDMainPanel.gd" id="1_vunhl"]
[node name="Control" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1_vunhl")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 15
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 15
theme_override_constants/margin_bottom = 10
[node name="VBC_Main" type="VBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="RichTextLabel" type="RichTextLabel" parent="MarginContainer/VBC_Main"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
text = "ReedScene Manage Panel
V0.3"
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBC_Main"]
layout_mode = 2
size_flags_vertical = 3
[node name="HBC_Header" type="HBoxContainer" parent="MarginContainer/VBC_Main/VBoxContainer"]
layout_mode = 2
[node name="BRefresh" type="Button" parent="MarginContainer/VBC_Main/VBoxContainer/HBC_Header"]
unique_name_in_owner = true
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
text = "Refresh"
[node name="BLoad" type="Button" parent="MarginContainer/VBC_Main/VBoxContainer/HBC_Header"]
unique_name_in_owner = true
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
text = "Load Database"
[node name="Spacer" type="Control" parent="MarginContainer/VBC_Main/VBoxContainer/HBC_Header"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBC_Main/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="GC_Rows" type="GridContainer" parent="MarginContainer/VBC_Main/VBoxContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
columns = 5

View File

@ -1,27 +0,0 @@
@tool
extends Control
@onready var tree: Tree = %Tree
func _ready():
_setup_tree()
_fill_dummy_data()
func _setup_tree():
tree.clear()
tree.columns = 3
tree.set_column_title(0, "ID")
tree.set_column_title(1, "Scene Path")
tree.set_column_title(2, "Action")
func _fill_dummy_data():
var root := tree.create_item()
_add_row(root, 10001, "res://levels/level_1.tscn")
_add_row(root, 10002, "res://levels/level_2.tscn")
func _add_row(root: TreeItem, id: int, path: String):
var item := tree.create_item(root)
item.set_text(0, str(id))
item.set_text(1, path)
item.set_text(2, "Open")

View File

@ -1,17 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://detpkhtovi6bp"]
[ext_resource type="Script" uid="uid://bi8dmt4ypmbhq" path="res://addons/reedscene/dock/scene_id_main_screen.gd" id="1_jm7r3"]
[node name="SceneIdMainScreen" type="CenterContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_jm7r3")
[node name="Tree" type="Tree" parent="."]
unique_name_in_owner = true
layout_mode = 2
columns = 3
hide_root = true

View File

@ -3,5 +3,5 @@
name="ReedScene" name="ReedScene"
description="这是一个用于2D游戏场景管理的插件通过组件化的方式将一个场景分成了场景中的各个组件和组件所对应的状态用户可以通过切换状态来实现一些动画的效果也可以通过切换状态来快速的事件存档不同事件触发不同的结果的任务系统等总之比较灵活" description="这是一个用于2D游戏场景管理的插件通过组件化的方式将一个场景分成了场景中的各个组件和组件所对应的状态用户可以通过切换状态来实现一些动画的效果也可以通过切换状态来快速的事件存档不同事件触发不同的结果的任务系统等总之比较灵活"
author="ReedZhu" author="ReedZhu"
version=".1" version="1.0"
script="reedscene.gd" script="reedscene.gd"

View File

@ -3,11 +3,10 @@ extends EditorPlugin
var inspector_plugins: Array[EditorInspectorPlugin] = [] var inspector_plugins: Array[EditorInspectorPlugin] = []
const AUTOLOAD_REED_SCENE_NAME: StringName = "ReedSceneSystem" const AUTOLOAD_REED_SCENE_NAME: StringName = "ReedSceneRegistry"
const AUTOLOAD_REED_SCENE_PATH:= "res://addons/reedscene/scene/SceneRegistry.gd" const AUTOLOAD_REED_SCENE_PATH:= "res://addons/reedscene/scene/SceneRegistry.gd"
var toolbar_button: Button var toolbar_button: Button
var scene_id_window: Window
var main_screen: Control var main_screen: Control
func _enter_tree() -> void: func _enter_tree() -> void:
@ -45,7 +44,7 @@ func _exit_tree() -> void:
##加載主界面 ##加載主界面
func _load_mainscreen() -> void: func _load_mainscreen() -> void:
main_screen = preload("res://addons/reedscene/dock/scene_id_main_screen.tscn").instantiate() main_screen = preload("res://addons/reedscene/dock/SceneIDMainPanel.tscn").instantiate()
EditorInterface.get_editor_main_screen().add_child(main_screen) EditorInterface.get_editor_main_screen().add_child(main_screen)
main_screen.visible = false main_screen.visible = false
@ -59,8 +58,10 @@ func _has_main_screen() -> bool:
return true return true
func _make_visible(visible: bool) -> void: func _make_visible(visible: bool) -> void:
if main_screen: if not main_screen: return
main_screen.visible = visible main_screen.visible = visible
if visible:
main_screen.on_activated()
func _get_plugin_name() -> String: func _get_plugin_name() -> String:
return "Reed Scene" return "Reed Scene"

View File

@ -39,10 +39,8 @@ class_name ReedScene extends Node2D
##Act管理器的命名 ##Act管理器的命名
const ACT_MANAGER_NAME := "ActManager" const ACT_MANAGER_NAME := "ActManager"
##Prop的根节点命名 ##Prop的根节点命名
const PROPS_ROOT_NAME := "Props" const PROPS_ROOT_NAME := "Props"
##场景管理器的根节点命名 ##场景管理器的根节点命名
const SCENE_MANAGER_NAME := "SceneManager" const SCENE_MANAGER_NAME := "SceneManager"
@ -56,20 +54,48 @@ const SCENE_MANAGER_NAME := "SceneManager"
## ============================== ## ==============================
## Internal State ## Internal State
## ============================== ## ==============================
var _scene_id_comp: ReedSceneID
var _act_manager: ActManager var _act_manager: ActManager
var _props_root: Node var _props_root: Node
var _prop_map: Dictionary = {} # prop_id -> PropComponent var _prop_map: Dictionary = {} # prop_id -> PropComponent
var _scene_manager: SceneManager
## ============================== ## ==============================
## ## Build In
## ============================== ## ==============================
func _enter_tree() -> void: func _enter_tree() -> void:
if Engine.is_editor_hint(): if Engine.is_editor_hint():
_editor_ensure_scene_nodes() ##进入场景树自动添加ActManager,PropsRoot,和SceneManager _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 not Engine.is_editor_hint():
ReedSceneRegistry.register_scene(self)
## ============================== ## ==============================
## Resolve References ## 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: func _resolve_act_manager() -> void:
_act_manager = get_node_or_null("ActManager") _act_manager = get_node_or_null("ActManager")
if _act_manager == null: if _act_manager == null:
@ -81,6 +107,10 @@ func _resolve_props_root() -> void:
if _props_root == null: if _props_root == null:
push_error("[ReedScene] Props root not found: %s" % props_root_path) 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 ## Prop Collection
## ============================== ## ==============================
@ -155,8 +185,45 @@ func _editor_ensure_node(name: String, type: Variant) -> Node:
return node return node
func _ready() -> void: ##保證存在ID節點
_resolve_act_manager() func _editor_ensure_scene_id_comp() -> ReedSceneID:
_resolve_props_root() # 1. 查找已有的 ReedSceneID只找直接子节点
_collect_props() var id_comp: ReedSceneID = null
_bind_act_events() 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 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
func get_scene_id_comp() -> ReedSceneID:
if _scene_id_comp == null:
push_warning("[ReedScene] Scene not has a ID Comp.")
return _scene_id_comp

View File

@ -2,7 +2,96 @@
extends Node extends Node
class_name ReedSceneID class_name ReedSceneID
@export var scene_id: int = -1 ##此Scene的描述ID无法主动修改你可以向編輯器申請。
@export_custom(
PROPERTY_HINT_NONE,
"",
PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY
)
var scene_id: int = -1:
set(value):
if _scene_id == value:
return
_scene_id = value
if Engine.is_editor_hint():
_sync_node_name()
update_configuration_warnings()
get():
return _scene_id
var _scene_id: int = -1
var _is_syncing_name := false
const DB_PATH := "res://addons/reedscene/demo/data_base/GLOBAL_SCENE_ID.tres"
## ==============================
## Build In
## ==============================
## 進入Tree
func _enter_tree() -> void:
if Engine.is_editor_hint():
_sync_node_name()
## 初始化完成后
func _ready() -> void:
if Engine.is_editor_hint():
renamed.connect(func():
_sync_node_name()
)
func has_id() -> bool: func has_id() -> bool:
return scene_id >= 0 return scene_id >= 0
func _get_display_name() -> String:
if not has_id():
return "[Invalid!]"
return "[ID: %d]" % scene_id
func _sync_node_name() -> void:
if _is_syncing_name:
return
_is_syncing_name = true
var desired := _get_display_name()
if name != desired:
name = desired
_is_syncing_name = false
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
# 1. 结构校验(只跟 Node 结构有关)
var parent := get_parent()
if parent == null or not (parent is ReedScene):
warnings.append("ReedSceneID must be a child of ReedScene.")
return warnings
# 2. 自身状态
if not has_id():
warnings.append("Scene ID is not assigned.")
return warnings
# 3. DB 一致性校验(委托给 DB
var scene_path := parent.scene_file_path
if scene_path.is_empty():
warnings.append("Parent scene is not saved; cannot validate Scene ID.")
return warnings
var uid := ResourceUID.path_to_uid(scene_path)
var db := load(DB_PATH) as SceneIDDatabase
if not db:
warnings.append("SceneIDDatabase not found.")
return warnings
var result := db.validate_scene_id(scene_id, uid, scene_path)
if result == null:
return warnings
for msg in result.messages:
warnings.append(msg)
return warnings

View File

@ -22,12 +22,12 @@ static func request_id_for_scene(scene_path: String) -> int:
push_error("[ReedSceneIDEditorSystem] Database not found: " + DB_PATH) push_error("[ReedSceneIDEditorSystem] Database not found: " + DB_PATH)
return -1 return -1
var db := _clone_db_to_new(db_ro) var db : SceneIDDatabase = _clone_db_to_new(db_ro)
if db.scene_map.has(scene_path): if db.scene_map.has(scene_path):
return int(db.scene_map[scene_path]) return int(db.scene_map[scene_path])
var new_id := db.next_id var new_id :int = db.next_id
db.next_id += 1 db.next_id += 1
db.scene_map[scene_path] = new_id db.scene_map[scene_path] = new_id

View File

@ -1,31 +1,108 @@
@tool @tool
extends EditorInspectorPlugin extends EditorInspectorPlugin
const DB_PATH := "res://addons/reedscene/demo/data_base/GLOBAL_SCENE_ID.tres"
func _can_handle(object) -> bool: func _can_handle(object) -> bool:
return object is ReedSceneID return object is ReedSceneID
func _parse_begin(object): func _parse_begin(object) -> void:
var button := Button.new() var comp := object as ReedSceneID
button.text = "Request Scene ID"
button.pressed.connect(func(): var parent := comp.get_parent()
_request_id(object) var scene := parent if parent is ReedScene else null
var is_scene_valid := scene != null
var is_scene_saved := false
if scene != null:
is_scene_saved = not scene.scene_file_path.is_empty()
# ==============================
# 提示文本(状态说明)
# ==============================
if not is_scene_valid:
var warn := Label.new()
warn.text = "SceneID requires parent to be ReedScene."
warn.modulate = Color(1, 0.6, 0.4)
add_custom_control(warn)
elif not is_scene_saved:
var warn := Label.new()
warn.text = "Please save the scene (.tscn) before requesting a Scene ID."
warn.modulate = Color(1, 0.6, 0.4)
add_custom_control(warn)
# ==============================
# Request 按钮
# ==============================
var btn := Button.new()
btn.text = "Request Scene ID"
btn.disabled = not (is_scene_valid and is_scene_saved)
if not btn.disabled:
btn.pressed.connect(func():
_open_request_dialog(comp)
)
add_custom_control(btn)
func _open_request_dialog(comp: ReedSceneID) -> void:
var dialog := SceneIDRequestDialog.new()
EditorInterface.get_base_control().add_child(dialog)
dialog.request_confirmed.connect(func(prefix: int):
_request_with_prefix(comp, prefix)
dialog.queue_free()
) )
add_custom_control(button)
func _request_id(id_comp: ReedSceneID): dialog.popup_centered(Vector2i(420, 160))
var scene := id_comp.get_owner()
if scene == null: func _request_with_prefix(comp: ReedSceneID, prefix: int) -> void:
push_error("[ReedSceneID] Cannot find owning scene") var db := load(DB_PATH) as SceneIDDatabase
if not db:
push_error("SceneIDDatabase not found: %s" % DB_PATH)
return return
var scene_path := scene.scene_file_path # 1) SceneID 节点的父节点才是 Scene
if scene_path == "": var scene_node := comp.get_parent()
push_error("[ReedSceneID] Scene must be saved before requesting ID") if scene_node == null:
push_error("Request Scene ID failed: ReedSceneID has no parent.")
return return
var id := ReedSceneIDEditorSystem.request_id_for_scene(scene_path) # 2) 父节点必须是 ReedScene 或其子类
if id < 0: if not (scene_node is ReedScene):
push_error("Request Scene ID failed: parent must be ReedScene (or subclass). Got: %s" % scene_node.get_class())
return return
id_comp.scene_id = id # 3) 父节点必须是一个“保存过的场景”,否则拿不到稳定的 path
id_comp.notify_property_list_changed() var scene_path := scene_node.scene_file_path
if scene_path.is_empty():
push_error("Request Scene ID failed: parent scene has no scene_file_path (scene not saved?).")
return
# 4) 获取 UID这里按你的要求用父节点的 uid
# 注意instance_id 不是跨会话稳定的,如果你未来需要“真正稳定 UID”建议换成 ResourceUID / GUID
var uid := ResourceUID.path_to_uid(scene_path)
# 5) 请求一个可用 ID
var new_id := db.request_new_id(prefix)
if new_id < 0:
push_error("Request Scene ID failed: invalid new_id.")
return
# 6) 注册到数据库(使用父节点的 path + uid + id
var ok: bool = db.register_scene_id(scene_path, uid, new_id)
if not ok:
push_error("Request Scene ID failed: register_scene_id returned false.")
return
# 7) 写回组件
comp.scene_id = new_id
comp.notify_property_list_changed()
# 8) 保存数据库资源
var err := ResourceSaver.save(db, DB_PATH)
if err != OK:
push_error("Failed to save SceneIDDatabase: %s (err=%d)" % [DB_PATH, err])

View File

@ -1,7 +1,86 @@
''' 用來儲存所有的SceneID的數據集
'''
@tool
extends Resource extends Resource
class_name SceneIDDatabase class_name SceneIDDatabase
@export var next_id: int = 10000 var next_id: int = 10000
# scene_path (String) -> scene_id (int) @export var entries: Array[SceneIDEntry] = []
@export var scene_map: Dictionary = {}
func request_new_id(prefix: int) -> int:
##合理性檢測
if prefix < 0: return -1
##獲取到已經使用的所有的ID
var used_ids : Array[int] = []
var used_set := {}
for i in entries:
used_ids.append(i.scene_id)
# 转成 SetO(1) contains
for id in used_ids:
used_set[id] = true
var candidate := prefix + 1
while used_set.has(candidate):
candidate += 1
return candidate
func register_scene_id(
scene_path: String,
uid: String,
scene_id: int
) -> bool:
for entry in entries:
if entry.scene_id == scene_id:
push_error("SceneIDDatabase: duplicated scene_id %d" % scene_id)
return false
var entry := SceneIDEntry.new()
entry.scene_id = scene_id
entry.scene_path = scene_path
entry.scene_uid = uid
entries.append(entry)
emit_changed()
return true
func validate_scene_id(
scene_id: int,
scene_uid: String,
scene_path: String
) -> SceneIDValidationResult:
var result := SceneIDValidationResult.new()
var matched_entry : SceneIDEntry = null
for entry in entries:
if entry.scene_id == scene_id:
matched_entry = entry
break
if matched_entry == null:
result.add_warning(
"Scene ID %d is not registered in SceneIDDatabase."
% scene_id
)
return result
if matched_entry.scene_uid != scene_uid:
result.add_warning(
"Scene ID %d belongs to a different scene."
% scene_id
)
if matched_entry.scene_path != scene_path:
result.add_warning(
"Scene ID %d path mismatch.\nDB: %s\nCurrent: %s"
% [scene_id, matched_entry.scene_path, scene_path]
)
# ✅ 无论有没有 warning都返回 result
return result

View File

@ -0,0 +1,7 @@
@tool
extends Resource
class_name SceneIDEntry
@export var scene_path: String
@export var scene_uid: String
@export var scene_id: int

View File

@ -0,0 +1 @@
uid://u5521mvu4rsw

View File

@ -0,0 +1,36 @@
@tool
extends ConfirmationDialog
class_name SceneIDRequestDialog
signal request_confirmed(prefix: int)
var _prefix_edit: SpinBox
func _ready() -> void:
title = "Request Scene ID"
ok_button_text = "Request"
cancel_button_text = "Cancel"
var v := VBoxContainer.new()
add_child(v)
# Prefix
var h := HBoxContainer.new()
v.add_child(h)
var l := Label.new()
l.text = "Prefix:"
h.add_child(l)
_prefix_edit = SpinBox.new()
_prefix_edit.min_value = 0
_prefix_edit.max_value = 99999999
_prefix_edit.step = 1
_prefix_edit.value = 10000 # 默认值,可改成自动推断
_prefix_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
h.add_child(_prefix_edit)
confirmed.connect(_on_confirmed)
func _on_confirmed() -> void:
request_confirmed.emit(int(_prefix_edit.value))

View File

@ -0,0 +1 @@
uid://db5uy3p0j82o6

View File

@ -0,0 +1,9 @@
extends RefCounted
class_name SceneIDValidationResult
var is_valid: bool = true
var messages: PackedStringArray = []
func add_warning(msg: String) -> void:
is_valid = false
messages.append(msg)

View File

@ -0,0 +1 @@
uid://d1c3ov1qwrm3m

View File

@ -1,74 +1,68 @@
@tool
class_name SceneManager class_name SceneManager
extends Node extends Node
@export var MainNodes: Array[Node] ## ==============================
@export var GameView: Node ## References
@export var TransitionNode: Node ## ==============================
var _scene: ReedScene
func _enter_tree() -> void: var _act_manager: ActManager
check() var _props: Dictionary # prop_id -> PropComponent
## ==============================
## Lifecycle
## ==============================
func _ready() -> void: func _ready() -> void:
if !Engine.is_editor_hint(): await get_parent().ready
add_to_group("SceneManager")
get_tree().node_removed.connect(check) _resolve_scene()
if _scene == null:
# ========================= push_error("[SceneManager] Parent is not ReedScene.")
# 对外 API
# =========================
static func change(scene_path: String) -> void:
var mgr := get_instance()
if mgr:
mgr._change(scene_path)
else:
push_error("SceneManager not found in scene tree")
# =========================
# 内部逻辑
# =========================
func _change(scene_path: String) -> void:
if GameView == null:
push_error("SceneManager: GameView is null")
return return
# 清空旧场景
for child in GameView.get_children():
child.queue_free()
# 加载新场景
var packed: PackedScene = load(scene_path)
if packed == null:
push_error("Failed to load scene: %s" % scene_path)
return
var instance := packed.instantiate()
GameView.add_child(instance)
print_debug("SceneManager: Changed scene to ", scene_path)
# ========================= _resolve_references()
# 结构检查 / 自修复 _bind_signals()
# =========================
func check(node: Node = null) -> void:
if TransitionNode == node:
TransitionNode = null
var tr := find_child("Transition") ## ==============================
if tr != null: ## Resolve References
if TransitionNode == null: ## ==============================
TransitionNode = tr func _resolve_scene() -> void:
var p := get_parent()
if p is ReedScene:
_scene = p
else: else:
if TransitionNode == null: _scene = null
var transition := Control.new()
transition.name = "Transition"
add_child.call_deferred(transition)
transition.set_owner.call_deferred(self)
TransitionNode = transition
# ========================= func _resolve_references() -> void:
# 工具 # 从 ReedScene 拿“整理好的结果”
# ========================= _act_manager = _scene._act_manager
static func get_instance() -> SceneManager: _props = _scene._prop_map
var tree := Engine.get_main_loop() as SceneTree
return tree.get_first_node_in_group("SceneManager") as SceneManager
## ==============================
## Signal Binding
## ==============================
func _bind_signals() -> void:
if _act_manager:
_act_manager.act_changed.connect(_on_act_changed)
#for prop_id in _props:
#var prop : PropComponent = _props[prop_id]
#_bind_prop_signals(prop)
#
#func _bind_prop_signals(prop: PropComponent) -> void:
## 示例:监听 prop 的自定义信号
#if prop.has_signal("activated"):
#prop.activated.connect(func():
#_on_prop_activated(prop)
#)
## ==============================
## Callbacks
## ==============================
func _on_act_changed(from_act: int, to_act: int) -> void:
print("[SceneManager] Act changed:", from_act, "->", to_act)
func _on_prop_activated(prop: PropComponent) -> void:
print("[SceneManager] Prop activated:", prop.prop_id)

View File

@ -1,103 +1,63 @@
# id (int) -> WeakRef(ReedScene) extends Node
var _by_id: Dictionary = {}
signal registered(id: int, node: ReedScene) ## SceneID -> ReedScene
signal unregistered(id: int) var _scene_map: Dictionary = {}
signal conflict(id: int, existing: ReedScene, incoming: ReedScene) ## SceneID -> ReedSceneIDComp
var _scene_id_comp_map: Dictionary[int, ReedSceneID] = {}
# ------------------------- signal scene_registered(scene_id: int, scene: ReedScene)
# 注册 signal scene_unregistered(scene_id: int)
# -------------------------
func register(id: int, node: ReedScene) -> bool: func register_scene(scene: ReedScene) -> bool:
if id < 0: if scene == null:
push_error("[SceneRegistry] register(): invalid id (<0): %d" % id)
return false return false
if node == null: var id_comp := scene.get_scene_id_comp()
push_error("[SceneRegistry] register(): node is null for id=%d" % id) if id_comp == null:
push_warning("[SceneRegistry] Scene has no ReedSceneID.")
return false return false
_cleanup_dead(id) return register_scene_with_id(id_comp.scene_id, scene, id_comp)
if _by_id.has(id): func register_scene_with_id(
var existing: ReedScene = _by_id[id].get_ref() scene_id: int,
if existing == node: scene: ReedScene,
return true # 重复注册同一个实例,忽略 id_comp: ReedSceneID = null
) -> bool:
emit_signal("conflict", id, existing, node) if scene_id < 0:
push_error( push_warning("[SceneRegistry] Reject invalid SceneID: %d" % scene_id)
"[SceneRegistry] Duplicate id=%d existing=%s incoming=%s"
% [id, existing, node]
)
return false return false
_by_id[id] = weakref(node) if _scene_map.has(scene_id):
emit_signal("registered", id, node) push_warning("[SceneRegistry] SceneID already registered: %d" % scene_id)
return false
_scene_map[scene_id] = scene
if id_comp != null:
_scene_id_comp_map[scene_id] = id_comp
if scene.debug_log:
print("[SceneRegistry] Registered Scene:", scene_id, scene.name)
return true return true
# -------------------------
# 注销 func unregister_scene(scene: ReedScene) -> void:
# ------------------------- if scene == null:
func unregister(id: int, node: ReedScene = null) -> void:
if not _by_id.has(id):
return return
var existing: ReedScene = _by_id[id].get_ref() var id_comp := scene.get_node_or_null("SceneID") as ReedSceneID
if node != null and existing != node: if id_comp == null:
# 防止误删
return return
_by_id.erase(id) var scene_id := id_comp.scene_id
emit_signal("unregistered", id) if not _scene_map.has(scene_id):
# -------------------------
# 查询
# -------------------------
func get_scene(id: int) -> ReedScene:
_cleanup_dead(id)
var ref: WeakRef = _by_id.get(id)
return ref.get_ref() if ref else null
func has(id: int) -> bool:
return get_scene(id) != null
# -------------------------
# 调用
# -------------------------
func call_scene(id: int, method: String, args: Array = []) -> Variant:
var n := get_scene(id)
if n == null:
push_warning("[SceneRegistry] call_scene(): id not found: %d" % id)
return null
if not n.has_method(method):
push_warning(
"[SceneRegistry] call_scene(): id=%d has no method '%s'"
% [id, method]
)
return null
return n.callv(method, args)
# -------------------------
# 列表
# -------------------------
func list_ids() -> PackedInt32Array:
for id in _by_id.keys():
_cleanup_dead(id)
var result := PackedInt32Array()
for id in _by_id.keys():
result.append(id)
return result
# -------------------------
# 内部:清理死引用
# -------------------------
func _cleanup_dead(id: int) -> void:
if not _by_id.has(id):
return return
var ref: WeakRef = _by_id[id] _scene_map.erase(scene_id)
if ref == null or ref.get_ref() == null: _scene_id_comp_map.erase(scene_id)
_by_id.erase(id)
func get_scene(scene_id: int) -> ReedScene:
return _scene_map.get(scene_id, null)

View File

@ -14,27 +14,6 @@ V0.3
- 实现了通过切换ActID来切换Act的功能 - 实现了通过切换ActID来切换Act的功能
## 更新计划 V1.0
V0.1 - Scene管理器全局ID功能
主要的工作是搭建一个插件的基本架构
V0.2
完善PropState的功能
V0.3
完善Act的功能
- Act管理器负责切换Act广播通知当前Scene下属的Prop进行状态切换。
- Act的快捷创建各种不同的Act。
V0.4
完善Scene的功能
- Scene具有全局唯一的ID
- SceneManager可以切换Act也可以切换PropState也可以通过ID获取到Prop
-

View File

@ -22,7 +22,7 @@ CameraSystem="*res://_shared/CameraSystem.tscn"
RoomSystem="*res://_shared/room_system.gd" RoomSystem="*res://_shared/room_system.gd"
GlobalEvent="*res://_shared/global_event.gd" GlobalEvent="*res://_shared/global_event.gd"
ReedVFX="*res://addons/reedfx/vfx/ReedVFXSystem.tscn" ReedVFX="*res://addons/reedfx/vfx/ReedVFXSystem.tscn"
ReedSceneSystem="*res://addons/reedscene/scene/SceneRegistry.gd" ReedSceneRegistry="*res://addons/reedscene/scene/SceneRegistry.gd"
[display] [display]
@ -95,3 +95,4 @@ grap_hook={
2d_physics/layer_2="Player" 2d_physics/layer_2="Player"
2d_physics/layer_3="Environment" 2d_physics/layer_3="Environment"
2d_physics/layer_4="Damage" 2d_physics/layer_4="Damage"
2d_physics/layer_5="Collectable"