From dd2577cfd068f7045e823351a004a315d1100635 Mon Sep 17 00:00:00 2001 From: Cold-Mint Date: Sun, 19 May 2024 22:32:34 +0800 Subject: [PATCH] =?UTF-8?q?Add=20sequential=20layout=20diagram=20parsing?= =?UTF-8?q?=20policies.=20=E6=B7=BB=E5=8A=A0=E9=A1=BA=E5=BA=8F=E7=9A=84?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E5=9B=BE=E8=A7=A3=E6=9E=90=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/levelGraphs/test.json | 83 +++++++ scripts/loader/sceneLoader/GameSceneLoader.cs | 5 +- .../loader/sceneLoader/SceneLoaderTemplate.cs | 2 +- .../ILayoutParsingStrategy.cs | 2 +- .../SequenceLayoutParsingStrategy.cs | 215 ++++++++++++++++++ scripts/map/MapGenerator.cs | 5 +- .../{ => layoutStrategy}/ILayoutStrategy.cs | 4 +- .../map/layoutStrategy/TestLayoutStrategy.cs | 32 +++ scripts/serialization/JsonSerialization.cs | 12 + 9 files changed, 354 insertions(+), 6 deletions(-) create mode 100644 data/levelGraphs/test.json rename scripts/map/{ => LayoutParsingStrategy}/ILayoutParsingStrategy.cs (95%) create mode 100644 scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs rename scripts/map/{ => layoutStrategy}/ILayoutStrategy.cs (70%) create mode 100644 scripts/map/layoutStrategy/TestLayoutStrategy.cs diff --git a/data/levelGraphs/test.json b/data/levelGraphs/test.json new file mode 100644 index 0000000..556d872 --- /dev/null +++ b/data/levelGraphs/test.json @@ -0,0 +1,83 @@ +{ + "ConnectionDataList": [ + { + "FromId": "c0255eb6-2c75-44f7-9058-0921fe8fb0d8", + "ToId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03", + "FromPort": 0, + "ToPort": 0 + }, + { + "FromId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03", + "ToId": "c604e1ef-f3e7-4189-a6f7-ab2bd23d5130", + "FromPort": 0, + "ToPort": 0 + }, + { + "FromId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03", + "ToId": "bb9da3f9-911f-465f-af2e-be1325f17d15", + "FromPort": 0, + "ToPort": 0 + }, + { + "FromId": "c604e1ef-f3e7-4189-a6f7-ab2bd23d5130", + "ToId": "c9e860b9-e001-4b9b-9e6d-adf132beb64f", + "FromPort": 0, + "ToPort": 0 + }, + { + "FromId": "bb9da3f9-911f-465f-af2e-be1325f17d15", + "ToId": "c9e860b9-e001-4b9b-9e6d-adf132beb64f", + "FromPort": 0, + "ToPort": 0 + } + ], + "RoomNodeDataList": [ + { + "Id": "c0255eb6-2c75-44f7-9058-0921fe8fb0d8", + "Title": "起点房间", + "Description": "测试的起点房间。", + "RoomTemplateSet": [ + "res://prefab/roomTemplates/dungeon/initialRoom.tscn" + ], + "Tags": [ + "StartingRoom" + ] + }, + { + "Id": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03", + "Title": "大厅", + "Description": "比普通房间要大一些的房间,有多个门,用于连接到其他房间。", + "RoomTemplateSet": [ + "res://prefab/roomTemplates/dungeon/utilityRoom.tscn" + ], + "Tags": null + }, + { + "Id": "c604e1ef-f3e7-4189-a6f7-ab2bd23d5130", + "Title": "普通房间1", + "Description": "测试使用。", + "RoomTemplateSet": [ + "res://prefab/roomTemplates/dungeon" + ], + "Tags": null + }, + { + "Id": "bb9da3f9-911f-465f-af2e-be1325f17d15", + "Title": "普通房间2", + "Description": "测试使用。", + "RoomTemplateSet": [ + "res://prefab/roomTemplates/dungeon" + ], + "Tags": null + }, + { + "Id": "c9e860b9-e001-4b9b-9e6d-adf132beb64f", + "Title": "Boos房间", + "Description": "关卡结束的地方。", + "RoomTemplateSet": [ + "res://prefab/roomTemplates/dungeon" + ], + "Tags": null + } + ] +} \ No newline at end of file diff --git a/scripts/loader/sceneLoader/GameSceneLoader.cs b/scripts/loader/sceneLoader/GameSceneLoader.cs index 549064e..66dd8cf 100644 --- a/scripts/loader/sceneLoader/GameSceneLoader.cs +++ b/scripts/loader/sceneLoader/GameSceneLoader.cs @@ -3,6 +3,8 @@ using ColdMint.scripts.character; using ColdMint.scripts.debug; using ColdMint.scripts.inventory; using ColdMint.scripts.map; +using ColdMint.scripts.map.LayoutParsingStrategy; +using ColdMint.scripts.map.layoutStrategy; using ColdMint.scripts.map.room; using ColdMint.scripts.map.roomHolder; using ColdMint.scripts.map.RoomPlacer; @@ -34,6 +36,7 @@ public partial class GameSceneLoader : SceneLoaderTemplate public override async Task LoadScene() { - + MapGenerator.LayoutStrategy = new TestLayoutStrategy(); + MapGenerator.LayoutParsingStrategy = new SequenceLayoutParsingStrategy(); } } \ No newline at end of file diff --git a/scripts/loader/sceneLoader/SceneLoaderTemplate.cs b/scripts/loader/sceneLoader/SceneLoaderTemplate.cs index 835cfa5..6eba2aa 100644 --- a/scripts/loader/sceneLoader/SceneLoaderTemplate.cs +++ b/scripts/loader/sceneLoader/SceneLoaderTemplate.cs @@ -4,7 +4,7 @@ using Godot; namespace ColdMint.scripts.loader.sceneLoader; /// -/// 场景加载器模板 +/// The scene loader template /// 场景加载器模板 /// public partial class SceneLoaderTemplate : Node2D, ISceneLoaderContract diff --git a/scripts/map/ILayoutParsingStrategy.cs b/scripts/map/LayoutParsingStrategy/ILayoutParsingStrategy.cs similarity index 95% rename from scripts/map/ILayoutParsingStrategy.cs rename to scripts/map/LayoutParsingStrategy/ILayoutParsingStrategy.cs index 62c53a3..78cee3f 100644 --- a/scripts/map/ILayoutParsingStrategy.cs +++ b/scripts/map/LayoutParsingStrategy/ILayoutParsingStrategy.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using ColdMint.scripts.levelGraphEditor; -namespace ColdMint.scripts.map; +namespace ColdMint.scripts.map.LayoutParsingStrategy; /// /// Layout parsing strategy diff --git a/scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs b/scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs new file mode 100644 index 0000000..73b9a2e --- /dev/null +++ b/scripts/map/LayoutParsingStrategy/SequenceLayoutParsingStrategy.cs @@ -0,0 +1,215 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using ColdMint.scripts.levelGraphEditor; + +namespace ColdMint.scripts.map.LayoutParsingStrategy; + +/// +/// Sequential layout diagram parsing strategy +/// 顺序的布局图解析策略 +/// +/// +///Traverse from the start element to the end element according to the ConnectionDataList. This policy considers the first element of the ConnectionDataList to be the fixed starting room information. +///按照ConnectionDataList从起始元素遍历到末项元素。本策略认为ConnectionDataList的首个元素为固定的起始房间信息。 +/// +/// This policy's Next() method does not return rooms that are not connected except for the start room +/// 本策略Next()方法不会返回除了起始房间外,没有连接的房间 +/// +public class SequenceLayoutParsingStrategy : ILayoutParsingStrategy +{ + private LevelGraphEditorSaveData? _levelGraphEditorSaveData; + + //Check whether data Settings are valid + //设置数据时,是否检查合法了 + private bool _checkLegality; + + //A special marker for the starting room. + //特殊标记,代表起始房间。 + private const int StartingRoomIndex = -1; + + //The connection index of the query + //查询的连接索引 + private int _index = StartingRoomIndex; + private int _maxIndex; + private Dictionary _roomNodeDataDictionary = new Dictionary(); + + public void SetLevelGraph(LevelGraphEditorSaveData levelGraphEditorSaveData) + { + _index = StartingRoomIndex; + _levelGraphEditorSaveData = levelGraphEditorSaveData; + if (_levelGraphEditorSaveData.RoomNodeDataList == null || _levelGraphEditorSaveData.RoomNodeDataList.Count == 0) + { + //No room data, unable to parse. + //没有房间数据,无法解析。 + return; + } + + if (_levelGraphEditorSaveData.ConnectionDataList == null || + _levelGraphEditorSaveData.ConnectionDataList.Count == 0) + { + _maxIndex = 0; + } + else + { + _maxIndex = _levelGraphEditorSaveData.ConnectionDataList.Count - 1; + } + + _roomNodeDataDictionary.Clear(); + foreach (var roomNodeData in _levelGraphEditorSaveData.RoomNodeDataList) + { + if (roomNodeData.Id == null) + { + continue; + } + + _roomNodeDataDictionary.Add(roomNodeData.Id, roomNodeData); + } + + //Check that the first room is the starting room. + //检查首个房间是否为起始房间。 + var firstRoom = GetFirstRoom(_levelGraphEditorSaveData); + if (firstRoom == null) + { + return; + } + + _checkLegality = firstRoom.HasTag(Config.RoomDataTag.StartingRoom); + } + + /// + /// Get the first room + /// 获取第一个房间 + /// + /// + /// + private RoomNodeData? GetFirstRoom(LevelGraphEditorSaveData? levelGraphEditorSaveData) + { + if (levelGraphEditorSaveData == null || levelGraphEditorSaveData.RoomNodeDataList == null || + levelGraphEditorSaveData.RoomNodeDataList.Count == 0) + { + return null; + } + + RoomNodeData? firstRoom = null; + if (levelGraphEditorSaveData.ConnectionDataList == null || + levelGraphEditorSaveData.ConnectionDataList.Count == 0) + { + //If there is no connection information, then fetch the first room in the RoomNodeDataList. + //如果没有连接信息,那么在RoomNodeDataList内取出第一个房间。 + firstRoom = levelGraphEditorSaveData.RoomNodeDataList[0]; + } + else + { + //If there is connection information, then fetch the first connected From room in the ConnectionDataList. + //如果有连接信息,那么在ConnectionDataList内取出第一个连接的From房间。 + var firstConnection = levelGraphEditorSaveData.ConnectionDataList[0]; + if (firstConnection.FromId == null) + { + return firstRoom; + } + + if (_roomNodeDataDictionary.TryGetValue(firstConnection.FromId, out var value)) + { + firstRoom = value; + } + } + + return firstRoom; + } + + public Task Next() + { + if (_index == StartingRoomIndex) + { + return Task.FromResult(GetFirstRoom(_levelGraphEditorSaveData)); + } + + var connectionData = GetIndexOfConnectionData(_index); + if (connectionData == null) + { + return Task.FromResult(null); + } + + if (connectionData.ToId == null) + { + return Task.FromResult(null); + } + + if (_roomNodeDataDictionary.TryGetValue(connectionData.ToId, out var value)) + { + return Task.FromResult(value); + } + + return Task.FromResult(null); + } + + /// + /// Gets connection information for the specified location + /// 获取指定位置的连接信息 + /// + /// + /// + private ConnectionData? GetIndexOfConnectionData(int index) + { + if (_levelGraphEditorSaveData == null) + { + return null; + } + + if (_levelGraphEditorSaveData.ConnectionDataList == null || + _levelGraphEditorSaveData.ConnectionDataList.Count == 0) + { + return null; + } + + return _levelGraphEditorSaveData.ConnectionDataList[index]; + } + + public Task GetNextParentNodeId() + { + if (_index == StartingRoomIndex) + { + //The start room will not have a parent node. + //起始房间不会有父节点。 + return Task.FromResult(null); + } + + var connectionData = GetIndexOfConnectionData(_index); + if (connectionData == null) + { + return Task.FromResult(null); + } + + if (connectionData.FromId == null) + { + return Task.FromResult(null); + } + + if (_roomNodeDataDictionary.ContainsKey(connectionData.FromId)) + { + return Task.FromResult(connectionData.FromId); + } + + return Task.FromResult(null); + } + + public Task HasNext() + { + if (!_checkLegality) + { + //If the check is not valid, then simply return false + //如果检查不合法,那么直接返回false + return Task.FromResult(false); + } + + if (_index == StartingRoomIndex) + { + //The start room is always considered to have the next room, in order to handle situations where levelGraphEditorSaveData has only room data and no connection data. + //起始房间始终被认为有下一个房间,这是为了处理levelGraphEditorSaveData仅有房间数据,没有连接数据的情况。 + _index++; + return Task.FromResult(true); + } + + return Task.FromResult(_index < _maxIndex); + } +} \ No newline at end of file diff --git a/scripts/map/MapGenerator.cs b/scripts/map/MapGenerator.cs index 86e2419..1fa5497 100644 --- a/scripts/map/MapGenerator.cs +++ b/scripts/map/MapGenerator.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using ColdMint.scripts.debug; using ColdMint.scripts.map.interfaces; +using ColdMint.scripts.map.LayoutParsingStrategy; +using ColdMint.scripts.map.layoutStrategy; namespace ColdMint.scripts.map; @@ -67,7 +69,8 @@ public static class MapGenerator //Get the layout data //拿到布局图数据 var levelGraphEditorSaveData = await _layoutStrategy.GetLayout(); - if (levelGraphEditorSaveData.RoomNodeDataList == null || levelGraphEditorSaveData.RoomNodeDataList.Count == 0) + if (levelGraphEditorSaveData == null || levelGraphEditorSaveData.RoomNodeDataList == null || + levelGraphEditorSaveData.RoomNodeDataList.Count == 0) { LogCat.LogError("map_generator_attempts_to_parse_empty_layout_diagrams"); return; diff --git a/scripts/map/ILayoutStrategy.cs b/scripts/map/layoutStrategy/ILayoutStrategy.cs similarity index 70% rename from scripts/map/ILayoutStrategy.cs rename to scripts/map/layoutStrategy/ILayoutStrategy.cs index 0bbe1d7..e38540c 100644 --- a/scripts/map/ILayoutStrategy.cs +++ b/scripts/map/layoutStrategy/ILayoutStrategy.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using ColdMint.scripts.levelGraphEditor; -namespace ColdMint.scripts.map; +namespace ColdMint.scripts.map.layoutStrategy; public interface ILayoutStrategy { @@ -10,5 +10,5 @@ public interface ILayoutStrategy /// 获取布局图 /// /// - public Task GetLayout(); + public Task GetLayout(); } \ No newline at end of file diff --git a/scripts/map/layoutStrategy/TestLayoutStrategy.cs b/scripts/map/layoutStrategy/TestLayoutStrategy.cs new file mode 100644 index 0000000..dad919e --- /dev/null +++ b/scripts/map/layoutStrategy/TestLayoutStrategy.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using ColdMint.scripts.levelGraphEditor; +using ColdMint.scripts.serialization; +using Godot; + +namespace ColdMint.scripts.map.layoutStrategy; + +/// +/// Layout selection strategy to use at test time +/// 测试时使用的布局选择策略 +/// +public class TestLayoutStrategy : ILayoutStrategy +{ + private string _path = "res://data/levelGraphs/test.json"; + + public Task GetLayout() + { + var exists = FileAccess.FileExists(_path); + if (!exists) + { + return Task.FromResult(null); + } + + var json = FileAccess.GetFileAsString(_path); + if (json == null) + { + return Task.FromResult(null); + } + + return Task.FromResult(JsonSerialization.Deserialize(json)); + } +} \ No newline at end of file diff --git a/scripts/serialization/JsonSerialization.cs b/scripts/serialization/JsonSerialization.cs index 4df6c4e..8dfa329 100644 --- a/scripts/serialization/JsonSerialization.cs +++ b/scripts/serialization/JsonSerialization.cs @@ -43,6 +43,18 @@ public static class JsonSerialization { return JsonSerializer.Serialize(obj, _options); } + + /// + /// Deserialize Json to an object + /// 将Json反序列化为对象 + /// + /// + /// + /// + public static T? Deserialize(string json) + { + return JsonSerializer.Deserialize(json, _options); + } public static async Task ReadJsonFileToObj(Stream openStream) {