diff --git a/DungeonShooting_Godot/prefab/ui/PauseMenu.tscn b/DungeonShooting_Godot/prefab/ui/PauseMenu.tscn new file mode 100644 index 0000000..421dd01 --- /dev/null +++ b/DungeonShooting_Godot/prefab/ui/PauseMenu.tscn @@ -0,0 +1,59 @@ +[gd_scene load_steps=3 format=3 uid="uid://bkq1wl66w3ais"] + +[ext_resource type="Script" path="res://src/game/ui/pauseMenu/PauseMenuPanel.cs" id="1_ef73i"] +[ext_resource type="Theme" uid="uid://drb1ajgvcih7p" path="res://resource/theme/theme1.tres" id="2_x1py5"] + +[node name="PauseMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1_ef73i") +Layer = 2 + +[node name="ColorRect" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.54902) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -64.0 +offset_top = -79.0 +offset_right = 64.0 +offset_bottom = 79.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Continue" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +focus_neighbor_top = NodePath("../Exit") +focus_neighbor_bottom = NodePath("../Setting") +theme = ExtResource("2_x1py5") +text = "继续" + +[node name="Restart" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +focus_neighbor_top = NodePath("../Exit") +focus_neighbor_bottom = NodePath("../Setting") +theme = ExtResource("2_x1py5") +text = "重新开始" + +[node name="Exit" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +focus_neighbor_top = NodePath("../Exit") +focus_neighbor_bottom = NodePath("../Setting") +theme = ExtResource("2_x1py5") +text = "退出游戏" diff --git a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs index c00452d..37575bb 100644 --- a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs +++ b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs @@ -251,7 +251,7 @@ if (_currWaveIndex < WaveList.Count) { GD.Print($"执行第{_currWaveIndex}波"); - _coroutineId = GameApplication.Instance.StartCoroutine(RunMark(WaveList[_currWaveIndex])); + _coroutineId = GameApplication.Instance.World.StartCoroutine(RunMark(WaveList[_currWaveIndex])); _currWaveIndex++; } } @@ -268,7 +268,7 @@ } GD.Print($"执行第{_currWaveIndex}波"); - _coroutineId = GameApplication.Instance.StartCoroutine(RunMark(WaveList[_currWaveIndex])); + _coroutineId = GameApplication.Instance.World.StartCoroutine(RunMark(WaveList[_currWaveIndex])); _currWaveIndex++; } diff --git a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs index 4600a54..1696169 100644 --- a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs +++ b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs @@ -254,6 +254,11 @@ GameCamera.Main.SetFollowTarget(null); BasisVelocity = Vector2.Zero; MoveController.ClearForce(); + + //暂停游戏 + GameApplication.Instance.World.Pause = true; + //弹出结算面板 + GameApplication.Instance.Cursor.SetGuiMode(true); UiManager.Open_Settlement(); } diff --git a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs index 26f4baa..e44024d 100644 --- a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs +++ b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs @@ -9,7 +9,6 @@ public const string excel_DungeonShooting_ExcelTool_deps_json = "res://excel/DungeonShooting_ExcelTool.deps.json"; public const string excel_DungeonShooting_ExcelTool_runtimeconfig_json = "res://excel/DungeonShooting_ExcelTool.runtimeconfig.json"; public const string prefab_Cursor_tscn = "res://prefab/Cursor.tscn"; - public const string prefab_FanCollisionShape_tscn = "res://prefab/FanCollisionShape.tscn"; public const string prefab_bullet_Bullet0001_tscn = "res://prefab/bullet/Bullet0001.tscn"; public const string prefab_bullet_Bullet0002_tscn = "res://prefab/bullet/Bullet0002.tscn"; public const string prefab_bullet_Bullet0003_tscn = "res://prefab/bullet/Bullet0003.tscn"; @@ -21,6 +20,7 @@ public const string prefab_effect_weapon_BulletSmoke_tscn = "res://prefab/effect/weapon/BulletSmoke.tscn"; public const string prefab_effect_weapon_FirePart_tscn = "res://prefab/effect/weapon/FirePart.tscn"; public const string prefab_effect_weapon_MeleeAttack1_tscn = "res://prefab/effect/weapon/MeleeAttack1.tscn"; + public const string prefab_effect_weapon_MeleeAttack2_tscn = "res://prefab/effect/weapon/MeleeAttack2.tscn"; public const string prefab_effect_weapon_ShotFire_tscn = "res://prefab/effect/weapon/ShotFire.tscn"; public const string prefab_map_RoomDoor_E_tscn = "res://prefab/map/RoomDoor_E.tscn"; public const string prefab_map_RoomDoor_N_tscn = "res://prefab/map/RoomDoor_N.tscn"; @@ -63,6 +63,7 @@ public const string prefab_ui_MapEditorProject_tscn = "res://prefab/ui/MapEditorProject.tscn"; public const string prefab_ui_MapEditorSelectObject_tscn = "res://prefab/ui/MapEditorSelectObject.tscn"; public const string prefab_ui_MapEditorTools_tscn = "res://prefab/ui/MapEditorTools.tscn"; + public const string prefab_ui_PauseMenu_tscn = "res://prefab/ui/PauseMenu.tscn"; public const string prefab_ui_RoomUI_tscn = "res://prefab/ui/RoomUI.tscn"; public const string prefab_ui_Settlement_tscn = "res://prefab/ui/Settlement.tscn"; public const string prefab_weapon_Weapon0001_tscn = "res://prefab/weapon/Weapon0001.tscn"; @@ -303,6 +304,7 @@ public const string scene_Main_tscn = "res://scene/Main.tscn"; public const string scene_World_tscn = "res://scene/World.tscn"; public const string scene_test_TestCommpont_tscn = "res://scene/test/TestCommpont.tscn"; + public const string scene_test_TestCreateSector_tscn = "res://scene/test/TestCreateSector.tscn"; public const string scene_test_TestExpression_tscn = "res://scene/test/TestExpression.tscn"; public const string scene_test_TestGenerateDungeon_tscn = "res://scene/test/TestGenerateDungeon.tscn"; public const string scene_test_TestNavigation2_tscn = "res://scene/test/TestNavigation2.tscn"; diff --git a/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs b/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs index 738391c..c64b660 100644 --- a/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs +++ b/DungeonShooting_Godot/src/game/manager/UiManager_Methods.cs @@ -22,6 +22,7 @@ public const string MapEditorProject = "MapEditorProject"; public const string MapEditorSelectObject = "MapEditorSelectObject"; public const string MapEditorTools = "MapEditorTools"; + public const string PauseMenu = "PauseMenu"; public const string RoomUI = "RoomUI"; public const string Settlement = "Settlement"; } @@ -795,6 +796,54 @@ } /// + /// 创建 PauseMenu, 并返回UI实例, 该函数不会打开 Ui + /// + public static UI.PauseMenu.PauseMenuPanel Create_PauseMenu() + { + return CreateUi(UiName.PauseMenu); + } + + /// + /// 打开 PauseMenu, 并返回UI实例 + /// + public static UI.PauseMenu.PauseMenuPanel Open_PauseMenu() + { + return OpenUi(UiName.PauseMenu); + } + + /// + /// 隐藏 PauseMenu 的所有实例 + /// + public static void Hide_PauseMenu() + { + var uiInstance = Get_PauseMenu_Instance(); + foreach (var uiPanel in uiInstance) + { + uiPanel.HideUi(); + } + } + + /// + /// 销毁 PauseMenu 的所有实例 + /// + public static void Destroy_PauseMenu() + { + var uiInstance = Get_PauseMenu_Instance(); + foreach (var uiPanel in uiInstance) + { + uiPanel.Destroy(); + } + } + + /// + /// 获取所有 PauseMenu 的实例, 如果没有实例, 则返回一个空数组 + /// + public static UI.PauseMenu.PauseMenuPanel[] Get_PauseMenu_Instance() + { + return GetUiInstance(nameof(UI.PauseMenu.PauseMenu)); + } + + /// /// 创建 RoomUI, 并返回UI实例, 该函数不会打开 Ui /// public static UI.RoomUI.RoomUIPanel Create_RoomUI() diff --git a/DungeonShooting_Godot/src/game/room/DungeonManager.cs b/DungeonShooting_Godot/src/game/room/DungeonManager.cs index f0b1bd7..c0b9834 100644 --- a/DungeonShooting_Godot/src/game/room/DungeonManager.cs +++ b/DungeonShooting_Godot/src/game/room/DungeonManager.cs @@ -72,7 +72,6 @@ GameApplication.Instance.StartCoroutine(RunLoadDungeonCoroutine(finish)); } - /// /// 重启地牢 /// @@ -152,6 +151,21 @@ { if (IsInDungeon) { + if (World.Pause) //已经暂停 + { + return; + } + + //暂停游戏 + if (Input.IsActionJustPressed("ui_cancel")) + { + World.Pause = true; + //鼠标改为Ui鼠标 + GameApplication.Instance.Cursor.SetGuiMode(true); + //打开暂停Ui + UiManager.Open_PauseMenu(); + } + _checkEnemyTimer += (float)delta; if (_checkEnemyTimer >= 1) { diff --git a/DungeonShooting_Godot/src/game/room/World.cs b/DungeonShooting_Godot/src/game/room/World.cs index 622803c..a11e2fd 100644 --- a/DungeonShooting_Godot/src/game/room/World.cs +++ b/DungeonShooting_Godot/src/game/room/World.cs @@ -1,10 +1,11 @@ +using System.Collections; using System.Collections.Generic; using Godot; /// /// 游戏世界 /// -public partial class World : Node2D +public partial class World : Node2D, ICoroutine { /// /// //对象根节点 @@ -73,12 +74,22 @@ public Vector2 Enemy_FindTargetPosition { get; set; } private bool _pause = false; - + private List _coroutineList; + public override void _Ready() { TileRoot.YSortEnabled = false; } + public override void _Process(double delta) + { + //协程更新 + if (_coroutineList != null) + { + ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, (float)delta); + } + } + /// /// 获取指定层级根节点 /// @@ -95,4 +106,23 @@ return null; } + public long StartCoroutine(IEnumerator able) + { + return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able); + } + + public void StopCoroutine(long coroutineId) + { + ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId); + } + + public bool IsCoroutineOver(long coroutineId) + { + return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId); + } + + public void StopAllCoroutine() + { + ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList); + } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/ui/pauseMenu/PauseMenu.cs b/DungeonShooting_Godot/src/game/ui/pauseMenu/PauseMenu.cs new file mode 100644 index 0000000..256a892 --- /dev/null +++ b/DungeonShooting_Godot/src/game/ui/pauseMenu/PauseMenu.cs @@ -0,0 +1,154 @@ +namespace UI.PauseMenu; + +/// +/// Ui代码, 该类是根据ui场景自动生成的, 请不要手动编辑该类, 以免造成代码丢失 +/// +public abstract partial class PauseMenu : UiBase +{ + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: PauseMenu.ColorRect + /// + public ColorRect L_ColorRect + { + get + { + if (_L_ColorRect == null) _L_ColorRect = new ColorRect((PauseMenuPanel)this, GetNode("ColorRect")); + return _L_ColorRect; + } + } + private ColorRect _L_ColorRect; + + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: PauseMenu.VBoxContainer + /// + public VBoxContainer L_VBoxContainer + { + get + { + if (_L_VBoxContainer == null) _L_VBoxContainer = new VBoxContainer((PauseMenuPanel)this, GetNode("VBoxContainer")); + return _L_VBoxContainer; + } + } + private VBoxContainer _L_VBoxContainer; + + + public PauseMenu() : base(nameof(PauseMenu)) + { + } + + public sealed override void OnInitNestedUi() + { + + } + + /// + /// 类型: , 路径: PauseMenu.ColorRect + /// + public class ColorRect : UiNode + { + public ColorRect(PauseMenuPanel uiPanel, Godot.ColorRect node) : base(uiPanel, node) { } + public override ColorRect Clone() => new (UiPanel, (Godot.ColorRect)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: PauseMenu.VBoxContainer.Continue + /// + public class Continue : UiNode + { + public Continue(PauseMenuPanel uiPanel, Godot.Button node) : base(uiPanel, node) { } + public override Continue Clone() => new (UiPanel, (Godot.Button)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: PauseMenu.VBoxContainer.Restart + /// + public class Restart : UiNode + { + public Restart(PauseMenuPanel uiPanel, Godot.Button node) : base(uiPanel, node) { } + public override Restart Clone() => new (UiPanel, (Godot.Button)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: PauseMenu.VBoxContainer.Exit + /// + public class Exit : UiNode + { + public Exit(PauseMenuPanel uiPanel, Godot.Button node) : base(uiPanel, node) { } + public override Exit Clone() => new (UiPanel, (Godot.Button)Instance.Duplicate()); + } + + /// + /// 类型: , 路径: PauseMenu.VBoxContainer + /// + public class VBoxContainer : UiNode + { + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: PauseMenu.Continue + /// + public Continue L_Continue + { + get + { + if (_L_Continue == null) _L_Continue = new Continue(UiPanel, Instance.GetNode("Continue")); + return _L_Continue; + } + } + private Continue _L_Continue; + + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: PauseMenu.Restart + /// + public Restart L_Restart + { + get + { + if (_L_Restart == null) _L_Restart = new Restart(UiPanel, Instance.GetNode("Restart")); + return _L_Restart; + } + } + private Restart _L_Restart; + + /// + /// 使用 Instance 属性获取当前节点实例对象, 节点类型: , 节点路径: PauseMenu.Exit + /// + public Exit L_Exit + { + get + { + if (_L_Exit == null) _L_Exit = new Exit(UiPanel, Instance.GetNode("Exit")); + return _L_Exit; + } + } + private Exit _L_Exit; + + public VBoxContainer(PauseMenuPanel uiPanel, Godot.VBoxContainer node) : base(uiPanel, node) { } + public override VBoxContainer Clone() => new (UiPanel, (Godot.VBoxContainer)Instance.Duplicate()); + } + + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: PauseMenu.ColorRect + /// + public ColorRect S_ColorRect => L_ColorRect; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: PauseMenu.VBoxContainer.Continue + /// + public Continue S_Continue => L_VBoxContainer.L_Continue; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: PauseMenu.VBoxContainer.Restart + /// + public Restart S_Restart => L_VBoxContainer.L_Restart; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: PauseMenu.VBoxContainer.Exit + /// + public Exit S_Exit => L_VBoxContainer.L_Exit; + + /// + /// 场景中唯一名称的节点, 节点类型: , 节点路径: PauseMenu.VBoxContainer + /// + public VBoxContainer S_VBoxContainer => L_VBoxContainer; + +} diff --git a/DungeonShooting_Godot/src/game/ui/pauseMenu/PauseMenuPanel.cs b/DungeonShooting_Godot/src/game/ui/pauseMenu/PauseMenuPanel.cs new file mode 100644 index 0000000..19e06a1 --- /dev/null +++ b/DungeonShooting_Godot/src/game/ui/pauseMenu/PauseMenuPanel.cs @@ -0,0 +1,66 @@ +using Godot; + +namespace UI.PauseMenu; + +public partial class PauseMenuPanel : PauseMenu +{ + + public override void OnCreateUi() + { + S_Continue.Instance.Pressed += OnContinueClick; + S_Restart.Instance.Pressed += OnRestartClick; + S_Exit.Instance.Pressed += OnExitClick; + + if (GameApplication.Instance.DungeonManager.IsEditorMode) //在编辑器模式下打开的Ui + { + S_Exit.Instance.Text = "返回编辑器"; + } + } + + public override void Process(float delta) + { + if (Input.IsActionJustPressed("ui_cancel")) //返回游戏 + { + OnContinueClick(); + } + } + + //继续游戏 + private void OnContinueClick() + { + GameApplication.Instance.World.Pause = false; + GameApplication.Instance.Cursor.SetGuiMode(false); + Destroy(); + } + + //重新开始 + private void OnRestartClick() + { + Destroy(); + if (GameApplication.Instance.DungeonManager.IsEditorMode) //在编辑器模式下打开的Ui + { + EditorPlayManager.Restart(); + } + else //正常重新开始 + { + GameApplication.Instance.DungeonManager.RestartDungeon(GameApplication.Instance.DungeonConfig); + } + } + + //退出地牢 + private void OnExitClick() + { + Destroy(); + if (GameApplication.Instance.DungeonManager.IsEditorMode) //在编辑器模式下打开的Ui + { + EditorPlayManager.Exit(); + } + else //正常关闭Ui + { + GameApplication.Instance.DungeonManager.ExitDungeon(() => + { + UiManager.Open_Main(); + }); + } + } +}