diff --git a/DungeonShooting_Godot/addons/dungeonShooting_plugin/Automation.cs b/DungeonShooting_Godot/addons/dungeonShooting_plugin/Automation.cs
index da597de..576b44f 100644
--- a/DungeonShooting_Godot/addons/dungeonShooting_plugin/Automation.cs
+++ b/DungeonShooting_Godot/addons/dungeonShooting_plugin/Automation.cs
@@ -1,64 +1,16 @@
 #if TOOLS
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.Json;
-using System.Text.RegularExpressions;
+using Generator;
 using Godot;
-using File = System.IO.File;
 
 [Tool]
 public partial class Automation : Control
 {
-	//支持后缀
-	private string[] suffix =
-	{
-		".png", ".jpg", ".txt", ".json", ".ini", ".tscn", ".tres", ".otf", ".gdshader", ".ogg", ".mp3", ".wav", ".svg"
-	};
-	//排除第一层的文件夹
-	private string[] exclude =
-	{
-		".vscode", ".idea", ".git", ".import", ".mono", "android", "addons", ".godot"
-	};
-	private string currDir = System.Environment.CurrentDirectory;
-
-	private string resultStr = "";
-	
 	/// <summary>
 	/// 更新 ResourcePath
 	/// </summary>
 	private void _on_Button_pressed()
 	{
-		resultStr = "/// <summary>\n" +
-		            "/// 编辑器下所有资源路径, 该类为 Automation 面板下自动生成的, 请不要手动编辑!\n" +
-		            "/// </summary>\n" +
-		            "public class ResourcePath\n" +
-		            "{\n";
-
-		GD.Print("更新 ResourcePath...");
-
-		var directoryInfo = new DirectoryInfo(currDir);
-
-		var directories = directoryInfo.GetDirectories();
-		for (int i = 0; i < directories.Length; i++)
-		{
-			var directory = directories[i];
-			if (!exclude.Contains(directory.Name))
-			{
-				EachDir(directory);
-			}
-		}
-
-		var fileInfos = directoryInfo.GetFiles();
-		for (var i = 0; i < fileInfos.Length; i++)
-		{
-			HandleFile(fileInfos[i]);
-		}
-
-		resultStr += "}";
-		File.WriteAllText("src/game/manager/ResourcePath.cs", resultStr);
-		GD.Print("ResourcePath.cs 写出完成!");
+		ResourcePathGenerator.Generate();
 	}
 
 	/// <summary>
@@ -66,139 +18,7 @@
 	/// </summary>
 	private void _on_Button2_pressed()
 	{
-		//地图路径
-		var tileDir = GameConfig.RoomTileDir;
-		//地图描述数据路径
-		var tileDataDir = GameConfig.RoomTileDataDir;
-		
-		var tileDirInfo = new DirectoryInfo(tileDir);
-		var tileDataDirInfo = new DirectoryInfo(tileDataDir);
-
-		//所有地图列表
-		var mapList = new HashSet<string>();
-		
-		//收集所有名称
-		var fileDataInfos = tileDataDirInfo.GetFiles();
-		foreach (var fileInfo in fileDataInfos)
-		{
-			mapList.Add(RemoveExtension(fileInfo.Name));
-		}
-		//收集所有名称
-		var fileInfos = tileDirInfo.GetFiles();
-		foreach (var fileInfo in fileInfos)
-		{
-			if (fileInfo.Extension == ".tscn")
-			{
-				mapList.Add(RemoveExtension(fileInfo.Name));
-			}
-		}
-		
-		//剔除多余的 tile.json
-		var arrays = mapList.ToArray();
-		foreach (var item in arrays)
-		{
-			if (!File.Exists(tileDir + item + ".tscn"))
-			{
-				mapList.Remove(item);
-				var filePath = tileDataDir + item + ".json";
-				if (File.Exists(filePath))
-				{
-					GD.Print($"未找到'{tileDir + item}.tscn', 删除配置文件: {filePath}");
-					File.Delete(filePath);
-				}
-			}
-		}
-
-		//手动生成缺失的 tile.json
-		foreach (var item in mapList)
-		{
-			if (!File.Exists(tileDataDir + item + ".json"))
-			{
-				var tscnName = tileDir + item + ".tscn";
-				var packedScene = ResourceManager.Load<PackedScene>(tscnName, false);
-				if (packedScene != null)
-				{
-					var dungeonRoomTemplate = packedScene.Instantiate<DungeonRoomTemplate>();
-					var usedRect = dungeonRoomTemplate.GetUsedRect();
-					var dungeonTile = new DungeonTile(dungeonRoomTemplate);
-					dungeonTile.SetFloorAtlasCoords(new List<Vector2I>() { new Vector2I(0, 8) });
-					//计算导航网格
-					dungeonTile.GenerateNavigationPolygon(0);
-					var polygonData = dungeonTile.GetPolygonData();
-					DungeonRoomTemplate.SaveConfig(new List<DoorAreaInfo>(), usedRect.Position, usedRect.Size, polygonData.ToList(), item);
-					dungeonRoomTemplate.QueueFree();
-				}
-			}
-		}
-
-		var list = new List<DungeonRoomSplit>();
-		//整合操作
-		foreach (var item in mapList)
-		{
-			var configPath = tileDataDir + item + ".json";
-			var configText = File.ReadAllText(configPath);
-			var roomInfo = DungeonRoomTemplate.DeserializeDungeonRoomInfo(configText);
-			var split = new DungeonRoomSplit();
-			split.ScenePath = ToResPath(tileDir + item + ".tscn");
-			split.ConfigPath = ToResPath(configPath);
-			split.RoomInfo = roomInfo;
-			list.Add(split);
-		}
-
-		//写出配置
-		var config = new JsonSerializerOptions();
-		config.WriteIndented = true;
-		var text = JsonSerializer.Serialize(list, config);
-		File.WriteAllText(GameConfig.RoomTileConfigFile, text);
-
-		GD.Print("地牢房间配置, 重新打包完成!");
-	}
-	
-	private void EachDir(DirectoryInfo directoryInfos)
-	{
-		var fileInfos = directoryInfos.GetFiles();
-		for (var i = 0; i < fileInfos.Length; i++)
-		{
-			HandleFile(fileInfos[i]);
-		}
-
-		var directories = directoryInfos.GetDirectories();
-		for (var i = 0; i < directories.Length; i++)
-		{
-			EachDir(directories[i]);
-		}
-	}
-
-	private void HandleFile(FileInfo fileInfo)
-	{
-		if (suffix.Contains(fileInfo.Extension))
-		{
-			var field = fileInfo.FullName.Substring(currDir.Length + 1);
-			field = field.Replace("\\", "/");
-			var resPath = "res://" + field;
-			field = field.Replace(".", "_");
-			field = field.Replace("/", "_");
-			field = Regex.Replace(field, "[^\\w_]", "");
-			resultStr += $"    public const string {field} = \"{resPath}\";\n";
-		}
-	}
-
-	private string ToResPath(string path)
-	{
-		var field = path.Substring(currDir.Length + 1);
-		field = field.Replace("\\", "/");
-		return "res://" + field;
-	}
-
-	private string RemoveExtension(string name)
-	{
-		var index = name.LastIndexOf(".", StringComparison.Ordinal);
-		if (index >= 0)
-		{
-			return name.Substring(0, index);
-		}
-
-		return name;
+		RoomPackGenerator.Generate();
 	}
 }
 #endif
\ No newline at end of file
diff --git a/DungeonShooting_Godot/prefab/ui/RoomUI.tscn b/DungeonShooting_Godot/prefab/ui/RoomUI.tscn
index 0428334..d7f5ba9 100644
--- a/DungeonShooting_Godot/prefab/ui/RoomUI.tscn
+++ b/DungeonShooting_Godot/prefab/ui/RoomUI.tscn
@@ -1,6 +1,6 @@
 [gd_scene load_steps=11 format=3 uid="uid://bvpmtfupny8iu"]
 
-[ext_resource type="Script" path="res://src/game/ui/RoomUI.cs" id="1"]
+[ext_resource type="Script" path="res://src/game/ui/roomUI/RoomUIPanel.cs" id="1_u48k1"]
 [ext_resource type="Texture2D" uid="uid://k621mhhkg65f" path="res://resource/sprite/ui/mapBar.png" id="2"]
 [ext_resource type="PackedScene" path="res://prefab/ui/bar/ReloadBar.tscn" id="3"]
 [ext_resource type="Texture2D" uid="uid://u5ul7fu8wv1a" path="res://resource/sprite/ui/healthBar.png" id="4"]
@@ -18,7 +18,7 @@
 anchor_bottom = 1.0
 grow_horizontal = 2
 grow_vertical = 2
-script = ExtResource("1")
+script = ExtResource("1_u48k1")
 
 [node name="Control" type="Control" parent="."]
 anchors_preset = 0
diff --git a/DungeonShooting_Godot/resource/theme/mainTheme.tres b/DungeonShooting_Godot/resource/theme/mainTheme.tres
index 2f97beb..553bffb 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_sxxms"]
+[sub_resource type="Image" id="Image_xtynf"]
 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_sxxms")
+image = SubResource("Image_xtynf")
 
 [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_d4kn7"]
+[sub_resource type="Image" id="Image_njets"]
 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_d4kn7")
+image = SubResource("Image_njets")
 
 [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_lim08"]
+[sub_resource type="Image" id="Image_1em1k"]
 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_lim08")
+image = SubResource("Image_1em1k")
 
 [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_86mbg"]
+[sub_resource type="Image" id="Image_366uy"]
 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_86mbg")
+image = SubResource("Image_366uy")
 
 [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_4edk4"]
+[sub_resource type="Image" id="Image_s1m4t"]
 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_4edk4")
+image = SubResource("Image_s1m4t")
 
 [sub_resource type="StyleBoxFlat" id="57"]
 content_margin_left = 6.0
diff --git a/DungeonShooting_Godot/src/framework/generator/ResourcePathGenerator.cs b/DungeonShooting_Godot/src/framework/generator/ResourcePathGenerator.cs
new file mode 100644
index 0000000..0958ed4
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/generator/ResourcePathGenerator.cs
@@ -0,0 +1,96 @@
+
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using Godot;
+
+namespace Generator;
+
+/// <summary>
+/// ResourcePath 类文件生成器
+/// </summary>
+public static class ResourcePathGenerator
+{
+    //支持后缀
+    private static string[] suffix =
+    {
+        ".png", ".jpg", ".txt", ".json", ".ini", ".tscn", ".tres", ".otf", ".gdshader", ".ogg", ".mp3", ".wav", ".svg"
+    };
+    //排除第一层的文件夹
+    private static string[] exclude =
+    {
+        ".vscode", ".idea", ".git", ".import", ".mono", "android", "addons", ".godot"
+    };
+    private static string currDir = System.Environment.CurrentDirectory;
+
+    private static string resultStr = "";
+
+    //保存路径
+    public static string savePath = "src/game/manager/ResourcePath.cs";
+
+    /// <summary>
+    /// 执行生成操作
+    /// </summary>
+    public static void Generate()
+    {
+        resultStr = "/// <summary>\n" +
+                    "/// 编辑器下所有资源路径, 该类为 Automation 面板下自动生成的, 请不要手动编辑!\n" +
+                    "/// </summary>\n" +
+                    "public class ResourcePath\n" +
+                    "{\n";
+
+        GD.Print("更新 ResourcePath...");
+
+        var directoryInfo = new DirectoryInfo(currDir);
+
+        var directories = directoryInfo.GetDirectories();
+        for (int i = 0; i < directories.Length; i++)
+        {
+            var directory = directories[i];
+            if (!exclude.Contains(directory.Name))
+            {
+                EachDir(directory);
+            }
+        }
+
+        var fileInfos = directoryInfo.GetFiles();
+        for (var i = 0; i < fileInfos.Length; i++)
+        {
+            HandleFile(fileInfos[i]);
+        }
+
+        resultStr += "}";
+        File.WriteAllText(savePath, resultStr);
+        GD.Print("ResourcePath.cs 写出完成!");
+    }
+    
+    private static void EachDir(DirectoryInfo directoryInfos)
+    {
+        var fileInfos = directoryInfos.GetFiles();
+        for (var i = 0; i < fileInfos.Length; i++)
+        {
+            HandleFile(fileInfos[i]);
+        }
+
+        var directories = directoryInfos.GetDirectories();
+        for (var i = 0; i < directories.Length; i++)
+        {
+            EachDir(directories[i]);
+        }
+    }
+
+    private static void HandleFile(FileInfo fileInfo)
+    {
+        if (suffix.Contains(fileInfo.Extension))
+        {
+            var field = fileInfo.FullName.Substring(currDir.Length + 1);
+            field = field.Replace("\\", "/");
+            var resPath = "res://" + field;
+            field = field.Replace(".", "_");
+            field = field.Replace("/", "_");
+            field = Regex.Replace(field, "[^\\w_]", "");
+            resultStr += $"    public const string {field} = \"{resPath}\";\n";
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/generator/RoomPackGenerator.cs b/DungeonShooting_Godot/src/framework/generator/RoomPackGenerator.cs
new file mode 100644
index 0000000..046b4ff
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/generator/RoomPackGenerator.cs
@@ -0,0 +1,128 @@
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using Godot;
+
+namespace Generator;
+
+/// <summary>
+/// 预制房间数据生成器
+/// </summary>
+public static class RoomPackGenerator
+{
+	private static string currDir = System.Environment.CurrentDirectory;
+	
+	/// <summary>
+	/// 执行生成操作
+	/// </summary>
+    public static void Generate()
+    {
+        //地图路径
+		var tileDir = DungeonRoomTemplate.RoomTileDir;
+		//地图描述数据路径
+		var tileDataDir = DungeonRoomTemplate.RoomTileDataDir;
+		
+		var tileDirInfo = new DirectoryInfo(tileDir);
+		var tileDataDirInfo = new DirectoryInfo(tileDataDir);
+
+		//所有地图列表
+		var mapList = new HashSet<string>();
+		
+		//收集所有名称
+		var fileDataInfos = tileDataDirInfo.GetFiles();
+		foreach (var fileInfo in fileDataInfos)
+		{
+			mapList.Add(RemoveExtension(fileInfo.Name));
+		}
+		//收集所有名称
+		var fileInfos = tileDirInfo.GetFiles();
+		foreach (var fileInfo in fileInfos)
+		{
+			if (fileInfo.Extension == ".tscn")
+			{
+				mapList.Add(RemoveExtension(fileInfo.Name));
+			}
+		}
+		
+		//剔除多余的 tile.json
+		var arrays = mapList.ToArray();
+		foreach (var item in arrays)
+		{
+			if (!File.Exists(tileDir + item + ".tscn"))
+			{
+				mapList.Remove(item);
+				var filePath = tileDataDir + item + ".json";
+				if (File.Exists(filePath))
+				{
+					GD.Print($"未找到'{tileDir + item}.tscn', 删除配置文件: {filePath}");
+					File.Delete(filePath);
+				}
+			}
+		}
+
+		//手动生成缺失的 tile.json
+		foreach (var item in mapList)
+		{
+			if (!File.Exists(tileDataDir + item + ".json"))
+			{
+				var tscnName = tileDir + item + ".tscn";
+				var packedScene = ResourceManager.Load<PackedScene>(tscnName, false);
+				if (packedScene != null)
+				{
+					var dungeonRoomTemplate = packedScene.Instantiate<DungeonRoomTemplate>();
+					var usedRect = dungeonRoomTemplate.GetUsedRect();
+					var dungeonTile = new DungeonTile(dungeonRoomTemplate);
+					dungeonTile.SetFloorAtlasCoords(new List<Vector2I>() { new Vector2I(0, 8) });
+					//计算导航网格
+					dungeonTile.GenerateNavigationPolygon(0);
+					var polygonData = dungeonTile.GetPolygonData();
+					DungeonRoomTemplate.SaveConfig(new List<DoorAreaInfo>(), usedRect.Position, usedRect.Size, polygonData.ToList(), item);
+					dungeonRoomTemplate.QueueFree();
+				}
+			}
+		}
+
+		var list = new List<DungeonRoomSplit>();
+		//整合操作
+		foreach (var item in mapList)
+		{
+			var configPath = tileDataDir + item + ".json";
+			var configText = File.ReadAllText(configPath);
+			var roomInfo = DungeonRoomTemplate.DeserializeDungeonRoomInfo(configText);
+			var split = new DungeonRoomSplit();
+			split.ScenePath = ToResPath(tileDir + item + ".tscn");
+			split.ConfigPath = ToResPath(configPath);
+			split.RoomInfo = roomInfo;
+			list.Add(split);
+		}
+
+		//写出配置
+		var config = new JsonSerializerOptions();
+		config.WriteIndented = true;
+		var text = JsonSerializer.Serialize(list, config);
+		File.WriteAllText(DungeonRoomTemplate.RoomTileConfigFile, text);
+
+		GD.Print("地牢房间配置, 重新打包完成!");
+    }
+    
+    private static string ToResPath(string path)
+    {
+	    var field = path.Substring(currDir.Length + 1);
+	    field = field.Replace("\\", "/");
+	    return "res://" + field;
+    }
+
+    private static string RemoveExtension(string name)
+    {
+	    var index = name.LastIndexOf(".", StringComparison.Ordinal);
+	    if (index >= 0)
+	    {
+		    return name.Substring(0, index);
+	    }
+
+	    return name;
+    }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/generator/UiGenerator.cs b/DungeonShooting_Godot/src/framework/generator/UiGenerator.cs
new file mode 100644
index 0000000..fd72026
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/generator/UiGenerator.cs
@@ -0,0 +1,136 @@
+
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using Godot;
+
+namespace Generator;
+
+public static class UiGenerator
+{
+    private static int _nodeIndex = 0;
+
+    public static void GenerateUi(Control control)
+    {
+        _nodeIndex = 0;
+        var uiNode = EachNode(control);
+        var code = GenerateClassCode(uiNode);
+        GD.Print("code: \n" + code);
+    }
+
+    private static string GenerateClassCode(UiNode uiNode)
+    {
+        return $"namespace UI;\n\n" +
+               $"public abstract partial class {uiNode.Name} : UiBase\n" +
+               $"{{\n" +
+               GeneratePropertyListClassCode("", uiNode.Name + ".", uiNode, "    ") +
+               $"\n\n" +
+               GenerateAllChildrenClassCode(uiNode.Name + ".", uiNode, "    ") +
+               $"}}\n";
+    }
+
+    private static string GenerateAllChildrenClassCode(string parent, UiNode uiNode, string retraction)
+    {
+        var str = "";
+        if (uiNode.Children != null)
+        {
+            for (var i = 0; i < uiNode.Children.Count; i++)
+            {
+                var item = uiNode.Children[i];
+                str += GenerateAllChildrenClassCode(parent + item.Name + ".", item, retraction);
+                str += GenerateChildrenClassCode(parent, item, retraction);
+            }
+        }
+
+        return str;
+    }
+    
+    private static string GenerateChildrenClassCode(string parent, UiNode uiNode, string retraction)
+    {
+        return retraction + $"/// <summary>\n" + 
+               retraction + $"/// 类型: <see cref=\"{uiNode.TypeName}\"/>, 路径: {parent}{uiNode.Name}\n" + 
+               retraction + $"/// </summary>\n" + 
+               retraction + $"public class {uiNode.ClassName}\n" +
+               retraction + $"{{\n" +
+               retraction + $"    /// <summary>\n" +
+               retraction + $"    /// Ui节点实例, 节点类型: <see cref=\"{uiNode.TypeName}\"/>, 节点路径: {parent}{uiNode.Name}\n" +
+               retraction + $"    /// </summary>\n" +
+               retraction + $"    public {uiNode.TypeName} Instance {{ get; }}\n\n" +
+               GeneratePropertyListClassCode("Instance.", parent, uiNode, retraction + "    ") + 
+               retraction + $"    public {uiNode.ClassName}({uiNode.TypeName} node) => Instance = node;\n" +
+               retraction + $"    public {uiNode.ClassName} Clone() => new (({uiNode.TypeName})Instance.Duplicate());\n" +
+               retraction + $"}}\n\n";
+    }
+
+    private static string GeneratePropertyListClassCode(string target, string parent, UiNode uiNode, string retraction)
+    {
+        var str = "";
+        if (uiNode.Children != null)
+        {
+            for (var i = 0; i < uiNode.Children.Count; i++)
+            {
+                var item = uiNode.Children[i];
+                str += GeneratePropertyCode(target, parent, item, retraction);
+            }
+        }
+
+        return str;
+    }
+    
+    private static string GeneratePropertyCode(string target, string parent, UiNode uiNode, string retraction)
+    {
+        return retraction + $"/// <summary>\n" + 
+               retraction + $"/// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref=\"{uiNode.TypeName}\"/>, 节点路径: {parent}{uiNode.Name}\n" + 
+               retraction + $"/// </summary>\n" + 
+               retraction + $"public {uiNode.ClassName} {uiNode.Name}\n" +
+               retraction + $"{{\n" + 
+               retraction + $"    get\n" + 
+               retraction + $"    {{\n" + 
+               retraction + $"        if (_{uiNode.Name} == null) _{uiNode.Name} = new {uiNode.ClassName}({target}GetNode<{uiNode.TypeName}>(\"{uiNode.Name}\"));\n" + 
+               retraction + $"        return _{uiNode.Name};\n" + 
+               retraction + $"    }}\n" + 
+               retraction + $"}}\n" +
+               retraction + $"private {uiNode.ClassName} _{uiNode.Name};\n\n";
+    }
+    
+    private static UiNode EachNode(Control control)
+    {
+        var name = Regex.Replace(control.Name, "[^\\w_]", "");
+        var uiNode = new UiNode(name, "UiNode" + (_nodeIndex++) + "_" + name, control.GetType().FullName);
+
+        var childCount = control.GetChildCount();
+        if (childCount > 0)
+        {
+            for (var i = 0; i < childCount; i++)
+            {
+                var children = control.GetChildOrNull<Control>(i);
+                if (children != null)
+                {
+                    if (uiNode.Children == null)
+                    {
+                        uiNode.Children = new List<UiNode>();
+                    }
+
+                    uiNode.Children.Add(EachNode(children));
+                }
+            }
+        }
+
+        return uiNode;
+    }
+
+    private class UiNode
+    {
+        public string Name;
+        public string ClassName;
+        public string TypeName;
+        public List<UiNode> Children;
+
+        public UiNode(string name, string className, string typeName)
+        {
+            Name = name;
+            ClassName = className;
+            TypeName = typeName;
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/ui/UiBase.cs b/DungeonShooting_Godot/src/framework/ui/UiBase.cs
new file mode 100644
index 0000000..d3e50ef
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/ui/UiBase.cs
@@ -0,0 +1,50 @@
+
+using Godot;
+
+/// <summary>
+/// Ui 基类
+/// </summary>
+public abstract partial class UiBase : Control
+{
+    /// <summary>
+    /// 当前 UI 所属层级
+    /// </summary>
+    [Export]
+    public UiLayer Layer = UiLayer.Middle;
+
+    /// <summary>
+    /// Ui 模式
+    /// </summary>
+    [Export]
+    public UiMode Mode = UiMode.Normal;
+
+    /// <summary>
+    /// 阻止下层 Ui 点击
+    /// </summary>
+    [Export]
+    public bool KeepOut = false;
+
+    /// <summary>
+    /// 创建当前ui时调用
+    /// </summary>
+    public virtual void OnCreate()
+    {
+    }
+    
+    /// <summary>
+    /// 当前ui打开时调用,并接收参数
+    /// </summary>
+    public abstract void OnOpen(params object[] args);
+
+    /// <summary>
+    /// 当前ui关闭时调用
+    /// </summary>
+    public abstract void OnClose();
+
+    /// <summary>
+    /// 销毁当前ui时调用
+    /// </summary>
+    public virtual void OnDispose()
+    {
+    }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/ui/UiLayer.cs b/DungeonShooting_Godot/src/framework/ui/UiLayer.cs
new file mode 100644
index 0000000..4a7002e
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/ui/UiLayer.cs
@@ -0,0 +1,20 @@
+
+public enum UiLayer
+{
+    /// <summary>
+    /// 最底层
+    /// </summary>
+    Bottom,
+    /// <summary>
+    /// 中间层
+    /// </summary>
+    Middle,
+    /// <summary>
+    /// 较高层
+    /// </summary>
+    Height,
+    /// <summary>
+    /// 最顶层
+    /// </summary>
+    Pop,
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/ui/UiManager.cs b/DungeonShooting_Godot/src/framework/ui/UiManager.cs
new file mode 100644
index 0000000..289a729
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/ui/UiManager.cs
@@ -0,0 +1,27 @@
+
+using Godot;
+
+public static partial class UiManager
+{
+    private static bool _init = false;
+    
+    public static void Init()
+    {
+        if (_init)
+        {
+            return;
+        }
+
+        _init = true;
+    }
+
+    public static UiBase OpenUi(string resourcePath)
+    {
+        return null;
+    }
+
+    public static T OpenUi<T>(string resourcePath) where T : UiBase
+    {
+        return (T)OpenUi(resourcePath);
+    }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/ui/UiMode.cs b/DungeonShooting_Godot/src/framework/ui/UiMode.cs
new file mode 100644
index 0000000..d4d9ccb
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/ui/UiMode.cs
@@ -0,0 +1,12 @@
+
+public enum UiMode
+{
+    /// <summary>
+    /// 正常模式,可以开多个相同的 Ui
+    /// </summary>
+    Normal,
+    /// <summary>
+    /// 单例模式, 只能同时打开一个相同的 Ui
+    /// </summary>
+    Singleton,
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs
index 4fbf5a0..9e7f69d 100644
--- a/DungeonShooting_Godot/src/game/GameApplication.cs
+++ b/DungeonShooting_Godot/src/game/GameApplication.cs
@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Text.Json;
 using Godot;
+using UI;
 
 public partial class GameApplication : Node2D
 {
@@ -49,7 +50,7 @@
 	/// <summary>
 	/// 游戏ui对象
 	/// </summary>
-	public RoomUI Ui { get; private set; }
+	public RoomUIPanel Ui { get; private set; }
 
 	/// <summary>
 	/// 全局根节点
@@ -81,6 +82,9 @@
 		ActivityObject.IsDebug = Debug;
 		//Engine.TimeScale = 0.3f;
 
+		//初始化ui
+		UiManager.Init();
+		
 		GlobalNodeRoot = GetNode<Node2D>(GlobalNodeRootPath);
 		// 初始化鼠标
 		Input.MouseMode = Input.MouseModeEnum.Hidden;
@@ -89,7 +93,7 @@
 		RoomManager = GetNode<RoomManager>(RoomPath);
 		SubViewport = GetNode<SubViewport>(ViewportPath);
 		SubViewportContainer = GetNode<SubViewportContainer>(ViewportContainerPath);
-		Ui = GetNode<RoomUI>(UiPath);
+		Ui = GetNode<RoomUIPanel>(UiPath);
 
 		Ui.AddChild(Cursor);
 	}
diff --git a/DungeonShooting_Godot/src/game/ui/ReloadBar.cs b/DungeonShooting_Godot/src/game/ui/ReloadBar.cs
index 2f4a7ba..697e3e0 100644
--- a/DungeonShooting_Godot/src/game/ui/ReloadBar.cs
+++ b/DungeonShooting_Godot/src/game/ui/ReloadBar.cs
@@ -35,7 +35,7 @@
     public void ShowBar(Vector2 position, float progress)
     {
         Visible = true;
-        GlobalPosition = position;
+        GlobalPosition = position.Round();
         progress = Mathf.Clamp(progress, 0, 1);
         block.Position = new Vector2(startX + (width - 3) * progress, 0);
     }
diff --git a/DungeonShooting_Godot/src/game/ui/RoomUI.cs b/DungeonShooting_Godot/src/game/ui/RoomUI.cs
deleted file mode 100644
index 2f25757..0000000
--- a/DungeonShooting_Godot/src/game/ui/RoomUI.cs
+++ /dev/null
@@ -1,179 +0,0 @@
-using Godot;
-
-/// <summary>
-/// 房间中的ui
-/// </summary>
-public partial class RoomUI : Control
-{
-    //public static RoomUI Current { get; private set; }
-
-    /// <summary>
-    /// 当前血量
-    /// </summary>
-    public int Hp { get; private set; }
-    /// <summary>
-    /// 最大血量
-    /// </summary>
-    public int MaxHp { get; private set; }
-    /// <summary>
-    /// 当前护盾值
-    /// </summary>
-    public int Shield { get; private set; }
-    /// <summary>
-    /// 最大护盾值
-    /// </summary>
-    public int MaxShield { get; private set; }
-    /// <summary>
-    /// 互动提示组件
-    /// </summary>
-    public InteractiveTipBar InteractiveTipBar { get; private set; }
-    /// <summary>
-    /// 换弹进度组件
-    /// </summary>
-    public ReloadBar ReloadBar { get; private set; }
-
-    private NinePatchRect hpSlot;
-    private NinePatchRect shieldSlot;
-    private TextureRect hpBar;
-    private TextureRect shieldBar;
-    private Label bulletText;
-    private TextureRect gunSprite;
-
-    public override void _EnterTree()
-    {
-        hpSlot = GetNode<NinePatchRect>("Control/HealthBar/HpSlot");
-        shieldSlot = GetNode<NinePatchRect>("Control/HealthBar/ShieldSlot");
-        hpBar = GetNode<TextureRect>("Control/HealthBar/HpSlot/HpBar");
-        shieldBar = GetNode<TextureRect>("Control/HealthBar/ShieldSlot/ShieldBar");
-
-        bulletText = GetNode<Label>("Control/GunBar/BulletText");
-        gunSprite = GetNode<TextureRect>("Control/GunBar/GunSprite");
-
-        InteractiveTipBar = GetNode<InteractiveTipBar>("ViewNode/InteractiveTipBar");
-        InteractiveTipBar.Visible = false;
-
-        ReloadBar = GetNode<ReloadBar>("ViewNode/ReloadBar");
-        ReloadBar.Visible = false;
-    }
-
-    public override void _Ready()
-    {
-        //将 GlobalNode 节点下的 ui 节点放入全局坐标中
-        var globalNode = GetNode("GlobalNode");
-        var root = GameApplication.Instance.GlobalNodeRoot;
-        var count = globalNode.GetChildCount();
-        for (int i = count - 1; i >= 0; i--)
-        {
-            var node = globalNode.GetChild(i);
-            globalNode.RemoveChild(node);
-            root.CallDeferred("add_child", node);
-        }
-        globalNode.CallDeferred("queue_free");
-        
-        //将 ViewNode 节点放到 SubViewport 下
-        var viewNode = GetNode("ViewNode");
-        var viewport = GameApplication.Instance.SubViewport;
-        count = viewNode.GetChildCount();
-        for (int i = count - 1; i >= 0; i--)
-        {
-            var node = viewNode.GetChild(i);
-            viewNode.RemoveChild(node);
-            viewport.CallDeferred("add_child", node);
-        }
-        viewNode.CallDeferred("queue_free");
-    }
-
-    public override void _Process(double delta)
-    {
-        
-    }
-
-    public override void _PhysicsProcess(double delta)
-    {
-        // var colorRect = GetNode<ColorRect>("ColorRect");
-        // var pos = GameApplication.Instance.ViewToGlobalPosition(GameApplication.Instance.Node3D.Player.GlobalPosition);
-        // colorRect.SetGlobalPosition(pos);
-        //GD.Print("pos: " + pos + ", " + colorRect.GlobalPosition);
-    }
-
-    /// <summary>
-    /// 设置最大血量
-    /// </summary>
-    public void SetMaxHp(int maxHp)
-    {
-        MaxHp = Mathf.Max(maxHp, 0);
-        hpSlot.Size = new Vector2(maxHp + 3, hpSlot.Size.Y);
-        if (Hp > maxHp)
-        {
-            SetHp(maxHp);
-        }
-    }
-
-    /// <summary>
-    /// 设置最大护盾值
-    /// </summary>
-    public void SetMaxShield(int maxShield)
-    {
-        MaxShield = Mathf.Max(maxShield, 0); ;
-        shieldSlot.Size = new Vector2(maxShield + 2, shieldSlot.Size.Y);
-        if (Shield > MaxShield)
-        {
-            SetShield(maxShield);
-        }
-    }
-
-    /// <summary>
-    /// 设置当前血量
-    /// </summary>
-    public void SetHp(int hp)
-    {
-        Hp = Mathf.Clamp(hp, 0, MaxHp);
-        hpBar.Size = new Vector2(hp, hpBar.Size.Y);
-    }
-
-    /// <summary>
-    /// 设置护盾值
-    /// </summary>
-    public void SetShield(int shield)
-    {
-        Shield = Mathf.Clamp(shield, 0, MaxShield);
-        shieldBar.Size = new Vector2(shield, shieldBar.Size.Y);
-    }
-
-    /// <summary>
-    /// 玩家受到伤害
-    /// </summary>
-    public void Hit(int num)
-    {
-
-    }
-
-    /// <summary>
-    /// 设置显示在 ui 上的枪的纹理
-    /// </summary>
-    /// <param name="gun">纹理</param>
-    public void SetGunTexture(Texture2D gun)
-    {
-        if (gun != null)
-        {
-            gunSprite.Texture = gun;
-            gunSprite.Visible = true;
-            bulletText.Visible = true;
-        }
-        else
-        {
-            gunSprite.Visible = false;
-            bulletText.Visible = false;
-        }
-    }
-
-    /// <summary>
-    /// 设置弹药数据
-    /// </summary>
-    /// <param name="curr">当前弹夹弹药量</param>
-    /// <param name="total">剩余弹药总数</param>
-    public void SetAmmunition(int curr, int total)
-    {
-        bulletText.Text = curr + " / " + total;
-    }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/ui/roomUI/RoomUI.cs b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUI.cs
new file mode 100644
index 0000000..0a9ac2e
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUI.cs
@@ -0,0 +1,277 @@
+namespace UI;
+
+public abstract partial class RoomUI : UiBase
+{
+    /// <summary>
+    /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.Control"/>, 节点路径: RoomUI.Control
+    /// </summary>
+    public UiNode1_Control Control
+    {
+        get
+        {
+            if (_Control == null) _Control = new UiNode1_Control(GetNode<Godot.Control>("Control"));
+            return _Control;
+        }
+    }
+    private UiNode1_Control _Control;
+
+
+
+    /// <summary>
+    /// 类型: <see cref="Godot.TextureRect"/>, 路径: RoomUI.Control.HealthBar.HpSlot.HpBar
+    /// </summary>
+    public class UiNode4_HpBar
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.HealthBar.HpSlot.HpBar
+        /// </summary>
+        public Godot.TextureRect Instance { get; }
+
+        public UiNode4_HpBar(Godot.TextureRect node) => Instance = node;
+        public UiNode4_HpBar Clone() => new ((Godot.TextureRect)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.NinePatchRect"/>, 路径: RoomUI.Control.HealthBar.HpSlot
+    /// </summary>
+    public class UiNode3_HpSlot
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.NinePatchRect"/>, 节点路径: RoomUI.Control.HealthBar.HpSlot
+        /// </summary>
+        public Godot.NinePatchRect Instance { get; }
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.HealthBar.HpBar
+        /// </summary>
+        public UiNode4_HpBar HpBar
+        {
+            get
+            {
+                if (_HpBar == null) _HpBar = new UiNode4_HpBar(Instance.GetNode<Godot.TextureRect>("HpBar"));
+                return _HpBar;
+            }
+        }
+        private UiNode4_HpBar _HpBar;
+
+        public UiNode3_HpSlot(Godot.NinePatchRect node) => Instance = node;
+        public UiNode3_HpSlot Clone() => new ((Godot.NinePatchRect)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.TextureRect"/>, 路径: RoomUI.Control.HealthBar.ShieldSlot.ShieldBar
+    /// </summary>
+    public class UiNode6_ShieldBar
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.HealthBar.ShieldSlot.ShieldBar
+        /// </summary>
+        public Godot.TextureRect Instance { get; }
+
+        public UiNode6_ShieldBar(Godot.TextureRect node) => Instance = node;
+        public UiNode6_ShieldBar Clone() => new ((Godot.TextureRect)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.NinePatchRect"/>, 路径: RoomUI.Control.HealthBar.ShieldSlot
+    /// </summary>
+    public class UiNode5_ShieldSlot
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.NinePatchRect"/>, 节点路径: RoomUI.Control.HealthBar.ShieldSlot
+        /// </summary>
+        public Godot.NinePatchRect Instance { get; }
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.HealthBar.ShieldBar
+        /// </summary>
+        public UiNode6_ShieldBar ShieldBar
+        {
+            get
+            {
+                if (_ShieldBar == null) _ShieldBar = new UiNode6_ShieldBar(Instance.GetNode<Godot.TextureRect>("ShieldBar"));
+                return _ShieldBar;
+            }
+        }
+        private UiNode6_ShieldBar _ShieldBar;
+
+        public UiNode5_ShieldSlot(Godot.NinePatchRect node) => Instance = node;
+        public UiNode5_ShieldSlot Clone() => new ((Godot.NinePatchRect)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.TextureRect"/>, 路径: RoomUI.Control.HealthBar
+    /// </summary>
+    public class UiNode2_HealthBar
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.HealthBar
+        /// </summary>
+        public Godot.TextureRect Instance { get; }
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.NinePatchRect"/>, 节点路径: RoomUI.Control.HpSlot
+        /// </summary>
+        public UiNode3_HpSlot HpSlot
+        {
+            get
+            {
+                if (_HpSlot == null) _HpSlot = new UiNode3_HpSlot(Instance.GetNode<Godot.NinePatchRect>("HpSlot"));
+                return _HpSlot;
+            }
+        }
+        private UiNode3_HpSlot _HpSlot;
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.NinePatchRect"/>, 节点路径: RoomUI.Control.ShieldSlot
+        /// </summary>
+        public UiNode5_ShieldSlot ShieldSlot
+        {
+            get
+            {
+                if (_ShieldSlot == null) _ShieldSlot = new UiNode5_ShieldSlot(Instance.GetNode<Godot.NinePatchRect>("ShieldSlot"));
+                return _ShieldSlot;
+            }
+        }
+        private UiNode5_ShieldSlot _ShieldSlot;
+
+        public UiNode2_HealthBar(Godot.TextureRect node) => Instance = node;
+        public UiNode2_HealthBar Clone() => new ((Godot.TextureRect)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.TextureRect"/>, 路径: RoomUI.Control.MapBar
+    /// </summary>
+    public class UiNode7_MapBar
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.MapBar
+        /// </summary>
+        public Godot.TextureRect Instance { get; }
+
+        public UiNode7_MapBar(Godot.TextureRect node) => Instance = node;
+        public UiNode7_MapBar Clone() => new ((Godot.TextureRect)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.TextureRect"/>, 路径: RoomUI.Control.GunBar.GunSprite
+    /// </summary>
+    public class UiNode9_GunSprite
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.GunBar.GunSprite
+        /// </summary>
+        public Godot.TextureRect Instance { get; }
+
+        public UiNode9_GunSprite(Godot.TextureRect node) => Instance = node;
+        public UiNode9_GunSprite Clone() => new ((Godot.TextureRect)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.Label"/>, 路径: RoomUI.Control.GunBar.BulletText
+    /// </summary>
+    public class UiNode10_BulletText
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.Label"/>, 节点路径: RoomUI.Control.GunBar.BulletText
+        /// </summary>
+        public Godot.Label Instance { get; }
+
+        public UiNode10_BulletText(Godot.Label node) => Instance = node;
+        public UiNode10_BulletText Clone() => new ((Godot.Label)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.Control"/>, 路径: RoomUI.Control.GunBar
+    /// </summary>
+    public class UiNode8_GunBar
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.Control"/>, 节点路径: RoomUI.Control.GunBar
+        /// </summary>
+        public Godot.Control Instance { get; }
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.Control.GunSprite
+        /// </summary>
+        public UiNode9_GunSprite GunSprite
+        {
+            get
+            {
+                if (_GunSprite == null) _GunSprite = new UiNode9_GunSprite(Instance.GetNode<Godot.TextureRect>("GunSprite"));
+                return _GunSprite;
+            }
+        }
+        private UiNode9_GunSprite _GunSprite;
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.Label"/>, 节点路径: RoomUI.Control.BulletText
+        /// </summary>
+        public UiNode10_BulletText BulletText
+        {
+            get
+            {
+                if (_BulletText == null) _BulletText = new UiNode10_BulletText(Instance.GetNode<Godot.Label>("BulletText"));
+                return _BulletText;
+            }
+        }
+        private UiNode10_BulletText _BulletText;
+
+        public UiNode8_GunBar(Godot.Control node) => Instance = node;
+        public UiNode8_GunBar Clone() => new ((Godot.Control)Instance.Duplicate());
+    }
+
+    /// <summary>
+    /// 类型: <see cref="Godot.Control"/>, 路径: RoomUI.Control
+    /// </summary>
+    public class UiNode1_Control
+    {
+        /// <summary>
+        /// Ui节点实例, 节点类型: <see cref="Godot.Control"/>, 节点路径: RoomUI.Control
+        /// </summary>
+        public Godot.Control Instance { get; }
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.HealthBar
+        /// </summary>
+        public UiNode2_HealthBar HealthBar
+        {
+            get
+            {
+                if (_HealthBar == null) _HealthBar = new UiNode2_HealthBar(Instance.GetNode<Godot.TextureRect>("HealthBar"));
+                return _HealthBar;
+            }
+        }
+        private UiNode2_HealthBar _HealthBar;
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.TextureRect"/>, 节点路径: RoomUI.MapBar
+        /// </summary>
+        public UiNode7_MapBar MapBar
+        {
+            get
+            {
+                if (_MapBar == null) _MapBar = new UiNode7_MapBar(Instance.GetNode<Godot.TextureRect>("MapBar"));
+                return _MapBar;
+            }
+        }
+        private UiNode7_MapBar _MapBar;
+
+        /// <summary>
+        /// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref="Godot.Control"/>, 节点路径: RoomUI.GunBar
+        /// </summary>
+        public UiNode8_GunBar GunBar
+        {
+            get
+            {
+                if (_GunBar == null) _GunBar = new UiNode8_GunBar(Instance.GetNode<Godot.Control>("GunBar"));
+                return _GunBar;
+            }
+        }
+        private UiNode8_GunBar _GunBar;
+
+        public UiNode1_Control(Godot.Control node) => Instance = node;
+        public UiNode1_Control Clone() => new ((Godot.Control)Instance.Duplicate());
+    }
+
+}
diff --git a/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
new file mode 100644
index 0000000..143b8a5
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
@@ -0,0 +1,163 @@
+using Godot;
+
+namespace UI;
+
+/// <summary>
+/// 房间中的ui
+/// </summary>
+public partial class RoomUIPanel : RoomUI
+{
+    /// <summary>
+    /// 当前血量
+    /// </summary>
+    public int Hp { get; private set; }
+    /// <summary>
+    /// 最大血量
+    /// </summary>
+    public int MaxHp { get; private set; }
+    /// <summary>
+    /// 当前护盾值
+    /// </summary>
+    public int Shield { get; private set; }
+    /// <summary>
+    /// 最大护盾值
+    /// </summary>
+    public int MaxShield { get; private set; }
+    
+    /// <summary>
+    /// 互动提示组件
+    /// </summary>
+    public InteractiveTipBar InteractiveTipBar { get; private set; }
+    /// <summary>
+    /// 换弹进度组件
+    /// </summary>
+    public ReloadBar ReloadBar { get; private set; }
+    
+    public override void OnOpen(params object[] args)
+    {
+        
+    }
+
+    public override void OnClose()
+    {
+        
+    }
+    
+    public override void _EnterTree()
+    {
+        InteractiveTipBar = GetNode<InteractiveTipBar>("ViewNode/InteractiveTipBar");
+        InteractiveTipBar.Visible = false;
+
+        ReloadBar = GetNode<ReloadBar>("ViewNode/ReloadBar");
+        ReloadBar.Visible = false;
+    }
+
+    public override void _Ready()
+    {
+        //将 GlobalNode 节点下的 ui 节点放入全局坐标中
+        var globalNode = GetNode("GlobalNode");
+        var root = GameApplication.Instance.GlobalNodeRoot;
+        var count = globalNode.GetChildCount();
+        for (int i = count - 1; i >= 0; i--)
+        {
+            var node = globalNode.GetChild(i);
+            globalNode.RemoveChild(node);
+            root.CallDeferred("add_child", node);
+        }
+        globalNode.CallDeferred("queue_free");
+        
+        //将 ViewNode 节点放到 SubViewport 下
+        var viewNode = GetNode("ViewNode");
+        var viewport = GameApplication.Instance.SubViewport;
+        count = viewNode.GetChildCount();
+        for (int i = count - 1; i >= 0; i--)
+        {
+            var node = viewNode.GetChild(i);
+            viewNode.RemoveChild(node);
+            viewport.CallDeferred("add_child", node);
+        }
+        viewNode.CallDeferred("queue_free");
+    }
+    
+    /// <summary>
+    /// 设置最大血量
+    /// </summary>
+    public void SetMaxHp(int maxHp)
+    {
+        MaxHp = Mathf.Max(maxHp, 0);
+        Control.HealthBar.HpSlot.Instance.Size = new Vector2(maxHp + 3, Control.HealthBar.HpSlot.Instance.Size.Y);
+        if (Hp > maxHp)
+        {
+            SetHp(maxHp);
+        }
+    }
+
+    /// <summary>
+    /// 设置最大护盾值
+    /// </summary>
+    public void SetMaxShield(int maxShield)
+    {
+        MaxShield = Mathf.Max(maxShield, 0); ;
+        Control.HealthBar.ShieldSlot.Instance.Size = new Vector2(maxShield + 2, Control.HealthBar.ShieldSlot.Instance.Size.Y);
+        if (Shield > MaxShield)
+        {
+            SetShield(maxShield);
+        }
+    }
+
+    /// <summary>
+    /// 设置当前血量
+    /// </summary>
+    public void SetHp(int hp)
+    {
+        Hp = Mathf.Clamp(hp, 0, MaxHp);
+        Control.HealthBar.HpSlot.Instance.Size = new Vector2(hp, Control.HealthBar.HpSlot.Instance.Size.Y);
+    }
+
+    /// <summary>
+    /// 设置护盾值
+    /// </summary>
+    public void SetShield(int shield)
+    {
+        Shield = Mathf.Clamp(shield, 0, MaxShield);
+        Control.HealthBar.ShieldSlot.Instance.Size = new Vector2(shield, Control.HealthBar.ShieldSlot.Instance.Size.Y);
+    }
+
+    /// <summary>
+    /// 玩家受到伤害
+    /// </summary>
+    public void Hit(int num)
+    {
+
+    }
+
+    /// <summary>
+    /// 设置显示在 ui 上的枪的纹理
+    /// </summary>
+    /// <param name="gun">纹理</param>
+    public void SetGunTexture(Texture2D gun)
+    {
+        if (gun != null)
+        {
+            Control.GunBar.GunSprite.Instance.Texture = gun;
+            Control.GunBar.GunSprite.Instance.Visible = true;
+            Control.GunBar.BulletText.Instance.Visible = true;
+        }
+        else
+        {
+            Control.GunBar.GunSprite.Instance.Visible = false;
+            Control.GunBar.BulletText.Instance.Visible = false;
+        }
+    }
+
+    /// <summary>
+    /// 设置弹药数据
+    /// </summary>
+    /// <param name="curr">当前弹夹弹药量</param>
+    /// <param name="total">剩余弹药总数</param>
+    public void SetAmmunition(int curr, int total)
+    {
+        Control.GunBar.BulletText.Instance.Text = curr + " / " + total;
+    }
+    
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/test/TestUi/Test.cs b/DungeonShooting_Godot/src/test/TestUi/Test.cs
new file mode 100644
index 0000000..d577cb9
--- /dev/null
+++ b/DungeonShooting_Godot/src/test/TestUi/Test.cs
@@ -0,0 +1,88 @@
+using Godot;
+
+namespace UI;
+
+/*
+ Test
+    c1
+        c11
+    c2
+ */
+public abstract partial class Test : UiBase
+{
+    public UiNode2_c1 c1
+    {
+        get
+        {
+            if (_c1 == null) _c1 = new UiNode2_c1(GetNode<Control>("c1"));
+            return _c1;
+        }
+    }
+    public UiNode3_c2 c2
+    {
+        get
+        {
+            if (_c2 == null) _c2 = new UiNode3_c2(GetNode<Control>("c2"));
+            return _c2;
+        }
+    }
+
+    private UiNode2_c1 _c1;
+    private UiNode3_c2 _c2;
+    
+    public class UiNode1_c11
+    {
+        public Control Instance { get; }
+
+        public UiNode1_c11(Control node)
+        {
+            Instance = node;
+        }
+
+        public UiNode1_c11 Clone()
+        {
+            return new UiNode1_c11((Control)Instance.Duplicate());
+        }
+    }
+    
+    public class UiNode2_c1
+    {
+        public Control Instance { get; }
+
+        public UiNode1_c11 c11
+        {
+            get
+            {
+                if (_c11 == null) _c11 = new UiNode1_c11(Instance.GetNode<Control>("c11"));
+                return _c11;
+            }
+        }
+
+        private UiNode1_c11 _c11;
+
+        public UiNode2_c1(Control node)
+        {
+            Instance = node;
+        }
+
+        public UiNode2_c1 Clone()
+        {
+            return new UiNode2_c1((Control)Instance.Duplicate());
+        }
+    }
+    
+    public class UiNode3_c2
+    {
+        public Control Instance { get; }
+
+        public UiNode3_c2(Control node)
+        {
+            Instance = node;
+        }
+
+        public UiNode3_c2 Clone()
+        {
+            return new UiNode3_c2((Control)Instance.Duplicate());
+        }
+    }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/test/TestUi/TestPanel.cs b/DungeonShooting_Godot/src/test/TestUi/TestPanel.cs
new file mode 100644
index 0000000..a65c268
--- /dev/null
+++ b/DungeonShooting_Godot/src/test/TestUi/TestPanel.cs
@@ -0,0 +1,14 @@
+namespace UI;
+
+public partial class TestPanel : Test
+{
+    public override void OnOpen(params object[] args)
+    {
+        
+    }
+
+    public override void OnClose()
+    {
+        
+    }
+}
\ No newline at end of file