diff --git a/DungeonShooting_Godot/prefab/ui/Settlement.tscn b/DungeonShooting_Godot/prefab/ui/Settlement.tscn
new file mode 100644
index 0000000..a67e040
--- /dev/null
+++ b/DungeonShooting_Godot/prefab/ui/Settlement.tscn
@@ -0,0 +1,63 @@
+[gd_scene load_steps=3 format=3 uid="uid://c8rugox2mbl3v"]
+
+[ext_resource type="Script" path="res://src/game/ui/settlement/SettlementPanel.cs" id="1_ayhkb"]
+[ext_resource type="Theme" uid="uid://drb1ajgvcih7p" path="res://resource/theme/theme1.tres" id="2_63mpy"]
+
+[node name="Settlement" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_ayhkb")
+
+[node name="Bg" type="ColorRect" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+color = Color(0, 0, 0, 0.27451)
+
+[node name="Title" type="Label" parent="."]
+layout_mode = 1
+anchors_preset = 10
+anchor_right = 1.0
+offset_top = 268.0
+offset_bottom = 311.0
+grow_horizontal = 2
+theme_override_font_sizes/font_size = 160
+text = "Game Over!"
+horizontal_alignment = 1
+vertical_alignment = 1
+
+[node name="ButtonList" type="VBoxContainer" parent="."]
+layout_mode = 1
+anchors_preset = 7
+anchor_left = 0.5
+anchor_top = 1.0
+anchor_right = 0.5
+anchor_bottom = 1.0
+offset_left = -96.0
+offset_top = -641.0
+offset_right = 96.0
+grow_horizontal = 2
+grow_vertical = 0
+alignment = 1
+
+[node name="Restart" type="Button" parent="ButtonList"]
+custom_minimum_size = Vector2(0, 50)
+layout_mode = 2
+focus_neighbor_top = NodePath("../ToMenu")
+theme = ExtResource("2_63mpy")
+text = "重新开始
+"
+
+[node name="ToMenu" type="Button" parent="ButtonList"]
+custom_minimum_size = Vector2(0, 50)
+layout_mode = 2
+focus_neighbor_bottom = NodePath("../Restart")
+theme = ExtResource("2_63mpy")
+text = "回到主菜单"
diff --git a/DungeonShooting_Godot/resource/theme/mainTheme.tres b/DungeonShooting_Godot/resource/theme/mainTheme.tres
index 66396a6..7a2c8ed 100644
--- a/DungeonShooting_Godot/resource/theme/mainTheme.tres
+++ b/DungeonShooting_Godot/resource/theme/mainTheme.tres
@@ -352,7 +352,7 @@
[sub_resource type="ImageTexture" id="58"]
-[sub_resource type="Image" id="Image_ej516"]
+[sub_resource type="Image" id="Image_blgn2"]
data = {
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 39, 255, 255, 255, 67, 255, 255, 255, 67, 255, 255, 255, 39, 255, 255, 255, 1, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 39, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 39, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 66, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 66, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 66, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 66, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 39, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 39, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 39, 255, 255, 255, 67, 255, 255, 255, 67, 255, 255, 255, 39, 255, 255, 255, 1, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
"format": "RGBA8",
@@ -362,7 +362,7 @@
}
[sub_resource type="ImageTexture" id="60"]
-image = SubResource("Image_ej516")
+image = SubResource("Image_blgn2")
[sub_resource type="StyleBoxTexture" id="61"]
content_margin_left = 2.0
@@ -372,7 +372,7 @@
texture = SubResource("60")
region_rect = Rect2(0, 0, 12, 12)
-[sub_resource type="Image" id="Image_irjg3"]
+[sub_resource type="Image" id="Image_k4r7l"]
data = {
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 191, 191, 0, 247, 247, 247, 0, 248, 248, 248, 0, 248, 248, 248, 0, 247, 247, 247, 0, 191, 191, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 191, 191, 0, 191, 191, 191, 4, 247, 247, 247, 98, 248, 248, 248, 167, 248, 248, 248, 167, 247, 247, 247, 98, 191, 191, 191, 4, 191, 191, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 247, 247, 0, 247, 247, 247, 97, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 247, 247, 247, 97, 247, 247, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 248, 248, 0, 248, 248, 248, 164, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 164, 248, 248, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 248, 248, 0, 248, 248, 248, 164, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 164, 248, 248, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 247, 247, 0, 247, 247, 247, 97, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 248, 248, 248, 186, 247, 247, 247, 97, 247, 247, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 191, 191, 0, 191, 191, 191, 4, 247, 247, 247, 98, 248, 248, 248, 167, 248, 248, 248, 167, 247, 247, 247, 98, 191, 191, 191, 4, 191, 191, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 191, 191, 0, 247, 247, 247, 0, 248, 248, 248, 0, 248, 248, 248, 0, 247, 247, 247, 0, 191, 191, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
"format": "RGBA8",
@@ -382,7 +382,7 @@
}
[sub_resource type="ImageTexture" id="63"]
-image = SubResource("Image_irjg3")
+image = SubResource("Image_k4r7l")
[sub_resource type="StyleBoxTexture" id="64"]
content_margin_left = 2.0
@@ -392,7 +392,7 @@
texture = SubResource("63")
region_rect = Rect2(0, 0, 12, 12)
-[sub_resource type="Image" id="Image_lj2yv"]
+[sub_resource type="Image" id="Image_cdubj"]
data = {
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 0, 173, 173, 173, 0, 173, 173, 173, 0, 173, 173, 173, 0, 173, 173, 173, 0, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 0, 127, 127, 127, 4, 173, 173, 173, 97, 173, 173, 173, 166, 173, 173, 173, 166, 173, 173, 173, 97, 127, 127, 127, 4, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 172, 172, 0, 172, 172, 172, 96, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 172, 172, 172, 96, 172, 172, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 173, 173, 0, 173, 173, 173, 163, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 163, 173, 173, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 173, 173, 0, 173, 173, 173, 163, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 163, 173, 173, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 172, 172, 0, 172, 172, 172, 96, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 173, 173, 173, 185, 172, 172, 172, 96, 172, 172, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 0, 127, 127, 127, 4, 173, 173, 173, 97, 173, 173, 173, 166, 173, 173, 173, 166, 173, 173, 173, 97, 127, 127, 127, 4, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 0, 173, 173, 173, 0, 173, 173, 173, 0, 173, 173, 173, 0, 173, 173, 173, 0, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
"format": "RGBA8",
@@ -402,7 +402,7 @@
}
[sub_resource type="ImageTexture" id="66"]
-image = SubResource("Image_lj2yv")
+image = SubResource("Image_cdubj")
[sub_resource type="StyleBoxTexture" id="67"]
content_margin_left = 2.0
@@ -412,7 +412,7 @@
texture = SubResource("66")
region_rect = Rect2(0, 0, 12, 12)
-[sub_resource type="Image" id="Image_1x5ku"]
+[sub_resource type="Image" id="Image_g01qr"]
data = {
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 4, 255, 255, 255, 16, 255, 255, 255, 16, 255, 255, 255, 4, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 16, 255, 255, 255, 21, 255, 255, 255, 21, 255, 255, 255, 16, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 16, 255, 255, 255, 21, 255, 255, 255, 21, 255, 255, 255, 16, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 4, 255, 255, 255, 16, 255, 255, 255, 16, 255, 255, 255, 4, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
"format": "RGBA8",
@@ -422,7 +422,7 @@
}
[sub_resource type="ImageTexture" id="69"]
-image = SubResource("Image_1x5ku")
+image = SubResource("Image_g01qr")
[sub_resource type="StyleBoxTexture" id="70"]
content_margin_left = 0.0
@@ -446,7 +446,7 @@
content_margin_right = 4.0
content_margin_bottom = 4.0
-[sub_resource type="Image" id="Image_caf60"]
+[sub_resource type="Image" id="Image_ptelj"]
data = {
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 76, 255, 255, 255, 17, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 17, 255, 255, 255, 76, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 76, 255, 255, 255, 228, 255, 255, 255, 188, 255, 255, 255, 17, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 17, 255, 255, 255, 188, 255, 255, 255, 228, 255, 255, 255, 76, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 18, 255, 255, 255, 188, 255, 255, 255, 229, 255, 255, 255, 187, 255, 255, 255, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 17, 255, 255, 255, 187, 255, 255, 255, 229, 255, 255, 255, 188, 255, 255, 255, 18, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 188, 255, 255, 255, 229, 255, 255, 255, 185, 255, 255, 255, 17, 255, 255, 255, 17, 255, 255, 255, 186, 255, 255, 255, 229, 255, 255, 255, 188, 255, 255, 255, 19, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 190, 255, 255, 255, 229, 255, 255, 255, 185, 255, 255, 255, 185, 255, 255, 255, 229, 255, 255, 255, 189, 255, 255, 255, 19, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 191, 255, 255, 255, 229, 255, 255, 255, 229, 255, 255, 255, 190, 255, 255, 255, 19, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 17, 255, 255, 255, 188, 255, 255, 255, 229, 255, 255, 255, 229, 255, 255, 255, 188, 255, 255, 255, 17, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 17, 255, 255, 255, 188, 255, 255, 255, 229, 255, 255, 255, 188, 255, 255, 255, 188, 255, 255, 255, 229, 255, 255, 255, 187, 255, 255, 255, 17, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 17, 255, 255, 255, 187, 255, 255, 255, 229, 255, 255, 255, 188, 255, 255, 255, 18, 255, 255, 255, 19, 255, 255, 255, 188, 255, 255, 255, 229, 255, 255, 255, 186, 255, 255, 255, 17, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 17, 255, 255, 255, 185, 255, 255, 255, 229, 255, 255, 255, 189, 255, 255, 255, 19, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 189, 255, 255, 255, 229, 255, 255, 255, 185, 255, 255, 255, 17, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 76, 255, 255, 255, 229, 255, 255, 255, 190, 255, 255, 255, 19, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 190, 255, 255, 255, 229, 255, 255, 255, 76, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 77, 255, 255, 255, 19, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 77, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
"format": "RGBA8",
@@ -456,7 +456,7 @@
}
[sub_resource type="ImageTexture" id="56"]
-image = SubResource("Image_caf60")
+image = SubResource("Image_ptelj")
[sub_resource type="StyleBoxFlat" id="57"]
content_margin_left = 6.0
diff --git a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs
index 7e73d3c..e64f8fd 100644
--- a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs
+++ b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs
@@ -173,6 +173,11 @@
///
public bool EnableCustomBehavior { get; set; } = true;
+ ///
+ /// 所在的 World 对象
+ ///
+ public World World { get; private set; }
+
// --------------------------------------------------------------------------------
//组件集合
@@ -234,8 +239,9 @@
private static long _instanceIndex = 0;
//初始化节点
- private void _InitNode(string itemId, string scenePath)
+ private void _InitNode(string itemId, string scenePath, World world)
{
+ World = world;
//加载预制体
var tempPrefab = ResourceManager.Load(scenePath);
if (tempPrefab == null)
diff --git a/DungeonShooting_Godot/src/framework/activity/ActivityObject_Register.cs b/DungeonShooting_Godot/src/framework/activity/ActivityObject_Register.cs
index 01e9f66..85f2521 100644
--- a/DungeonShooting_Godot/src/framework/activity/ActivityObject_Register.cs
+++ b/DungeonShooting_Godot/src/framework/activity/ActivityObject_Register.cs
@@ -83,10 +83,15 @@
///
public static ActivityObject Create(string itemId)
{
+ var world = GameApplication.Instance.World;
+ if (world == null)
+ {
+ throw new Exception("实例化 ActivityObject 前请先调用 'GameApplication.Instance.CreateNewWorld()' 初始化 World 对象");
+ }
if (_activityRegisterMap.TryGetValue(itemId, out var item))
{
var instance = item.CallBack();
- instance._InitNode(item.RegisterActivity.ItemId, item.RegisterActivity.PrefabPath);
+ instance._InitNode(item.RegisterActivity.ItemId, item.RegisterActivity.PrefabPath, world);
item.RegisterActivity.CustomHandler(instance);
return instance;
}
diff --git a/DungeonShooting_Godot/src/framework/generator/UiManagerMethodsGenerator.cs b/DungeonShooting_Godot/src/framework/generator/UiManagerMethodsGenerator.cs
index 7634384..df51675 100644
--- a/DungeonShooting_Godot/src/framework/generator/UiManagerMethodsGenerator.cs
+++ b/DungeonShooting_Godot/src/framework/generator/UiManagerMethodsGenerator.cs
@@ -54,6 +54,30 @@
$" }}\n" +
$"\n" +
$" /// \n" +
+ $" /// 隐藏 {uiName} 的所有实例\n" +
+ $" /// \n" +
+ $" public static void Hide_{uiName}()\n" +
+ $" {{\n" +
+ $" var uiInstance = Get_{uiName}_Instance();\n" +
+ $" foreach (var uiPanel in uiInstance)\n" +
+ $" {{\n" +
+ $" uiPanel.HideUi();\n" +
+ $" }}\n" +
+ $" }}\n" +
+ $"\n" +
+ $" /// \n" +
+ $" /// 销毁 {uiName} 的所有实例\n" +
+ $" /// \n" +
+ $" public static void Dispose_{uiName}()\n" +
+ $" {{\n" +
+ $" var uiInstance = Get_{uiName}_Instance();\n" +
+ $" foreach (var uiPanel in uiInstance)\n" +
+ $" {{\n" +
+ $" uiPanel.DisposeUi();\n" +
+ $" }}\n" +
+ $" }}\n" +
+ $"\n" +
+ $" /// \n" +
$" /// 获取所有 {uiName} 的实例, 如果没有实例, 则返回一个空数组\n" +
$" /// \n" +
$" public static UI.{uiName}.{uiName}Panel[] Get_{uiName}_Instance()\n" +
diff --git a/DungeonShooting_Godot/src/framework/ui/UiManager.cs b/DungeonShooting_Godot/src/framework/ui/UiManager.cs
index ee32c42..a9b5a61 100644
--- a/DungeonShooting_Godot/src/framework/ui/UiManager.cs
+++ b/DungeonShooting_Godot/src/framework/ui/UiManager.cs
@@ -139,6 +139,54 @@
}
///
+ ///
+ ///
+ public static void HideUi(UiBase uiBase)
+ {
+ uiBase.HideUi();
+ }
+
+ ///
+ /// 销毁所有Ui
+ ///
+ public static void DisposeAllUi()
+ {
+ var map = new Dictionary>();
+ foreach (var item in _recordUiMap)
+ {
+ map.Add(item.Key, new List(item.Value));
+ }
+
+ foreach (var item in map)
+ {
+ foreach (var uiBase in item.Value)
+ {
+ uiBase.DisposeUi();
+ }
+ }
+ }
+
+ ///
+ /// 隐藏所有Ui
+ ///
+ public static void HideAllUi()
+ {
+ var map = new Dictionary>();
+ foreach (var item in _recordUiMap)
+ {
+ map.Add(item.Key, new List(item.Value));
+ }
+
+ foreach (var item in map)
+ {
+ foreach (var uiBase in item.Value)
+ {
+ uiBase.HideUi();
+ }
+ }
+ }
+
+ ///
/// 获取Ui实例
///
public static T[] GetUiInstance(string uiName) where T : UiBase
diff --git a/DungeonShooting_Godot/src/game/Cursor.cs b/DungeonShooting_Godot/src/game/Cursor.cs
index 12d0c5d..3d382cf 100644
--- a/DungeonShooting_Godot/src/game/Cursor.cs
+++ b/DungeonShooting_Godot/src/game/Cursor.cs
@@ -5,36 +5,6 @@
///
public partial class Cursor : Node2D
{
- public enum CursorStyle
- {
- ///
- /// 手指
- ///
- Finger,
- ///
- /// 准心模式
- ///
- Sight,
- }
-
- ///
- /// 准心样式
- ///
- public CursorStyle Style
- {
- get => _style;
- set
- {
- if (_style != value)
- {
- _style = value;
- SetStyle(value);
- }
- }
- }
-
- private CursorStyle _style;
-
///
/// 是否是GUI模式
///
@@ -60,28 +30,21 @@
rt = GetNode("RT");
rb = GetNode("RB");
finger = GetNode("Finger");
- SetStyle(CursorStyle.Finger);
+ SetGuiMode(true);
}
public override void _Process(double delta)
{
- if (_style == CursorStyle.Sight)
+ if (!_isGuiMode)
{
- if (_isGuiMode)
+ var targetGun = _mountRole?.Holster.ActiveWeapon;
+ if (targetGun != null)
{
- SetScope(0, null);
+ SetScope(targetGun.CurrScatteringRange, targetGun);
}
else
{
- var targetGun = _mountRole?.Holster.ActiveWeapon;
- if (targetGun != null)
- {
- SetScope(targetGun.CurrScatteringRange, targetGun);
- }
- else
- {
- SetScope(0, null);
- }
+ SetScope(0, null);
}
}
@@ -94,6 +57,22 @@
public void SetGuiMode(bool flag)
{
_isGuiMode = flag;
+ if (flag) //手指
+ {
+ lt.Visible = false;
+ lb.Visible = false;
+ rt.Visible = false;
+ rb.Visible = false;
+ finger.Visible = true;
+ }
+ else //准心
+ {
+ lt.Visible = true;
+ lb.Visible = true;
+ rt.Visible = true;
+ rb.Visible = true;
+ finger.Visible = false;
+ }
}
///
@@ -120,26 +99,6 @@
return _mountRole;
}
- private void SetStyle(CursorStyle style)
- {
- if (style == CursorStyle.Finger) //手指
- {
- lt.Visible = false;
- lb.Visible = false;
- rt.Visible = false;
- rb.Visible = false;
- finger.Visible = true;
- }
- else if (style == CursorStyle.Sight) //准心
- {
- lt.Visible = true;
- lb.Visible = true;
- rt.Visible = true;
- rb.Visible = true;
- finger.Visible = false;
- }
- }
-
///
/// 设置光标半径范围
///
diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs
index 1112ec3..c340fe1 100644
--- a/DungeonShooting_Godot/src/game/GameApplication.cs
+++ b/DungeonShooting_Godot/src/game/GameApplication.cs
@@ -110,13 +110,10 @@
UiManager.Init();
// 初始化鼠标
InitCursor();
- //加载世界场景
- World = ResourceManager.LoadAndInstantiate(ResourcePath.scene_World_tscn);
- SceneRoot.AddChild(World);
//地牢管理器
DungeonManager = new DungeonManager();
DungeonManager.Name = "DungeonManager";
- World.AddChild(DungeonManager);
+ SceneRoot.AddChild(DungeonManager);
//打开主菜单Ui
UiManager.Open_Main();
}
@@ -134,6 +131,33 @@
}
///
+ /// 创建新的 World 对象, 相当于清理房间
+ ///
+ public World CreateNewWorld()
+ {
+ if (World != null)
+ {
+ World.QueueFree();
+ }
+ World = ResourceManager.LoadAndInstantiate(ResourcePath.scene_World_tscn);
+ SceneRoot.AddChild(World);
+ return World;
+ }
+
+ ///
+ /// 销毁 World 对象, 相当于清理房间
+ ///
+ public void DestroyWorld()
+ {
+ if (World != null)
+ {
+ World.QueueFree();
+ }
+
+ World = null;
+ }
+
+ ///
/// 将 viewport 以外的全局坐标 转换成 viewport 内的全局坐标
///
public Vector2 GlobalToViewPosition(Vector2 globalPos)
diff --git a/DungeonShooting_Godot/src/game/camera/GameCamera.cs b/DungeonShooting_Godot/src/game/camera/GameCamera.cs
index 993097e..19efec9 100644
--- a/DungeonShooting_Godot/src/game/camera/GameCamera.cs
+++ b/DungeonShooting_Godot/src/game/camera/GameCamera.cs
@@ -70,7 +70,9 @@
// (viewportContainer.Material as ShaderMaterial)?.SetShaderParameter("offset", SubPixelPosition);
// GlobalPosition = _camPos.Round();
- if (_followTarget != null)
+
+ var world = GameApplication.Instance.World;
+ if (world != null && !world.Pause && _followTarget != null)
{
var mousePosition = InputManager.GetViewportMousePosition();
var targetPosition = _followTarget.GlobalPosition;
@@ -104,7 +106,11 @@
public void SetFollowTarget(Role target)
{
_followTarget = target;
- GlobalPosition = target.GlobalPosition;
+ if (target != null)
+ {
+ _camPos = target.GlobalPosition;
+ GlobalPosition = _camPos;
+ }
}
///
diff --git a/DungeonShooting_Godot/src/game/event/EventEnum.cs b/DungeonShooting_Godot/src/game/event/EventEnum.cs
index 4d6e2ff..9432065 100644
--- a/DungeonShooting_Godot/src/game/event/EventEnum.cs
+++ b/DungeonShooting_Godot/src/game/event/EventEnum.cs
@@ -40,4 +40,12 @@
/// 刷新玩家手持武器纹理, 参数类型为
///
OnPlayerRefreshWeaponTexture,
+ ///
+ /// 当玩家进入地牢时调用, 没有参数
+ ///
+ OnEnterDungeon,
+ ///
+ /// 当玩家退出地牢时调用, 没有参数
+ ///
+ OnExitDungeon,
}
diff --git a/DungeonShooting_Godot/src/game/event/EventManager.cs b/DungeonShooting_Godot/src/game/event/EventManager.cs
index 665fe38..394db2a 100644
--- a/DungeonShooting_Godot/src/game/event/EventManager.cs
+++ b/DungeonShooting_Godot/src/game/event/EventManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
+using Godot;
///
/// 事件管理器
@@ -57,7 +58,14 @@
var binder = binders[i];
if (!binder.IsDiscard)
{
- binder.Callback(arg);
+ try
+ {
+ binder.Callback(arg);
+ }
+ catch (Exception e)
+ {
+ GD.PrintErr($"EventManager 派发事件: '{eventType}' 发生异常: \n" + e.Message + "\n" + e.StackTrace);
+ }
}
}
}
diff --git a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
index be603f9..2d28691 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
@@ -8,11 +8,6 @@
public abstract partial class Weapon : ActivityObject
{
///
- /// 所有被扔在地上的武器
- ///
- public static readonly HashSet UnclaimedWeapons = new HashSet();
-
- ///
/// 开火回调事件
///
public event Action FireEvent;
@@ -288,7 +283,7 @@
//收集落在地上的武器
if (IsInGround())
{
- UnclaimedWeapons.Add(this);
+ World.Weapon_UnclaimedWeapons.Add(this);
}
}
@@ -296,7 +291,7 @@
{
base._ExitTree();
- UnclaimedWeapons.Remove(this);
+ World.Weapon_UnclaimedWeapons.Remove(this);
}
protected override void Process(float delta)
diff --git a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
index 0f65d6c..0c33cc3 100644
--- a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
+++ b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
@@ -22,6 +22,7 @@
public const string prefab_ui_EditorTools_tscn = "res://prefab/ui/EditorTools.tscn";
public const string prefab_ui_Main_tscn = "res://prefab/ui/Main.tscn";
public const string prefab_ui_RoomUI_tscn = "res://prefab/ui/RoomUI.tscn";
+ public const string prefab_ui_Settlement_tscn = "res://prefab/ui/Settlement.tscn";
public const string prefab_weapon_Knife_tscn = "res://prefab/weapon/Knife.tscn";
public const string prefab_weapon_Weapon_tscn = "res://prefab/weapon/Weapon.tscn";
public const string prefab_weapon_bullet_Bullet_tscn = "res://prefab/weapon/bullet/Bullet.tscn";
@@ -253,8 +254,8 @@
public const string resource_sprite_role_role8_png = "res://resource/sprite/role/role8.png";
public const string resource_sprite_role_role9_png = "res://resource/sprite/role/role9.png";
public const string resource_sprite_shell_shellCase_png = "res://resource/sprite/shell/shellCase.png";
- public const string resource_sprite_ui_Cursor_png = "res://resource/sprite/ui/Cursor.png";
public const string resource_sprite_ui_CursorCenter_png = "res://resource/sprite/ui/CursorCenter.png";
+ public const string resource_sprite_ui_cursors_png = "res://resource/sprite/ui/cursors.png";
public const string resource_sprite_ui_font_bg_png = "res://resource/sprite/ui/font_bg.png";
public const string resource_sprite_ui_GUI_png = "res://resource/sprite/ui/GUI.png";
public const string resource_sprite_ui_healthBar_png = "res://resource/sprite/ui/healthBar.png";
diff --git a/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs b/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs
index 7a6b33c..3feb3d4 100644
--- a/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs
+++ b/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs
@@ -9,6 +9,7 @@
public const string EditorTools = "EditorTools";
public const string Main = "Main";
public const string RoomUI = "RoomUI";
+ public const string Settlement = "Settlement";
}
///
@@ -20,6 +21,30 @@
}
///
+ /// 隐藏 EditorTools 的所有实例
+ ///
+ public static void Hide_EditorTools()
+ {
+ var uiInstance = Get_EditorTools_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.HideUi();
+ }
+ }
+
+ ///
+ /// 销毁 EditorTools 的所有实例
+ ///
+ public static void Dispose_EditorTools()
+ {
+ var uiInstance = Get_EditorTools_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.DisposeUi();
+ }
+ }
+
+ ///
/// 获取所有 EditorTools 的实例, 如果没有实例, 则返回一个空数组
///
public static UI.EditorTools.EditorToolsPanel[] Get_EditorTools_Instance()
@@ -36,6 +61,30 @@
}
///
+ /// 隐藏 Main 的所有实例
+ ///
+ public static void Hide_Main()
+ {
+ var uiInstance = Get_Main_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.HideUi();
+ }
+ }
+
+ ///
+ /// 销毁 Main 的所有实例
+ ///
+ public static void Dispose_Main()
+ {
+ var uiInstance = Get_Main_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.DisposeUi();
+ }
+ }
+
+ ///
/// 获取所有 Main 的实例, 如果没有实例, 则返回一个空数组
///
public static UI.Main.MainPanel[] Get_Main_Instance()
@@ -52,6 +101,30 @@
}
///
+ /// 隐藏 RoomUI 的所有实例
+ ///
+ public static void Hide_RoomUI()
+ {
+ var uiInstance = Get_RoomUI_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.HideUi();
+ }
+ }
+
+ ///
+ /// 销毁 RoomUI 的所有实例
+ ///
+ public static void Dispose_RoomUI()
+ {
+ var uiInstance = Get_RoomUI_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.DisposeUi();
+ }
+ }
+
+ ///
/// 获取所有 RoomUI 的实例, 如果没有实例, 则返回一个空数组
///
public static UI.RoomUI.RoomUIPanel[] Get_RoomUI_Instance()
@@ -59,4 +132,44 @@
return GetUiInstance(nameof(UI.RoomUI.RoomUI));
}
+ ///
+ /// 打开 Settlement, 并返回UI实例
+ ///
+ public static UI.Settlement.SettlementPanel Open_Settlement()
+ {
+ return OpenUi(UiName.Settlement);
+ }
+
+ ///
+ /// 隐藏 Settlement 的所有实例
+ ///
+ public static void Hide_Settlement()
+ {
+ var uiInstance = Get_Settlement_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.HideUi();
+ }
+ }
+
+ ///
+ /// 销毁 Settlement 的所有实例
+ ///
+ public static void Dispose_Settlement()
+ {
+ var uiInstance = Get_Settlement_Instance();
+ foreach (var uiPanel in uiInstance)
+ {
+ uiPanel.DisposeUi();
+ }
+ }
+
+ ///
+ /// 获取所有 Settlement 的实例, 如果没有实例, 则返回一个空数组
+ ///
+ public static UI.Settlement.SettlementPanel[] Get_Settlement_Instance()
+ {
+ return GetUiInstance(nameof(UI.Settlement.Settlement));
+ }
+
}
diff --git a/DungeonShooting_Godot/src/game/role/Player.cs b/DungeonShooting_Godot/src/game/role/Player.cs
index 6687cca..8daf864 100644
--- a/DungeonShooting_Godot/src/game/role/Player.cs
+++ b/DungeonShooting_Godot/src/game/role/Player.cs
@@ -28,6 +28,9 @@
public static void SetCurrentPlayer(Player player)
{
Current = player;
+ //设置相机和鼠标跟随玩家
+ GameCamera.Main.SetFollowTarget(player);
+ GameApplication.Instance.Cursor.SetMountRole(player);
}
public override void OnInit()
@@ -62,6 +65,10 @@
protected override void Process(float delta)
{
+ if (IsDie)
+ {
+ return;
+ }
base.Process(delta);
//脸的朝向
if (LookTarget == null)
@@ -104,10 +111,20 @@
{
Attack();
}
+
+ if (Input.IsKeyPressed(Key.P))
+ {
+ Hurt(1000, 0);
+ }
}
protected override void PhysicsProcess(float delta)
{
+ if (IsDie)
+ {
+ return;
+ }
+
base.PhysicsProcess(delta);
HandleMoveInput(delta);
//播放动画
@@ -178,6 +195,13 @@
EventManager.EmitEvent(EventEnum.OnPlayerMaxShieldChange, maxShield);
}
+ protected override void OnDie()
+ {
+ GameCamera.Main.SetFollowTarget(null);
+ UiManager.Open_Settlement();
+ //GameApplication.Instance.World.ProcessMode = ProcessModeEnum.WhenPaused;
+ }
+
///
/// 刷新 ui 上手持的物体
///
diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
index 57e8bd8..ca1a00a 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
@@ -21,26 +21,6 @@
public partial class Enemy : Role
{
///
- /// 公共属性, 是否找到目标, 如果找到目标, 则与目标同房间的所有敌人都会知道目标的位置
- ///
- public static bool IsFindTarget { get; private set; }
-
- ///
- /// 公共属性, 在哪个区域找到的目标, 所有该区域下的敌人都会知道目标的位置
- ///
- public static HashSet FindTargetAffiliationSet { get; } = new HashSet();
-
- ///
- /// 公共属性, 找到的目标的位置, 如果目标在视野内, 则一直更新
- ///
- public static Vector2 FindTargetPosition { get; private set; }
-
- ///
- /// 记录所有存活的敌人
- ///
- private static readonly List _enemieList = new List();
-
- ///
/// 敌人身上的状态机控制器
///
public StateController StateController { get; private set; }
@@ -116,16 +96,16 @@
public override void _EnterTree()
{
- if (!_enemieList.Contains(this))
+ if (!World.Enemy_InstanceList.Contains(this))
{
- _enemieList.Add(this);
+ World.Enemy_InstanceList.Add(this);
}
}
public override void _ExitTree()
{
base._ExitTree();
- _enemieList.Remove(this);
+ World.Enemy_InstanceList.Remove(this);
}
protected override void OnDie()
@@ -191,7 +171,7 @@
///
public bool CheckUsableWeaponInUnclaimed()
{
- foreach (var unclaimedWeapon in Weapon.UnclaimedWeapons)
+ foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons)
{
//判断是否能拾起武器, 条件: 相同的房间
if (unclaimedWeapon.Affiliation == Affiliation)
@@ -231,7 +211,7 @@
{
Weapon target = null;
var position = Position;
- foreach (var weapon in Weapon.UnclaimedWeapons)
+ foreach (var weapon in World.Weapon_UnclaimedWeapons)
{
//判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间
if (weapon.Affiliation == Affiliation)
@@ -283,7 +263,7 @@
///
public bool CanChangeLeaveFor()
{
- if (!IsFindTarget)
+ if (!World.Enemy_IsFindTarget)
{
return false;
}
@@ -292,35 +272,11 @@
if (currState == AiStateEnum.AiNormal || currState == AiStateEnum.AiProbe)
{
//判断是否在同一个房间内
- return FindTargetAffiliationSet.Contains(Affiliation);
+ return World.Enemy_FindTargetAffiliationSet.Contains(Affiliation);
}
return false;
}
-
- ///
- /// 更新敌人视野
- ///
- public static void UpdateEnemiesView()
- {
- IsFindTarget = false;
- FindTargetAffiliationSet.Clear();
- for (var i = 0; i < _enemieList.Count; i++)
- {
- var enemy = _enemieList[i];
- var state = enemy.StateController.CurrState;
- if (state == AiStateEnum.AiFollowUp || state == AiStateEnum.AiSurround) //目标在视野内
- {
- if (!IsFindTarget)
- {
- IsFindTarget = true;
- FindTargetPosition = Player.Current.GetCenterPosition();
- FindTargetAffiliationSet.Add(Player.Current.Affiliation);
- }
- FindTargetAffiliationSet.Add(enemy.Affiliation);
- }
- }
- }
///
/// Ai触发的攻击
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs
index 6d01f73..96e90b5 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs
@@ -16,9 +16,9 @@
public override void Enter(AiStateEnum prev, params object[] args)
{
- if (Enemy.IsFindTarget)
+ if (Master.World.Enemy_IsFindTarget)
{
- Master.NavigationAgent2D.TargetPosition = Enemy.FindTargetPosition;
+ Master.NavigationAgent2D.TargetPosition = Master.World.EnemyFindTargetPosition;
}
else
{
@@ -47,7 +47,7 @@
{
//每隔一段时间秒更改目标位置
_navigationUpdateTimer = _navigationInterval;
- Master.NavigationAgent2D.TargetPosition = Enemy.FindTargetPosition;
+ Master.NavigationAgent2D.TargetPosition = Master.World.EnemyFindTargetPosition;
}
else
{
@@ -58,7 +58,7 @@
{
//计算移动
var nextPos = Master.NavigationAgent2D.GetNextPathPosition();
- Master.LookTargetPosition(Enemy.FindTargetPosition);
+ Master.LookTargetPosition(Master.World.EnemyFindTargetPosition);
Master.AnimatedSprite.Play(AnimatorNames.Run);
Master.BasisVelocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() *
Master.MoveSpeed;
diff --git a/DungeonShooting_Godot/src/game/room/DungeonManager.cs b/DungeonShooting_Godot/src/game/room/DungeonManager.cs
index 582634f..d5f45e0 100644
--- a/DungeonShooting_Godot/src/game/room/DungeonManager.cs
+++ b/DungeonShooting_Godot/src/game/room/DungeonManager.cs
@@ -25,43 +25,53 @@
public AffiliationArea ActiveAffiliation => Player.Current?.Affiliation;
///
- /// 地图根节点
+ /// 是否在地牢里
///
- public TileMap TileRoot => GameApplication.Instance.World.TileRoot;
+ public bool IsInDungeon { get; private set; }
private DungeonTile _dungeonTile;
private AutoTileConfig _autoTileConfig;
private DungeonGenerator _dungeonGenerator;
+ //房间内所有静态导航网格数据
+ private List _roomStaticNavigationList;
+ private World _world;
+
+ //用于检查房间敌人的计时器
private int _affiliationIndex = 0;
private float _checkEnemyTimer = 0;
-
- //房间内所有静态导航网格数据
- private static List _roomStaticNavigationList = new List();
+
+
+ public DungeonManager()
+ {
+ //绑定事件
+ EventManager.AddEventListener(EventEnum.OnPlayerFirstEnterRoom, OnPlayerFirstEnterRoom);
+ EventManager.AddEventListener(EventEnum.OnPlayerEnterRoom, OnPlayerEnterRoom);
+ }
///
/// 加载地牢
///
public void LoadDungeon(DungeonConfig config)
{
- //绑定事件
- EventManager.AddEventListener(EventEnum.OnPlayerFirstEnterRoom, OnPlayerFirstEnterRoom);
- EventManager.AddEventListener(EventEnum.OnPlayerEnterRoom, OnPlayerEnterRoom);
-
+ IsInDungeon = true;
var nowTicks = DateTime.Now.Ticks;
+ //创建世界场景
+ _world = GameApplication.Instance.CreateNewWorld();
//生成地牢房间
_dungeonGenerator = new DungeonGenerator(config);
_dungeonGenerator.Generate();
//填充地牢
_autoTileConfig = new AutoTileConfig();
- _dungeonTile = new DungeonTile(TileRoot);
+ _dungeonTile = new DungeonTile(_world.TileRoot);
_dungeonTile.AutoFillRoomTile(_autoTileConfig, _dungeonGenerator.StartRoom);
//生成寻路网格, 这一步操作只生成过道的导航
_dungeonTile.GenerateNavigationPolygon(GameConfig.AisleFloorMapLayer);
//挂载过道导航区域
- _dungeonTile.MountNavigationPolygon(this);
+ _dungeonTile.MountNavigationPolygon(_world.TileRoot);
//过道导航区域数据
+ _roomStaticNavigationList = new List();
_roomStaticNavigationList.AddRange(_dungeonTile.GetPolygonData());
//门导航区域数据
_roomStaticNavigationList.AddRange(_dungeonTile.GetConnectDoorPolygonData());
@@ -77,45 +87,70 @@
var playerBirthMark = StartRoom.ActivityMarks.FirstOrDefault(mark => mark.Type == ActivityIdPrefix.ActivityPrefixType.Player);
//创建玩家
var player = ActivityObject.Create(ActivityIdPrefix.Role + "0001");
- Player.SetCurrentPlayer(player);
if (playerBirthMark != null)
{
player.Position = playerBirthMark.Position;
}
player.Name = "Player";
+ Player.SetCurrentPlayer(player);
player.PutDown(RoomLayerEnum.YSortLayer);
player.PickUpWeapon(ActivityObject.Create(ActivityIdPrefix.Weapon + "0001"));
-
- //相机跟随玩家
- GameCamera.Main.SetFollowTarget(player);
- //鼠标指针挂载到玩家身上
- var cursor = GameApplication.Instance.Cursor;
- cursor.Style = Cursor.CursorStyle.Sight;
- cursor.SetGuiMode(false);
- cursor.SetMountRole(player);
-
+ GameApplication.Instance.Cursor.SetGuiMode(false);
//打开游戏中的ui
- UiManager.Open_RoomUI();
+ var roomUi = UiManager.Open_RoomUI();
+ roomUi.InitData(player);
+ //派发进入地牢事件
+ EventManager.EmitEvent(EventEnum.OnEnterDungeon);
}
-
+
+ ///
+ /// 退出地牢
+ ///
+ public void ExitDungeon()
+ {
+ IsInDungeon = false;
+
+ _dungeonGenerator.EachRoom(DisposeRoomInfo);
+ _dungeonTile = null;
+ _autoTileConfig = null;
+ _dungeonGenerator = null;
+ _roomStaticNavigationList.Clear();
+ _roomStaticNavigationList = null;
+
+ UiManager.Hide_RoomUI();
+ Player.SetCurrentPlayer(null);
+ _world = null;
+ GameApplication.Instance.DestroyWorld();
+ //鼠标还原
+ GameApplication.Instance.Cursor.SetGuiMode(false);
+ //派发退出地牢事件
+ EventManager.EmitEvent(EventEnum.OnExitDungeon);
+ }
+
public override void _PhysicsProcess(double delta)
{
- _checkEnemyTimer += (float)delta;
- if (_checkEnemyTimer >= 1)
+ if (IsInDungeon)
{
- _checkEnemyTimer %= 1;
- //检查房间内的敌人存活状况
- OnCheckEnemy();
+ _checkEnemyTimer += (float)delta;
+ if (_checkEnemyTimer >= 1)
+ {
+ _checkEnemyTimer %= 1;
+ //检查房间内的敌人存活状况
+ OnCheckEnemy();
+ }
}
}
public override void _Process(double delta)
{
- Enemy.UpdateEnemiesView();
- if (GameApplication.Instance.Debug)
+ if (IsInDungeon)
{
- QueueRedraw();
+ UpdateEnemiesView();
+ if (GameApplication.Instance.Debug)
+ {
+ QueueRedraw();
+ }
}
}
@@ -160,7 +195,7 @@
var navigationPolygon = new NavigationRegion2D();
navigationPolygon.Name = "NavigationRegion" + (GetChildCount() + 1);
navigationPolygon.NavigationPolygon = polygon;
- AddChild(navigationPolygon);
+ _world.TileRoot.AddChild(navigationPolygon);
}
//创建门
@@ -204,7 +239,7 @@
(roomInfo.Size - new Vector2I(2, 2)) * GameConfig.TileCellSize));
roomInfo.Affiliation = affiliation;
- TileRoot.AddChild(affiliation);
+ _world.AddChild(affiliation);
}
///
@@ -246,6 +281,39 @@
}
}
+ ///
+ /// 更新敌人视野
+ ///
+ private void UpdateEnemiesView()
+ {
+ _world.Enemy_IsFindTarget = false;
+ _world.Enemy_FindTargetAffiliationSet.Clear();
+ for (var i = 0; i < _world.Enemy_InstanceList.Count; i++)
+ {
+ var enemy = _world.Enemy_InstanceList[i];
+ var state = enemy.StateController.CurrState;
+ if (state == AiStateEnum.AiFollowUp || state == AiStateEnum.AiSurround) //目标在视野内
+ {
+ if (!_world.Enemy_IsFindTarget)
+ {
+ _world.Enemy_IsFindTarget = true;
+ _world.EnemyFindTargetPosition = Player.Current.GetCenterPosition();
+ _world.Enemy_FindTargetAffiliationSet.Add(Player.Current.Affiliation);
+ }
+ _world.Enemy_FindTargetAffiliationSet.Add(enemy.Affiliation);
+ }
+ }
+ }
+
+ private void DisposeRoomInfo(RoomInfo roomInfo)
+ {
+ foreach (var activityMark in roomInfo.ActivityMarks)
+ {
+ activityMark.QueueFree();
+ }
+ roomInfo.ActivityMarks.Clear();
+ }
+
public override void _Draw()
{
if (GameApplication.Instance.Debug)
@@ -263,7 +331,7 @@
//绘制房间区域, debug 用
private void DrawRoomInfo(RoomInfo room)
{
- var cellSize = TileRoot.CellQuadrantSize;
+ var cellSize = _world.TileRoot.CellQuadrantSize;
var pos1 = (room.Position + room.Size / 2) * cellSize;
//绘制下一个房间
diff --git a/DungeonShooting_Godot/src/game/room/World.cs b/DungeonShooting_Godot/src/game/room/World.cs
index 72145db..74f7c4d 100644
--- a/DungeonShooting_Godot/src/game/room/World.cs
+++ b/DungeonShooting_Godot/src/game/room/World.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using Godot;
///
@@ -19,7 +20,57 @@
/// 地图根节点
///
[Export] public TileMap TileRoot;
+
+ ///
+ /// 是否暂停
+ ///
+ public bool Pause
+ {
+ get => _pause;
+ set
+ {
+ if (_pause != value)
+ {
+ _pause = value;
+ if (value)
+ {
+ ProcessMode = ProcessModeEnum.Pausable;
+ }
+ else
+ {
+ ProcessMode = ProcessModeEnum.Inherit;
+ }
+ }
+ }
+ }
+
+ ///
+ /// 所有被扔在地上的武器
+ ///
+ public HashSet Weapon_UnclaimedWeapons { get; } = new HashSet();
+
+ ///
+ /// 记录所有存活的敌人
+ ///
+ public List Enemy_InstanceList { get; } = new List();
+
+ ///
+ /// 公共属性, 敌人是否找到目标, 如果找到目标, 则与目标同房间的所有敌人都会知道目标的位置
+ ///
+ public bool Enemy_IsFindTarget { get; set; }
+ ///
+ /// 公共属性, 敌人在哪个区域找到的目标, 所有该区域下的敌人都会知道目标的位置
+ ///
+ public HashSet Enemy_FindTargetAffiliationSet { get; } = new HashSet();
+
+ ///
+ /// 公共属性, 敌人找到的目标的位置, 如果目标在视野内, 则一直更新
+ ///
+ public Vector2 EnemyFindTargetPosition { get; set; }
+
+ private bool _pause = false;
+
public override void _Ready()
{
TileRoot.YSortEnabled = false;
@@ -40,5 +91,5 @@
return null;
}
-
+
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
index cbf1e66..795c3e7 100644
--- a/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
+++ b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
@@ -38,6 +38,14 @@
_gunBar.OnHide();
}
+ public void InitData(Player player)
+ {
+ _healthBar.SetMaxHp(player.MaxHp);
+ _healthBar.SetHp(player.Hp);
+ _healthBar.SetMaxShield(player.MaxShield);
+ _healthBar.SetShield(player.Shield);
+ }
+
public override void Process(float delta)
{
_gunBar.Process(delta);
diff --git a/DungeonShooting_Godot/src/game/ui/settlement/Settlement.cs b/DungeonShooting_Godot/src/game/ui/settlement/Settlement.cs
new file mode 100644
index 0000000..e61f5ca
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/ui/settlement/Settlement.cs
@@ -0,0 +1,123 @@
+namespace UI.Settlement;
+
+///
+/// Ui代码, 该类是根据ui场景自动生成的, 请不要手动编辑该类, 以免造成代码丢失
+///
+public abstract partial class Settlement : UiBase
+{
+ ///
+ /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: Settlement.Bg
+ ///
+ public UiNode_Bg L_Bg
+ {
+ get
+ {
+ if (_L_Bg == null) _L_Bg = new UiNode_Bg(GetNodeOrNull("Bg"));
+ return _L_Bg;
+ }
+ }
+ private UiNode_Bg _L_Bg;
+
+ ///
+ /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: Settlement.Title
+ ///
+ public UiNode_Title L_Title
+ {
+ get
+ {
+ if (_L_Title == null) _L_Title = new UiNode_Title(GetNodeOrNull("Title"));
+ return _L_Title;
+ }
+ }
+ private UiNode_Title _L_Title;
+
+ ///
+ /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: Settlement.ButtonList
+ ///
+ public UiNode_ButtonList L_ButtonList
+ {
+ get
+ {
+ if (_L_ButtonList == null) _L_ButtonList = new UiNode_ButtonList(GetNodeOrNull("ButtonList"));
+ return _L_ButtonList;
+ }
+ }
+ private UiNode_ButtonList _L_ButtonList;
+
+
+ public Settlement() : base(nameof(Settlement))
+ {
+ }
+
+ ///
+ /// 类型: , 路径: Settlement.Bg
+ ///
+ public class UiNode_Bg : IUiNode
+ {
+ public UiNode_Bg(Godot.ColorRect node) : base(node) { }
+ public override UiNode_Bg Clone() => new ((Godot.ColorRect)Instance.Duplicate());
+ }
+
+ ///
+ /// 类型: , 路径: Settlement.Title
+ ///
+ public class UiNode_Title : IUiNode
+ {
+ public UiNode_Title(Godot.Label node) : base(node) { }
+ public override UiNode_Title Clone() => new ((Godot.Label)Instance.Duplicate());
+ }
+
+ ///
+ /// 类型: , 路径: Settlement.ButtonList.Restart
+ ///
+ public class UiNode_Restart : IUiNode
+ {
+ public UiNode_Restart(Godot.Button node) : base(node) { }
+ public override UiNode_Restart Clone() => new ((Godot.Button)Instance.Duplicate());
+ }
+
+ ///
+ /// 类型: , 路径: Settlement.ButtonList.ToMenu
+ ///
+ public class UiNode_ToMenu : IUiNode
+ {
+ public UiNode_ToMenu(Godot.Button node) : base(node) { }
+ public override UiNode_ToMenu Clone() => new ((Godot.Button)Instance.Duplicate());
+ }
+
+ ///
+ /// 类型: , 路径: Settlement.ButtonList
+ ///
+ public class UiNode_ButtonList : IUiNode
+ {
+ ///
+ /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: Settlement.Restart
+ ///
+ public UiNode_Restart L_Restart
+ {
+ get
+ {
+ if (_L_Restart == null) _L_Restart = new UiNode_Restart(Instance.GetNodeOrNull("Restart"));
+ return _L_Restart;
+ }
+ }
+ private UiNode_Restart _L_Restart;
+
+ ///
+ /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: Settlement.ToMenu
+ ///
+ public UiNode_ToMenu L_ToMenu
+ {
+ get
+ {
+ if (_L_ToMenu == null) _L_ToMenu = new UiNode_ToMenu(Instance.GetNodeOrNull("ToMenu"));
+ return _L_ToMenu;
+ }
+ }
+ private UiNode_ToMenu _L_ToMenu;
+
+ public UiNode_ButtonList(Godot.VBoxContainer node) : base(node) { }
+ public override UiNode_ButtonList Clone() => new ((Godot.VBoxContainer)Instance.Duplicate());
+ }
+
+}
diff --git a/DungeonShooting_Godot/src/game/ui/settlement/SettlementPanel.cs b/DungeonShooting_Godot/src/game/ui/settlement/SettlementPanel.cs
new file mode 100644
index 0000000..d5c8b89
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/ui/settlement/SettlementPanel.cs
@@ -0,0 +1,33 @@
+using Godot;
+
+namespace UI.Settlement;
+
+public partial class SettlementPanel : Settlement
+{
+
+ public override void OnShowUi()
+ {
+ L_ButtonList.L_Restart.Instance.Pressed += OnRestartClick;
+ L_ButtonList.L_ToMenu.Instance.Pressed += OnToMenuClick;
+ }
+
+ public override void OnHideUi()
+ {
+ L_ButtonList.L_Restart.Instance.Pressed -= OnRestartClick;
+ L_ButtonList.L_ToMenu.Instance.Pressed -= OnToMenuClick;
+
+ }
+
+ private void OnRestartClick()
+ {
+
+ }
+
+ private void OnToMenuClick()
+ {
+ HideUi();
+ GameApplication.Instance.DungeonManager.ExitDungeon();
+ UiManager.Open_Main();
+ }
+
+}