diff --git a/DungeonShooting_Godot/prefab/ui/MapEditor.tscn b/DungeonShooting_Godot/prefab/ui/MapEditor.tscn index cd8e817..357a8dd 100644 --- a/DungeonShooting_Godot/prefab/ui/MapEditor.tscn +++ b/DungeonShooting_Godot/prefab/ui/MapEditor.tscn @@ -1,11 +1,13 @@ -[gd_scene load_steps=10 format=3 uid="uid://csbxfkdupsckv"] +[gd_scene load_steps=12 format=3 uid="uid://csbxfkdupsckv"] [ext_resource type="Script" path="res://src/game/ui/mapEditor/MapEditorPanel.cs" id="1_5s7a0"] [ext_resource type="Texture2D" uid="uid://cajcnlimvoxk" path="res://resource/sprite/ui/mapEditorProject/Back.png" id="2_s2w5x"] [ext_resource type="TileSet" uid="uid://b00g22o1cqhe8" path="res://resource/map/tileSet/TileSet1.tres" id="2_vrg60"] -[ext_resource type="Script" path="res://src/game/ui/mapEditor/TileView/EditorTileMap.cs" id="2_waq8f"] [ext_resource type="Texture2D" uid="uid://0878uloew5jo" path="res://resource/sprite/ui/mapEditor/ErrorCell.png" id="4_465u2"] +[ext_resource type="Script" path="res://src/game/ui/mapEditor/tileView/EditorTileMap.cs" id="4_wbtsa"] [ext_resource type="PackedScene" uid="uid://b4u66mxndxbrg" path="res://prefab/ui/MapEditorTools.tscn" id="6_7pvgu"] +[ext_resource type="Texture2D" uid="uid://dmm8jw06bhffh" path="res://resource/sprite/ui/mapEditorTools/Lock.png" id="7_lli1g"] +[ext_resource type="Texture2D" uid="uid://dqvg18aacx6db" path="res://resource/sprite/ui/mapEditorTools/Visible.png" id="8_gm7y5"] [sub_resource type="Animation" id="Animation_o3btm"] length = 0.001 @@ -144,7 +146,7 @@ scale = Vector2(4, 4) tile_set = ExtResource("2_vrg60") format = 2 -script = ExtResource("2_waq8f") +script = ExtResource("4_wbtsa") [node name="ErrorCell" type="Sprite2D" parent="Bg/VBoxContainer/HSplitContainer/Left/MarginContainer/MapView/SubViewport/TileMap"] visible = false @@ -172,3 +174,47 @@ layout_mode = 2 size_flags_horizontal = 3 size_flags_stretch_ratio = 3.0 + +[node name="MarginContainer" type="MarginContainer" parent="Bg/VBoxContainer/HSplitContainer/Right"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 2 + +[node name="TabContainer" type="TabContainer" parent="Bg/VBoxContainer/HSplitContainer/Right/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="MapLayer" type="MarginContainer" parent="Bg/VBoxContainer/HSplitContainer/Right/MarginContainer/TabContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 10 + +[node name="ScrollContainer" type="ScrollContainer" parent="Bg/VBoxContainer/HSplitContainer/Right/MarginContainer/TabContainer/MapLayer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="LayerButton" type="Button" parent="Bg/VBoxContainer/HSplitContainer/Right/MarginContainer/TabContainer/MapLayer/ScrollContainer"] +custom_minimum_size = Vector2(0, 70) +layout_mode = 2 +size_flags_horizontal = 3 +text = "layer1" +icon = ExtResource("7_lli1g") +alignment = 0 + +[node name="VisibleButton" type="TextureButton" parent="Bg/VBoxContainer/HSplitContainer/Right/MarginContainer/TabContainer/MapLayer/ScrollContainer/LayerButton"] +layout_mode = 1 +anchors_preset = 11 +anchor_left = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -55.0 +grow_horizontal = 0 +grow_vertical = 2 +texture_normal = ExtResource("8_gm7y5") +stretch_mode = 3 diff --git a/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_roomInfo.json b/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_roomInfo.json index ae58317..13d9a10 100644 --- a/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_roomInfo.json +++ b/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_roomInfo.json @@ -1 +1 @@ -{"Position":{"X":-3,"Y":-3},"Size":{"X":6,"Y":6},"DoorAreaInfos":[{"Direction":3,"Start":16,"End":-16},{"Direction":2,"Start":16,"End":-16},{"Direction":1,"Start":16,"End":-16},{"Direction":0,"Start":16,"End":-16}],"GroupName":"TestGroup1","RoomType":0,"RoomName":"Room2","Weight":100,"Remark":""} \ No newline at end of file +{"Position":{"X":-5,"Y":-5},"Size":{"X":13,"Y":12},"DoorAreaInfos":[{"Direction":3,"Start":0,"End":176},{"Direction":0,"Start":0,"End":160},{"Direction":2,"Start":0,"End":176},{"Direction":1,"Start":0,"End":160}],"GroupName":"TestGroup1","RoomType":0,"RoomName":"Room2","Weight":100,"Remark":""} \ No newline at end of file diff --git a/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_tileInfo.json b/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_tileInfo.json index 2e7fe73..7e35f85 100644 --- a/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_tileInfo.json +++ b/DungeonShooting_Godot/resource/map/tileMaps/TestGroup1/battle/Room2/Room2_tileInfo.json @@ -1 +1 @@ -{"NavigationList":[{"Type":0,"Points":[-24,-24,24,-24,24,32,-24,32]}],"Floor":[-1,-2,0,0,8,-1,-1,0,0,8,-1,0,0,0,8,-1,1,0,0,8,0,-2,0,0,8,0,-1,0,0,8,0,0,0,0,8,0,1,0,0,8,1,-2,0,0,8,1,-1,0,0,8,1,0,0,0,8,1,1,0,0,8,-2,-2,0,0,8,-2,-1,0,0,8,-2,0,0,0,8,-2,1,0,0,8],"Middle":[-2,-3,0,2,7,-1,-3,0,2,7,0,-3,0,2,7,1,-3,0,2,7],"Top":[-3,-3,0,3,4,-3,-2,0,3,3,-3,-1,0,3,3,-3,0,0,3,3,-3,1,0,3,3,-3,2,0,11,2,-2,2,0,2,2,-1,2,0,2,2,0,2,0,2,2,1,2,0,2,2,2,-3,0,1,4,2,-2,0,1,3,2,-1,0,1,3,2,0,0,1,3,2,1,0,1,3,2,2,0,13,2]} \ No newline at end of file +{"NavigationList":[{"Type":0,"Points":[-56,-56,104,-56,104,96,-56,96]}],"Floor":[0,5,0,0,8,0,4,0,0,8,0,3,0,0,8,0,2,0,0,8,0,-1,0,0,8,0,-2,0,0,8,0,-3,0,0,8,0,-4,0,0,8,0,1,0,0,8,0,0,0,0,8,1,5,0,0,8,1,4,0,0,8,1,3,0,0,8,1,2,0,0,8,1,-1,0,0,8,1,-2,0,0,8,1,-3,0,0,8,1,-4,0,0,8,1,1,0,0,8,1,0,0,0,8,-4,5,0,0,8,-4,4,0,0,8,-4,3,0,0,8,-4,2,0,0,8,-4,1,0,0,8,-4,0,0,0,8,-4,-1,0,0,8,-4,-2,0,0,8,-4,-3,0,0,8,-4,-4,0,0,8,-3,5,0,0,8,-3,4,0,0,8,-3,3,0,0,8,-3,2,0,0,8,-3,1,0,0,8,-3,0,0,0,8,-3,-1,0,0,8,-3,-2,0,0,8,-3,-3,0,0,8,-3,-4,0,0,8,-2,5,0,0,8,-2,4,0,0,8,-2,3,0,0,8,-2,2,0,0,8,-2,1,0,0,8,-2,0,0,0,8,-2,-1,0,0,8,-2,-2,0,0,8,-2,-3,0,0,8,-2,-4,0,0,8,-1,5,0,0,8,-1,4,0,0,8,-1,3,0,0,8,-1,2,0,0,8,-1,1,0,0,8,-1,0,0,0,8,-1,-1,0,0,8,-1,-2,0,0,8,-1,-3,0,0,8,-1,-4,0,0,8,2,5,0,0,8,2,4,0,0,8,2,3,0,0,8,2,2,0,0,8,2,1,0,0,8,2,0,0,0,8,2,-1,0,0,8,2,-2,0,0,8,2,-3,0,0,8,2,-4,0,0,8,3,5,0,0,8,3,4,0,0,8,3,3,0,0,8,3,2,0,0,8,3,1,0,0,8,3,0,0,0,8,3,-1,0,0,8,3,-2,0,0,8,3,-3,0,0,8,3,-4,0,0,8,4,5,0,0,8,4,4,0,0,8,4,3,0,0,8,4,2,0,0,8,4,1,0,0,8,4,0,0,0,8,4,-1,0,0,8,4,-2,0,0,8,4,-3,0,0,8,4,-4,0,0,8,5,5,0,0,8,5,4,0,0,8,5,3,0,0,8,5,2,0,0,8,5,1,0,0,8,5,0,0,0,8,5,-1,0,0,8,5,-2,0,0,8,5,-3,0,0,8,5,-4,0,0,8,6,5,0,0,8,6,4,0,0,8,6,3,0,0,8,6,2,0,0,8,6,1,0,0,8,6,0,0,0,8,6,-1,0,0,8,6,-2,0,0,8,6,-3,0,0,8,6,-4,0,0,8],"Middle":[-4,-5,0,2,7,-3,-5,0,2,7,-2,-5,0,2,7,-1,-5,0,2,7,0,-5,0,2,7,1,-5,0,2,7,2,-5,0,2,7,3,-5,0,2,7,4,-5,0,2,7,5,-5,0,2,7,6,-5,0,2,7],"Top":[-5,-5,0,3,4,-5,-4,0,3,3,-5,-3,0,3,3,-5,-2,0,3,3,-5,-1,0,3,3,-5,0,0,3,3,-5,1,0,3,3,-5,2,0,3,3,-5,3,0,3,3,-5,4,0,3,3,-5,5,0,3,3,-5,6,0,11,2,-4,6,0,2,2,-3,6,0,2,2,-2,6,0,2,2,-1,6,0,2,2,0,6,0,2,2,1,6,0,2,2,2,6,0,2,2,3,6,0,2,2,4,6,0,2,2,5,6,0,2,2,6,6,0,2,2,7,-5,0,1,4,7,-4,0,1,3,7,-3,0,1,3,7,-2,0,1,3,7,-1,0,1,3,7,0,0,1,3,7,1,0,1,3,7,2,0,1,3,7,3,0,1,3,7,4,0,1,3,7,5,0,1,3,7,6,0,13,2]} \ No newline at end of file diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Hide.png b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Hide.png new file mode 100644 index 0000000..1925305 --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Hide.png Binary files differ diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Hide.png.import b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Hide.png.import new file mode 100644 index 0000000..7ab915f --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Hide.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://tx8ynskqa5ad" +path="res://.godot/imported/Hide.png-9e29fe6e5d5256a8bf655e87bfb1c514.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resource/sprite/ui/mapEditorTools/Hide.png" +dest_files=["res://.godot/imported/Hide.png-9e29fe6e5d5256a8bf655e87bfb1c514.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Lock.png b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Lock.png new file mode 100644 index 0000000..40f033f --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Lock.png Binary files differ diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Lock.png.import b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Lock.png.import new file mode 100644 index 0000000..b614d76 --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Lock.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dmm8jw06bhffh" +path="res://.godot/imported/Lock.png-04701e8ff332d79831f8e7e3966494e3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resource/sprite/ui/mapEditorTools/Lock.png" +dest_files=["res://.godot/imported/Lock.png-04701e8ff332d79831f8e7e3966494e3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Unlock.png b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Unlock.png new file mode 100644 index 0000000..7244b37 --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Unlock.png Binary files differ diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Unlock.png.import b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Unlock.png.import new file mode 100644 index 0000000..107458b --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Unlock.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://jvcvtpvt2fid" +path="res://.godot/imported/Unlock.png-ddcb534918f7b36b0aebe029c72635c5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resource/sprite/ui/mapEditorTools/Unlock.png" +dest_files=["res://.godot/imported/Unlock.png-ddcb534918f7b36b0aebe029c72635c5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Visible.png b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Visible.png new file mode 100644 index 0000000..9bbd5cb --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Visible.png Binary files differ diff --git a/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Visible.png.import b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Visible.png.import new file mode 100644 index 0000000..4179b9e --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/ui/mapEditorTools/Visible.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqvg18aacx6db" +path="res://.godot/imported/Visible.png-2ddbc06e87cdb9bc38126fec565ee66d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resource/sprite/ui/mapEditorTools/Visible.png" +dest_files=["res://.godot/imported/Visible.png-2ddbc06e87cdb9bc38126fec565ee66d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/DungeonShooting_Godot/resource/theme/mainTheme.tres b/DungeonShooting_Godot/resource/theme/mainTheme.tres index b485bed..2983772 100644 --- a/DungeonShooting_Godot/resource/theme/mainTheme.tres +++ b/DungeonShooting_Godot/resource/theme/mainTheme.tres @@ -1,4 +1,4 @@ -[gd_resource type="Theme" load_steps=59 format=3 uid="uid://ds668te2rph30"] +[gd_resource type="Theme" load_steps=60 format=3 uid="uid://ds668te2rph30"] [ext_resource type="FontFile" uid="uid://cad0in7dtweo5" path="res://resource/font/VonwaonBitmap-16px.ttf" id="1_1e6k7"] @@ -378,6 +378,23 @@ border_width_bottom = 2 border_color = Color(0.188235, 0.188235, 0.188235, 1) +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kt3n1"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(1, 1, 1, 0.75) +draw_center = false +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +corner_detail = 5 +expand_margin_left = 2.0 +expand_margin_top = 2.0 +expand_margin_right = 2.0 +expand_margin_bottom = 2.0 + [resource] default_font = ExtResource("1_1e6k7") default_font_size = 32 @@ -489,3 +506,4 @@ TextEdit/styles/normal = SubResource("StyleBoxFlat_0jpwx") TextEdit/styles/read_only = SubResource("StyleBoxFlat_c3u8l") TooltipLabel/font_sizes/font_size = 32 +Tree/styles/focus = SubResource("StyleBoxFlat_kt3n1") diff --git a/DungeonShooting_Godot/src/framework/ui/UiNode.cs b/DungeonShooting_Godot/src/framework/ui/UiNode.cs index 1ed8c8b..5213ae2 100644 --- a/DungeonShooting_Godot/src/framework/ui/UiNode.cs +++ b/DungeonShooting_Godot/src/framework/ui/UiNode.cs @@ -68,6 +68,16 @@ return (T)OpenNestedUi(uiName); } + /// + /// 克隆当前节点, 并放到同父节点下 + /// + public TCloneType CloneAndPut() + { + var inst = Clone(); + Instance.GetParent().AddChild(inst.GetUiInstance()); + return inst; + } + public Node GetUiInstance() { return Instance; diff --git a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs index b8caa60..db5793c 100644 --- a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs +++ b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs @@ -183,7 +183,12 @@ public const string resource_sprite_ui_mapEditorTools_DoorDragButton_hover_png = "res://resource/sprite/ui/mapEditorTools/DoorDragButton_hover.png"; public const string resource_sprite_ui_mapEditorTools_DoorTool_png = "res://resource/sprite/ui/mapEditorTools/DoorTool.png"; public const string resource_sprite_ui_mapEditorTools_DragTool_png = "res://resource/sprite/ui/mapEditorTools/DragTool.png"; + public const string resource_sprite_ui_mapEditorTools_Hide_png = "res://resource/sprite/ui/mapEditorTools/Hide.png"; + public const string resource_sprite_ui_mapEditorTools_Lock_png = "res://resource/sprite/ui/mapEditorTools/Lock.png"; public const string resource_sprite_ui_mapEditorTools_PenTool_png = "res://resource/sprite/ui/mapEditorTools/PenTool.png"; + public const string resource_sprite_ui_mapEditorTools_ToolSelect_png = "res://resource/sprite/ui/mapEditorTools/ToolSelect.png"; + public const string resource_sprite_ui_mapEditorTools_Unlock_png = "res://resource/sprite/ui/mapEditorTools/Unlock.png"; + public const string resource_sprite_ui_mapEditorTools_Visible_png = "res://resource/sprite/ui/mapEditorTools/Visible.png"; public const string resource_sprite_ui_roomUI_ChargeProgress_png = "res://resource/sprite/ui/roomUI/ChargeProgress.png"; public const string resource_sprite_ui_roomUI_ChargeProgressBar_png = "res://resource/sprite/ui/roomUI/ChargeProgressBar.png"; public const string resource_sprite_ui_roomUI_Cooldown_png = "res://resource/sprite/ui/roomUI/Cooldown.png"; diff --git a/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditor.cs b/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditor.cs index 0cfd5e5..100a7f3 100644 --- a/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditor.cs +++ b/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditor.cs @@ -278,10 +278,142 @@ } /// + /// 类型: , 路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.ScrollContainer.LayerButton.VisibleButton + /// + public class VisibleButton : UiNode + { + public VisibleButton(MapEditor uiPanel, Godot.TextureButton node) : base(uiPanel, node) { } + public override VisibleButton Clone() => new (UiPanel, (Godot.TextureButton)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.ScrollContainer.LayerButton + /// + public class LayerButton : UiNode + { + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.ScrollContainer.VisibleButton + /// + public VisibleButton L_VisibleButton + { + get + { + if (_L_VisibleButton == null) _L_VisibleButton = new VisibleButton(UiPanel, Instance.GetNodeOrNull("VisibleButton")); + return _L_VisibleButton; + } + } + private VisibleButton _L_VisibleButton; + + public LayerButton(MapEditor uiPanel, Godot.Button node) : base(uiPanel, node) { } + public override LayerButton Clone() => new (UiPanel, (Godot.Button)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.ScrollContainer + /// + public class ScrollContainer : UiNode + { + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.LayerButton + /// + public LayerButton L_LayerButton + { + get + { + if (_L_LayerButton == null) _L_LayerButton = new LayerButton(UiPanel, Instance.GetNodeOrNull("LayerButton")); + return _L_LayerButton; + } + } + private LayerButton _L_LayerButton; + + public ScrollContainer(MapEditor uiPanel, Godot.ScrollContainer node) : base(uiPanel, node) { } + public override ScrollContainer Clone() => new (UiPanel, (Godot.ScrollContainer)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer + /// + public class MapLayer : UiNode + { + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.ScrollContainer + /// + public ScrollContainer L_ScrollContainer + { + get + { + if (_L_ScrollContainer == null) _L_ScrollContainer = new ScrollContainer(UiPanel, Instance.GetNodeOrNull("ScrollContainer")); + return _L_ScrollContainer; + } + } + private ScrollContainer _L_ScrollContainer; + + public MapLayer(MapEditor uiPanel, Godot.MarginContainer node) : base(uiPanel, node) { } + public override MapLayer Clone() => new (UiPanel, (Godot.MarginContainer)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer + /// + public class TabContainer : UiNode + { + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.MapLayer + /// + public MapLayer L_MapLayer + { + get + { + if (_L_MapLayer == null) _L_MapLayer = new MapLayer(UiPanel, Instance.GetNodeOrNull("MapLayer")); + return _L_MapLayer; + } + } + private MapLayer _L_MapLayer; + + public TabContainer(MapEditor uiPanel, Godot.TabContainer node) : base(uiPanel, node) { } + public override TabContainer Clone() => new (UiPanel, (Godot.TabContainer)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer + /// + public class MarginContainer_1 : UiNode + { + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.TabContainer + /// + public TabContainer L_TabContainer + { + get + { + if (_L_TabContainer == null) _L_TabContainer = new TabContainer(UiPanel, Instance.GetNodeOrNull("TabContainer")); + return _L_TabContainer; + } + } + private TabContainer _L_TabContainer; + + public MarginContainer_1(MapEditor uiPanel, Godot.MarginContainer node) : base(uiPanel, node) { } + public override MarginContainer_1 Clone() => new (UiPanel, (Godot.MarginContainer)Instance.Duplicate()); + } + + /// /// 类型: , 路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right /// public class Right : UiNode { + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.MarginContainer + /// + public MarginContainer_1 L_MarginContainer + { + get + { + if (_L_MarginContainer == null) _L_MarginContainer = new MarginContainer_1(UiPanel, Instance.GetNodeOrNull("MarginContainer")); + return _L_MarginContainer; + } + } + private MarginContainer_1 _L_MarginContainer; + public Right(MapEditor uiPanel, Godot.Panel node) : base(uiPanel, node) { } public override Right Clone() => new (UiPanel, (Godot.Panel)Instance.Duplicate()); } @@ -430,16 +562,36 @@ public MapView S_MapView => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Left.L_MarginContainer.L_MapView; /// - /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Left.MarginContainer - /// - public MarginContainer S_MarginContainer => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Left.L_MarginContainer; - - /// /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Left /// public Left S_Left => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Left; /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.ScrollContainer.LayerButton.VisibleButton + /// + public VisibleButton S_VisibleButton => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Right.L_MarginContainer.L_TabContainer.L_MapLayer.L_ScrollContainer.L_LayerButton.L_VisibleButton; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.ScrollContainer.LayerButton + /// + public LayerButton S_LayerButton => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Right.L_MarginContainer.L_TabContainer.L_MapLayer.L_ScrollContainer.L_LayerButton; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer.ScrollContainer + /// + public ScrollContainer S_ScrollContainer => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Right.L_MarginContainer.L_TabContainer.L_MapLayer.L_ScrollContainer; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer.MapLayer + /// + public MapLayer S_MapLayer => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Right.L_MarginContainer.L_TabContainer.L_MapLayer; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right.MarginContainer.TabContainer + /// + public TabContainer S_TabContainer => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Right.L_MarginContainer.L_TabContainer; + + /// /// 场景中唯一名称的节点, 节点类型: , 节点路径: MapEditor.Bg.VBoxContainer.HSplitContainer.Right /// public Right S_Right => L_Bg.L_VBoxContainer.L_HSplitContainer.L_Right; diff --git a/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditorPanel.cs b/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditorPanel.cs index b886c99..c380fbd 100644 --- a/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditorPanel.cs +++ b/DungeonShooting_Godot/src/game/ui/mapEditor/MapEditorPanel.cs @@ -6,10 +6,15 @@ public partial class MapEditorPanel : MapEditor { private EditorTileMapBar _editorTileMapBar; + private EditorLayerBar _editorLayerBar; public override void OnCreateUi() { + S_TabContainer.Instance.SetTabTitle(0, "地图"); + //S_MapLayer.Instance.Init(S_MapLayer); + _editorTileMapBar = new EditorTileMapBar(this, S_TileMap); + _editorLayerBar = new EditorLayerBar(this, S_MapLayer); } public override void OnShowUi() @@ -19,6 +24,7 @@ OnMapViewResized(); _editorTileMapBar.OnShow(); + _editorLayerBar.OnShow(); } public override void OnHideUi() @@ -26,11 +32,19 @@ S_Left.Instance.Resized -= OnMapViewResized; S_Back.Instance.Pressed -= OnBackClick; _editorTileMapBar.OnHide(); + _editorLayerBar.OnHide(); } + public override void OnDestroyUi() + { + _editorTileMapBar.OnDestroy(); + _editorLayerBar.OnDestroy(); + } + public override void Process(float delta) { _editorTileMapBar.Process(delta); + _editorLayerBar.Process(delta); } /// diff --git a/DungeonShooting_Godot/src/game/ui/mapEditor/TileView/EditorTileMapBar.cs b/DungeonShooting_Godot/src/game/ui/mapEditor/TileView/EditorTileMapBar.cs index bc50836..eb9b839 100644 --- a/DungeonShooting_Godot/src/game/ui/mapEditor/TileView/EditorTileMapBar.cs +++ b/DungeonShooting_Godot/src/game/ui/mapEditor/TileView/EditorTileMapBar.cs @@ -4,14 +4,12 @@ public class EditorTileMapBar { - private MapEditorPanel _editorPanel; private MapEditor.TileMap _editorTileMap; private EventFactory _eventFactory; public EditorTileMapBar(MapEditorPanel editorPanel, MapEditor.TileMap editorTileMap) { _editorTileMap = editorTileMap; - _editorPanel = editorPanel; _editorTileMap.Instance.MapEditorPanel = editorPanel; _editorTileMap.Instance.MapEditorToolsPanel = editorPanel.S_MapEditorTools.Instance; _editorTileMap.Instance.MapEditorToolsPanel.EditorMap = _editorTileMap; @@ -44,4 +42,9 @@ { _editorTileMap.Instance.DrawGuides(_editorTileMap.L_Brush.Instance); } + + public void OnDestroy() + { + + } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/ui/mapEditor/tabView/layerTab/EditorLayerBar.cs b/DungeonShooting_Godot/src/game/ui/mapEditor/tabView/layerTab/EditorLayerBar.cs new file mode 100644 index 0000000..a04dd28 --- /dev/null +++ b/DungeonShooting_Godot/src/game/ui/mapEditor/tabView/layerTab/EditorLayerBar.cs @@ -0,0 +1,69 @@ +using Godot; + +namespace UI.MapEditor; + +public class EditorLayerBar +{ + public class LayerButtonData + { + /// + /// 显示文本 + /// + public string Title; + /// + /// 是否锁定 + /// + public bool IsLock; + /// + /// Map层级 + /// + public int Layer; + + public LayerButtonData(string title, bool isLock, int layer) + { + Title = title; + IsLock = isLock; + Layer = layer; + } + } + + private readonly MapEditorPanel _mapEditorPanel; + private readonly MapEditor.MapLayer _mapLayer; + private readonly UiGrid _grid; + + public EditorLayerBar(MapEditorPanel mapEditorPanel, MapEditor.MapLayer mapLayer) + { + _mapEditorPanel = mapEditorPanel; + _mapLayer = mapLayer; + _grid = new UiGrid(mapLayer.L_ScrollContainer.L_LayerButton, typeof(LayerButtonCell)); + _grid.SetCellOffset(new Vector2I(0, 2)); + _grid.SetHorizontalExpand(true); + + _grid.Add(new LayerButtonData("地面", false, EditorTileMap.AutoFloorLayer)); + _grid.Add(new LayerButtonData("自定义底层", false, EditorTileMap.CustomFloorLayer)); + _grid.Add(new LayerButtonData("中层自动图块", true, EditorTileMap.AutoMiddleLayer)); + _grid.Add(new LayerButtonData("自定义中层", false, EditorTileMap.CustomMiddleLayer)); + _grid.Add(new LayerButtonData("高层自动图块", true, EditorTileMap.AutoTopLayer)); + _grid.Add(new LayerButtonData("自定义高层", false, EditorTileMap.CustomTopLayer)); + } + + public void OnShow() + { + + } + + public void OnHide() + { + + } + + public void OnDestroy() + { + _grid.Destroy(); + } + + public void Process(float delta) + { + + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/ui/mapEditor/tabView/layerTab/LayerButtonCell.cs b/DungeonShooting_Godot/src/game/ui/mapEditor/tabView/layerTab/LayerButtonCell.cs new file mode 100644 index 0000000..60a740f --- /dev/null +++ b/DungeonShooting_Godot/src/game/ui/mapEditor/tabView/layerTab/LayerButtonCell.cs @@ -0,0 +1,48 @@ +namespace UI.MapEditor; + +public class LayerButtonCell : UiCell +{ + private bool _visible; + + public override void OnInit() + { + CellNode.L_VisibleButton.Instance.Pressed += OnVisibleButtonClick; + } + + public override void OnSetData(EditorLayerBar.LayerButtonData data) + { + if (data.IsLock) + { + CellNode.Instance.Icon = ResourceManager.LoadTexture2D(ResourcePath.resource_sprite_ui_mapEditorTools_Lock_png); + } + else + { + CellNode.Instance.Icon = ResourceManager.LoadTexture2D(ResourcePath.resource_sprite_ui_mapEditorTools_Unlock_png); + } + + CellNode.Instance.Text = data.Title; + var panel = (MapEditorPanel)CellNode.UiPanel; + _visible = panel.S_TileMap.Instance.IsLayerEnabled(data.Layer); + SetVisibleIcon(_visible); + } + + private void OnVisibleButtonClick() + { + var panel = (MapEditorPanel)CellNode.UiPanel; + _visible = !_visible; + panel.S_TileMap.Instance.SetLayerEnabled(Data.Layer, _visible); + SetVisibleIcon(_visible); + } + + private void SetVisibleIcon(bool visible) + { + if (visible) + { + CellNode.L_VisibleButton.Instance.TextureNormal = ResourceManager.LoadTexture2D(ResourcePath.resource_sprite_ui_mapEditorTools_Visible_png); + } + else + { + CellNode.L_VisibleButton.Instance.TextureNormal = ResourceManager.LoadTexture2D(ResourcePath.resource_sprite_ui_mapEditorTools_Hide_png); + } + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/ui/mapEditor/tileView/EditorTileMap.cs b/DungeonShooting_Godot/src/game/ui/mapEditor/tileView/EditorTileMap.cs new file mode 100644 index 0000000..eea44cd --- /dev/null +++ b/DungeonShooting_Godot/src/game/ui/mapEditor/tileView/EditorTileMap.cs @@ -0,0 +1,925 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Godot; +using Godot.Collections; +using UI.MapEditorTools; + +namespace UI.MapEditor; + +public partial class EditorTileMap : TileMap +{ + + public enum MouseButtonType + { + /// + /// 无状态 + /// + None, + /// + /// 拖拽模式 + /// + Drag, + /// + /// 笔 + /// + Pen, + /// + /// 绘制区域模式 + /// + Area, + /// + /// 编辑门区域模式 + /// + Door, + } + + /// + /// 自动图块地板层 + /// + public const int AutoFloorLayer = 0; + /// + /// 自定义图块地板层 + /// + public const int CustomFloorLayer = 1; + /// + /// 自动图块中间层 + /// + public const int AutoMiddleLayer = 2; + /// + /// 自定义图块中间层 + /// + public const int CustomMiddleLayer = 3; + /// + /// 自动图块顶层 + /// + public const int AutoTopLayer = 4; + /// + /// 自定义图块顶层 + /// + public const int CustomTopLayer = 5; + + /// + /// 所属地图编辑器UI + /// + public MapEditorPanel MapEditorPanel { get; set; } + + /// + /// 编辑器工具UI + /// + public MapEditorToolsPanel MapEditorToolsPanel { get; set; } + + /// + /// 左键功能 + /// + public MouseButtonType MouseType { get; set; } = MouseButtonType.Pen; + + //鼠标坐标 + private Vector2 _mousePosition; + //鼠标所在的cell坐标 + private Vector2I _mouseCellPosition; + //上一帧鼠标所在的cell坐标 + private Vector2I _prevMouseCellPosition = new Vector2I(-99999, -99999); + //单次绘制是否改变过tile数据 + private bool _changeFlag = false; + //左键开始按下时鼠标所在的坐标 + private Vector2I _mouseStartCellPosition; + //鼠标中建是否按下 + private bool _isMiddlePressed = false; + private Vector2 _moveOffset; + //左键是否按下 + private bool _isLeftPressed = false; + //右键是否按下 + private bool _isRightPressed = false; + //绘制填充区域 + private bool _drawFullRect = false; + //负责存储自动图块数据 + private Grid _autoCellLayerGrid = new Grid(); + //用于生成导航网格 + private DungeonTileMap _dungeonTileMap; + //停止绘制多久后开始执行生成操作 + private float _generateInterval = 3f; + //生成自动图块和导航网格的计时器 + private float _generateTimer = -1; + //检测地形结果 + private bool _checkTerrainFlag = true; + //错误地形位置 + private Vector2I _checkTerrainErrorPosition = Vector2I.Zero; + //是否执行生成地形成功 + private bool _isGenerateTerrain = false; + private bool _initLayer = false; + + //--------- 配置数据 ------------- + private int _sourceId = 0; + private int _terrainSet = 0; + private int _terrain = 0; + private AutoTileConfig _autoTileConfig = new AutoTileConfig(); + + //原数据 + private DungeonRoomSplit _roomSplit; + + //变动过的数据 + + //地图位置, 单位: 格 + private Vector2I _roomPosition; + //地图大小, 单位: 格 + private Vector2I _roomSize; + private List _doorConfigs = new List(); + //------------------------------- + + public override void _Ready() + { + InitLayer(); + } + + public override void _Process(double delta) + { + var newDelta = (float)delta; + _drawFullRect = false; + var position = GetLocalMousePosition(); + _mouseCellPosition = LocalToMap(position); + _mousePosition = new Vector2( + _mouseCellPosition.X * GameConfig.TileCellSize, + _mouseCellPosition.Y * GameConfig.TileCellSize + ); + + if (!MapEditorToolsPanel.S_HBoxContainer.Instance.IsPositionOver(GetGlobalMousePosition())) //不在Ui节点上 + { + //左键绘制 + if (_isLeftPressed) + { + if (MouseType == MouseButtonType.Pen) //绘制单格 + { + if (_prevMouseCellPosition != _mouseCellPosition || !_changeFlag) //鼠标位置变过 + { + _changeFlag = true; + _prevMouseCellPosition = _mouseCellPosition; + //绘制自动图块 + SetSingleAutoCell(_mouseCellPosition); + } + } + else if (MouseType == MouseButtonType.Area) //绘制区域 + { + _drawFullRect = true; + } + else if (MouseType == MouseButtonType.Drag) //拖拽 + { + SetMapPosition(GetGlobalMousePosition() + _moveOffset); + } + } + else if (_isRightPressed) //右键擦除 + { + if (MouseType == MouseButtonType.Pen) //绘制单格 + { + if (_prevMouseCellPosition != _mouseCellPosition || !_changeFlag) //鼠标位置变过 + { + _changeFlag = true; + _prevMouseCellPosition = _mouseCellPosition; + EraseSingleAutoCell(_mouseCellPosition); + } + } + else if (MouseType == MouseButtonType.Area) //绘制区域 + { + _drawFullRect = true; + } + else if (MouseType == MouseButtonType.Drag) //拖拽 + { + SetMapPosition(GetGlobalMousePosition() + _moveOffset); + } + } + else if (_isMiddlePressed) //中键移动 + { + SetMapPosition(GetGlobalMousePosition() + _moveOffset); + } + } + + //绘制停止指定时间后, 生成导航网格 + if (_generateTimer > 0) + { + _generateTimer -= newDelta; + if (_generateTimer <= 0) + { + //计算区域 + CalcTileRect(false); + GD.Print("开始检测是否可以生成地形..."); + if (CheckTerrain()) + { + GD.Print("开始绘制导航网格..."); + if (GenerateNavigation()) + { + GD.Print("开始绘制自动贴图..."); + GenerateTerrain(); + _isGenerateTerrain = true; + } + } + else + { + SetErrorCell(_checkTerrainErrorPosition); + } + } + } + } + + /// + /// 绘制辅助线 + /// + public void DrawGuides(CanvasItem canvasItem) + { + //轴线 + canvasItem.DrawLine(new Vector2(0, 2000), new Vector2(0, -2000), Colors.Green); + canvasItem.DrawLine(new Vector2(2000, 0), new Vector2( -2000, 0), Colors.Red); + + //绘制房间区域 + if (_roomSize.X != 0 && _roomSize.Y != 0) + { + var size = TileSet.TileSize; + canvasItem.DrawRect(new Rect2(_roomPosition * size, _roomSize * size), + Colors.Aqua, false, 5f / Scale.X); + } + + if (_checkTerrainFlag) //已经通过地形检测 + { + //绘制导航网格 + var result = _dungeonTileMap.GetGenerateNavigationResult(); + if (result != null && result.Success) + { + var polygonData = _dungeonTileMap.GetPolygonData(); + Utils.DrawNavigationPolygon(canvasItem, polygonData, 3f / Scale.X); + } + } + + if (MouseType == MouseButtonType.Pen || MouseType == MouseButtonType.Area) + { + if (_drawFullRect) //绘制填充矩形 + { + var size = TileSet.TileSize; + var cellPos = _mouseStartCellPosition; + var temp = size; + if (_mouseStartCellPosition.X > _mouseCellPosition.X) + { + cellPos.X += 1; + temp.X -= size.X; + } + if (_mouseStartCellPosition.Y > _mouseCellPosition.Y) + { + cellPos.Y += 1; + temp.Y -= size.Y; + } + + var pos = cellPos * size; + canvasItem.DrawRect(new Rect2(pos, _mousePosition - pos + temp), Colors.White, false, 2f / Scale.X); + } + else //绘制单格 + { + canvasItem.DrawRect(new Rect2(_mousePosition, TileSet.TileSize), Colors.White, false, 2f / Scale.X); + } + } + } + + public override void _Input(InputEvent @event) + { + if (@event is InputEventMouseButton mouseButton) + { + if (mouseButton.ButtonIndex == MouseButton.Left) //左键 + { + if (mouseButton.Pressed) //按下 + { + _moveOffset = Position - GetGlobalMousePosition(); + _mouseStartCellPosition = LocalToMap(GetLocalMousePosition()); + } + else + { + _changeFlag = false; + if (_drawFullRect) //松开, 提交绘制的矩形区域 + { + SetRectAutoCell(_mouseStartCellPosition, _mouseCellPosition); + _drawFullRect = false; + } + } + + _isLeftPressed = mouseButton.Pressed; + } + else if (mouseButton.ButtonIndex == MouseButton.Right) //右键 + { + if (mouseButton.Pressed) //按下 + { + _moveOffset = Position - GetGlobalMousePosition(); + _mouseStartCellPosition = LocalToMap(GetLocalMousePosition()); + } + else + { + _changeFlag = false; + if (_drawFullRect) //松开, 提交擦除的矩形区域 + { + EraseRectAutoCell(_mouseStartCellPosition, _mouseCellPosition); + _drawFullRect = false; + } + } + + _isRightPressed = mouseButton.Pressed; + } + else if (mouseButton.ButtonIndex == MouseButton.WheelDown) + { + //缩小 + Shrink(); + } + else if (mouseButton.ButtonIndex == MouseButton.WheelUp) + { + //放大 + Magnify(); + } + else if (mouseButton.ButtonIndex == MouseButton.Middle) + { + _isMiddlePressed = mouseButton.Pressed; + if (_isMiddlePressed) + { + _moveOffset = Position - GetGlobalMousePosition(); + } + } + } + else if (@event is InputEventKey eventKey) + { + if (eventKey.Pressed && eventKey.Keycode == Key.M) + { + GD.Print("保存地牢房间数据..."); + TriggerSave(); + } + } + } + + //将指定层数据存入list中 + private void PushLayerDataToList(int layer, int sourceId, List list) + { + var layerArray = GetUsedCellsById(layer, sourceId); + foreach (var pos in layerArray) + { + var atlasCoords = GetCellAtlasCoords(layer, pos); + list.Add(pos.X); + list.Add(pos.Y); + list.Add(_sourceId); + list.Add(atlasCoords.X); + list.Add(atlasCoords.Y); + } + } + + private void SetLayerDataFromList(int layer, List list) + { + for (var i = 0; i < list.Count; i += 5) + { + var pos = new Vector2I(list[i], list[i + 1]); + var sourceId = list[i + 2]; + var atlasCoords = new Vector2I(list[i + 3], list[i + 4]); + SetCell(layer, pos, sourceId, atlasCoords); + if (layer == AutoFloorLayer) + { + _autoCellLayerGrid.Set(pos, true); + } + } + } + + //保存地牢 + private void TriggerSave() + { + SaveRoomInfoConfig(); + SaveTileInfoConfig(); + } + + /// + /// 加载地牢, 返回是否加载成功 + /// + public bool Load(DungeonRoomSplit roomSplit) + { + _roomSplit = roomSplit; + var roomInfo = roomSplit.RoomInfo; + var tileInfo = roomSplit.TileInfo; + + _roomPosition = roomInfo.Position.AsVector2I(); + SetMapSize(roomInfo.Size.AsVector2I(), true); + _doorConfigs.Clear(); + foreach (var doorAreaInfo in roomInfo.DoorAreaInfos) + { + _doorConfigs.Add(doorAreaInfo.Clone()); + } + + //初始化层级数据 + InitLayer(); + + //地块数据 + SetLayerDataFromList(AutoFloorLayer, tileInfo.Floor); + SetLayerDataFromList(AutoMiddleLayer, tileInfo.Middle); + SetLayerDataFromList(AutoTopLayer, tileInfo.Top); + + //导航网格数据 + _dungeonTileMap.SetPolygonData(tileInfo.NavigationList); + + //聚焦 + //MapEditorPanel.CallDelay(0.1f, OnClickCenterTool); + //CallDeferred(nameof(OnClickCenterTool), null); + + //加载门编辑区域 + foreach (var doorAreaInfo in _doorConfigs) + { + MapEditorToolsPanel.CreateDoorTool(doorAreaInfo); + } + return true; + } + + private void InitLayer() + { + if (_initLayer) + { + return; + } + + _initLayer = true; + //初始化层级数据 + AddLayer(CustomFloorLayer); + SetLayerZIndex(CustomFloorLayer, CustomFloorLayer); + AddLayer(AutoMiddleLayer); + SetLayerZIndex(AutoMiddleLayer, AutoMiddleLayer); + AddLayer(CustomMiddleLayer); + SetLayerZIndex(CustomMiddleLayer, CustomMiddleLayer); + AddLayer(AutoTopLayer); + SetLayerZIndex(AutoTopLayer, AutoTopLayer); + AddLayer(CustomTopLayer); + SetLayerZIndex(CustomTopLayer, CustomTopLayer); + + _dungeonTileMap = new DungeonTileMap(this); + _dungeonTileMap.SetFloorAtlasCoords(new List(new []{ _autoTileConfig.Floor.AutoTileCoord })); + } + + //缩小 + private void Shrink() + { + var pos = GetLocalMousePosition(); + var scale = Scale / 1.1f; + if (scale.LengthSquared() >= 0.5f) + { + Scale = scale; + SetMapPosition(Position + pos * 0.1f * scale); + } + else + { + GD.Print("太小了"); + } + } + //放大 + private void Magnify() + { + var pos = GetLocalMousePosition(); + var prevScale = Scale; + var scale = prevScale * 1.1f; + if (scale.LengthSquared() <= 2000) + { + Scale = scale; + SetMapPosition(Position - pos * 0.1f * prevScale); + } + else + { + GD.Print("太大了"); + } + } + + //绘制单个自动贴图 + private void SetSingleAutoCell(Vector2I position) + { + SetCell(GetFloorLayer(), position, _sourceId, _autoTileConfig.Floor.AutoTileCoord); + if (!_autoCellLayerGrid.Contains(position.X, position.Y)) + { + ResetGenerateTimer(); + _autoCellLayerGrid.Set(position.X, position.Y, true); + } + } + + //绘制区域自动贴图 + private void SetRectAutoCell(Vector2I start, Vector2I end) + { + ResetGenerateTimer(); + + if (start.X > end.X) + { + var temp = end.X; + end.X = start.X; + start.X = temp; + } + if (start.Y > end.Y) + { + var temp = end.Y; + end.Y = start.Y; + start.Y = temp; + } + + var width = end.X - start.X + 1; + var height = end.Y - start.Y + 1; + for (var i = 0; i < width; i++) + { + for (var j = 0; j < height; j++) + { + SetCell(GetFloorLayer(), new Vector2I(start.X + i, start.Y + j), _sourceId, _autoTileConfig.Floor.AutoTileCoord); + } + } + + _autoCellLayerGrid.SetRect(start, new Vector2I(width, height), true); + } + + //擦除单个自动图块 + private void EraseSingleAutoCell(Vector2I position) + { + EraseCell(GetFloorLayer(), position); + if (_autoCellLayerGrid.Remove(position.X, position.Y)) + { + ResetGenerateTimer(); + } + } + + //擦除一个区域内的自动贴图 + private void EraseRectAutoCell(Vector2I start, Vector2I end) + { + ResetGenerateTimer(); + + if (start.X > end.X) + { + var temp = end.X; + end.X = start.X; + start.X = temp; + } + if (start.Y > end.Y) + { + var temp = end.Y; + end.Y = start.Y; + start.Y = temp; + } + + var width = end.X - start.X + 1; + var height = end.Y - start.Y + 1; + for (var i = 0; i < width; i++) + { + for (var j = 0; j < height; j++) + { + EraseCell(GetFloorLayer(), new Vector2I(start.X + i, start.Y + j)); + } + } + _autoCellLayerGrid.RemoveRect(start, new Vector2I(width, height)); + } + + //重置计时器 + private void ResetGenerateTimer() + { + _generateTimer = _generateInterval; + _isGenerateTerrain = false; + _dungeonTileMap.ClearPolygonData(); + ClearLayer(AutoTopLayer); + ClearLayer(AutoMiddleLayer); + } + + //重新计算房间区域 + private void CalcTileRect(bool refreshDoorTrans) + { + var rect = GetUsedRect(); + _roomPosition = rect.Position; + SetMapSize(rect.Size, refreshDoorTrans); + } + + //检测是否有不合规的图块, 返回true表示图块正常 + private bool CheckTerrain() + { + var x = _roomPosition.X; + var y = _roomPosition.Y; + var w = _roomSize.X; + var h = _roomSize.Y; + + for (var i = 0; i < w; i++) + { + for (var j = 0; j < h; j++) + { + var pos = new Vector2I(x + i, y + j); + if (GetCellSourceId(AutoFloorLayer, pos) == -1) + { + //先检测对边是否有地板 + if ((_autoCellLayerGrid.Get(pos.X - 1, pos.Y) && _autoCellLayerGrid.Get(pos.X + 1, pos.Y)) //left & right + || (_autoCellLayerGrid.Get(pos.X, pos.Y + 1) && _autoCellLayerGrid.Get(pos.X, pos.Y - 1))) //top & down + { + _checkTerrainFlag = false; + _checkTerrainErrorPosition = pos; + return false; + } + + //再检测对角是否有地板 + var topLeft = _autoCellLayerGrid.Get(pos.X - 1, pos.Y + 1); //top-left + var downRight = _autoCellLayerGrid.Get(pos.X + 1, pos.Y - 1); //down-right + var downLeft = _autoCellLayerGrid.Get(pos.X - 1, pos.Y - 1); //down-left + var topRight = _autoCellLayerGrid.Get(pos.X + 1, pos.Y + 1); //top-right + if ((topLeft && downRight && !downLeft && !topRight) || (!topLeft && !downRight && downLeft && topRight)) + { + _checkTerrainFlag = false; + _checkTerrainErrorPosition = pos; + return false; + } + } + } + } + + _checkTerrainFlag = true; + return true; + } + + //生成自动图块 (地形) + private void GenerateTerrain() + { + ClearLayer(AutoFloorLayer); + + var list = new List(); + _autoCellLayerGrid.ForEach((x, y, data) => + { + if (data) + { + list.Add(new Vector2I(x, y)); + } + }); + var arr = new Array(list); + //绘制自动图块 + SetCellsTerrainConnect(AutoFloorLayer, arr, _terrainSet, _terrain, false); + //计算区域 + CalcTileRect(true); + //将墙壁移动到指定层 + MoveTerrainCell(); + } + + //将自动生成的图块从 AutoFloorLayer 移动到指定图层中 + private void MoveTerrainCell() + { + ClearLayer(AutoTopLayer); + ClearLayer(AutoMiddleLayer); + + var x = _roomPosition.X; + var y = _roomPosition.Y; + var w = _roomSize.X; + var h = _roomSize.Y; + + for (var i = 0; i < w; i++) + { + for (var j = 0; j < h; j++) + { + var pos = new Vector2I(x + i, y + j); + if (!_autoCellLayerGrid.Contains(pos) && GetCellSourceId(AutoFloorLayer, pos) != -1) + { + var atlasCoords = GetCellAtlasCoords(AutoFloorLayer, pos); + var layer = _autoTileConfig.GetLayer(atlasCoords); + if (layer == GameConfig.MiddleMapLayer) + { + layer = AutoMiddleLayer; + } + else if (layer == GameConfig.TopMapLayer) + { + layer = AutoTopLayer; + } + else + { + GD.PrintErr($"异常图块: {pos}, 这个图块的图集坐标'{atlasCoords}'不属于'MiddleMapLayer'和'TopMapLayer'!"); + continue; + } + EraseCell(AutoFloorLayer, pos); + SetCell(layer, pos, _sourceId, atlasCoords); + } + } + } + } + + //生成导航网格 + private bool GenerateNavigation() + { + _dungeonTileMap.GenerateNavigationPolygon(AutoFloorLayer); + var result = _dungeonTileMap.GetGenerateNavigationResult(); + if (result.Success) + { + CloseErrorCell(); + } + else + { + SetErrorCell(result.Exception.Point); + } + + return result.Success; + } + + //设置显示的错误cell, 会标记上红色的闪烁动画 + private void SetErrorCell(Vector2I pos) + { + MapEditorPanel.S_ErrorCell.Instance.Position = pos * CellQuadrantSize; + MapEditorPanel.S_ErrorCellAnimationPlayer.Instance.Play(AnimatorNames.Show); + } + + //关闭显示的错误cell + private void CloseErrorCell() + { + MapEditorPanel.S_ErrorCellAnimationPlayer.Instance.Stop(); + } + + private int GetFloorLayer() + { + return AutoFloorLayer; + } + + private int GetMiddleLayer() + { + return AutoMiddleLayer; + } + + private int GetTopLayer() + { + return AutoTopLayer; + } + + /// + /// 选中拖拽功能 + /// + public void OnSelectHandTool(object arg) + { + MouseType = MouseButtonType.Drag; + } + + /// + /// 选中画笔攻击 + /// + public void OnSelectPenTool(object arg) + { + MouseType = MouseButtonType.Pen; + } + + /// + /// 选中绘制区域功能 + /// + public void OnSelectRectTool(object arg) + { + MouseType = MouseButtonType.Area; + } + + /// + /// 选择编辑门区域 + /// + public void OnSelectDoorTool(object arg) + { + MouseType = MouseButtonType.Door; + } + + /// + /// 聚焦 + /// + public void OnClickCenterTool(object arg) + { + var pos = MapEditorPanel.S_SubViewport.Instance.Size / 2; + if (_roomSize.X == 0 && _roomSize.Y == 0) //聚焦原点 + { + SetMapPosition(pos); + } + else //聚焦地图中心点 + { + SetMapPosition(pos - (_roomPosition + _roomSize / 2) * TileSet.TileSize * Scale); + } + } + + /// + /// 创建地牢房间门区域 + /// + /// 门方向 + /// 起始坐标, 单位: 像素 + /// 结束坐标, 单位: 像素 + public DoorAreaInfo CreateDoorArea(DoorDirection direction, int start, int end) + { + var doorAreaInfo = new DoorAreaInfo(); + doorAreaInfo.Direction = direction; + doorAreaInfo.Start = start; + doorAreaInfo.End = end; + //doorAreaInfo.CalcPosition(_roomPosition, _roomSize); + _doorConfigs.Add(doorAreaInfo); + return doorAreaInfo; + } + + /// + /// 检测门区域数据是否可以提交 + /// + /// 门方向 + /// 起始坐标, 单位: 像素 + /// 结束坐标, 单位: 像素 + /// + public bool CheckDoorArea(DoorDirection direction, int start, int end) + { + foreach (var item in _doorConfigs) + { + if (item.Direction == direction) + { + if (CheckValueCollision(item.Start, item.End, start, end)) + { + return false; + } + } + } + + return true; + } + + /// + /// 检测门区域数据是否可以提交 + /// + /// 需要检测的门 + /// 起始坐标, 单位: 像素 + /// 结束坐标, 单位: 像素 + public bool CheckDoorArea(DoorAreaInfo target, int start, int end) + { + foreach (var item in _doorConfigs) + { + if (item.Direction == target.Direction && item != target) + { + if (CheckValueCollision(item.Start, item.End, start, end)) + { + return false; + } + } + } + + return true; + } + + private bool CheckValueCollision(float o1, float o2, float h1, float h2) + { + var size = GameConfig.TileCellSize; + return !(h2 < o1 - 3 * size || o2 + 3 * size < h1); + } + + /// + /// 移除门区域数据 + /// + public void RemoveDoorArea(DoorAreaInfo doorAreaInfo) + { + _doorConfigs.Remove(doorAreaInfo); + } + + //保存房间配置 + private void SaveRoomInfoConfig() + { + //存入本地 + var roomInfo = _roomSplit.RoomInfo; + var path = MapProjectManager.GetConfigPath(roomInfo.GroupName,roomInfo.RoomType, roomInfo.RoomName); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + roomInfo.Position = new SerializeVector2(_roomPosition); + roomInfo.Size = new SerializeVector2(_roomSize); + roomInfo.DoorAreaInfos.Clear(); + roomInfo.DoorAreaInfos.AddRange(_doorConfigs); + + path += "/" + MapProjectManager.GetRoomInfoConfigName(roomInfo.RoomName); + var jsonStr = JsonSerializer.Serialize(roomInfo); + File.WriteAllText(path, jsonStr); + } + + //保存地块数据 + public void SaveTileInfoConfig() + { + //存入本地 + var roomInfo = _roomSplit.RoomInfo; + var path = MapProjectManager.GetConfigPath(roomInfo.GroupName,roomInfo.RoomType, roomInfo.RoomName); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + var tileInfo = _roomSplit.TileInfo; + tileInfo.NavigationList.Clear(); + tileInfo.NavigationList.AddRange(_dungeonTileMap.GetPolygonData()); + tileInfo.Floor.Clear(); + tileInfo.Middle.Clear(); + tileInfo.Top.Clear(); + + PushLayerDataToList(AutoFloorLayer, _sourceId, tileInfo.Floor); + PushLayerDataToList(AutoMiddleLayer, _sourceId, tileInfo.Middle); + PushLayerDataToList(AutoTopLayer, _sourceId, tileInfo.Top); + + path += "/" + MapProjectManager.GetTileInfoConfigName(roomInfo.RoomName); + var jsonStr = JsonSerializer.Serialize(tileInfo); + File.WriteAllText(path, jsonStr); + } + + //设置地图坐标 + private void SetMapPosition(Vector2 pos) + { + Position = pos; + MapEditorToolsPanel.SetDoorToolTransform(pos, Scale); + } + + //设置地图大小 + private void SetMapSize(Vector2I size, bool refreshDoorTrans) + { + if (_roomSize != size) + { + _roomSize = size; + + if (refreshDoorTrans) + { + MapEditorToolsPanel.SetDoorHoverToolTransform(_roomPosition, _roomSize); + } + } + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/ui/mapEditor/tileView/EditorTileMapBar.cs b/DungeonShooting_Godot/src/game/ui/mapEditor/tileView/EditorTileMapBar.cs new file mode 100644 index 0000000..eb9b839 --- /dev/null +++ b/DungeonShooting_Godot/src/game/ui/mapEditor/tileView/EditorTileMapBar.cs @@ -0,0 +1,50 @@ +using Godot; + +namespace UI.MapEditor; + +public class EditorTileMapBar +{ + private MapEditor.TileMap _editorTileMap; + private EventFactory _eventFactory; + + public EditorTileMapBar(MapEditorPanel editorPanel, MapEditor.TileMap editorTileMap) + { + _editorTileMap = editorTileMap; + _editorTileMap.Instance.MapEditorPanel = editorPanel; + _editorTileMap.Instance.MapEditorToolsPanel = editorPanel.S_MapEditorTools.Instance; + _editorTileMap.Instance.MapEditorToolsPanel.EditorMap = _editorTileMap; + + _eventFactory = EventManager.CreateEventFactory(); + } + + public void OnShow() + { + _editorTileMap.L_Brush.Instance.Draw += OnDrawGuides; + _eventFactory.AddEventListener(EventEnum.OnSelectDragTool, _editorTileMap.Instance.OnSelectHandTool); + _eventFactory.AddEventListener(EventEnum.OnSelectPenTool, _editorTileMap.Instance.OnSelectPenTool); + _eventFactory.AddEventListener(EventEnum.OnSelectRectTool, _editorTileMap.Instance.OnSelectRectTool); + _eventFactory.AddEventListener(EventEnum.OnSelectDoorTool, _editorTileMap.Instance.OnSelectDoorTool); + _eventFactory.AddEventListener(EventEnum.OnClickCenterTool, _editorTileMap.Instance.OnClickCenterTool); + } + + public void OnHide() + { + _editorTileMap.L_Brush.Instance.Draw -= OnDrawGuides; + _eventFactory.RemoveAllEventListener(); + } + + public void Process(float delta) + { + _editorTileMap.L_Brush.Instance.QueueRedraw(); + } + + private void OnDrawGuides() + { + _editorTileMap.Instance.DrawGuides(_editorTileMap.L_Brush.Instance); + } + + public void OnDestroy() + { + + } +} \ No newline at end of file