Supports loading and saving hierarchy diagrams from json files now.

支持加载和保存层级图从json文件了。
This commit is contained in:
Cold-Mint 2024-05-16 22:46:03 +08:00
parent c9dd33b840
commit e0723ec261
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
11 changed files with 347 additions and 121 deletions

View File

@ -9,7 +9,7 @@ jump,跳跃,Jump,飛
throw,抛出,Throw a ,ほうしゅつ throw,抛出,Throw a ,ほうしゅつ
use_item,使用,Use,しよう use_item,使用,Use,しよう
jump_down,跳下平台,Jump off platform,踊り場から飛び降ります jump_down,跳下平台,Jump off platform,踊り場から飛び降ります
de,的,'s,の de,的,"'s",の
default_player_name,白纸,blankPaper,しらかみ default_player_name,白纸,blankPaper,しらかみ
item_prompt_debug,ID{0}\n名称{1}\n数量{2}\n最大叠加数量{3}\n数据类型{4}\n描述{5},ID: {0}\nName: {1}\nQuantity: {2}\nMaximum stacking quantity: {3}\nData type: {4}\nDescription{5},id:{0}\n名称:{1}\nの数は最大{2}\nシナジー数:{3}\nデータタイプ:{4}\n述べ表わす:{5} item_prompt_debug,ID{0}\n名称{1}\n数量{2}\n最大叠加数量{3}\n数据类型{4}\n描述{5},ID: {0}\nName: {1}\nQuantity: {2}\nMaximum stacking quantity: {3}\nData type: {4}\nDescription{5},id:{0}\n名称:{1}\nの数は最大{2}\nシナジー数:{3}\nデータタイプ:{4}\n述べ表わす:{5}
level_graph_editor,关卡图编辑器,Level graph editor,ステージマップエディター level_graph_editor,关卡图编辑器,Level graph editor,ステージマップエディター
@ -25,4 +25,5 @@ line_errors_must_start_with_res,位于{0}错误必须以res://开头。,"Loca
open_the_export_directory,打开导出目录,Open the export directory,エクスポートディレクトリを開きます open_the_export_directory,打开导出目录,Open the export directory,エクスポートディレクトリを開きます
save,保存,Save,保留 save,保存,Save,保留
filename,文件名,File name,ファイル名 filename,文件名,File name,ファイル名
cancel,取消,Cancel,キャンセル cancel,取消,Cancel,キャンセル
load,加载,Load,ろーど
1 id zh en jp
9 throw 抛出 Throw a ほうしゅつ
10 use_item 使用 Use しよう
11 jump_down 跳下平台 Jump off platform 踊り場から飛び降ります
12 de 's
13 default_player_name 白纸 blankPaper しらかみ
14 item_prompt_debug ID:{0}\n名称:{1}\n数量:{2}\n最大叠加数量:{3}\n数据类型:{4}\n描述:{5} ID: {0}\nName: {1}\nQuantity: {2}\nMaximum stacking quantity: {3}\nData type: {4}\nDescription:{5} id:{0}\n名称:{1}\nの数は最大{2}\nシナジー数:{3}\nデータタイプ:{4}\n述べ表わす:{5}
15 level_graph_editor 关卡图编辑器 Level graph editor ステージマップエディター
25 open_the_export_directory 打开导出目录 Open the export directory エクスポートディレクトリを開きます
26 save 保存 Save 保留
27 filename 文件名 File name ファイル名
28 cancel 取消 Cancel キャンセル
29 load 加载 Load ろーど

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -147,6 +147,7 @@ hotbar_previous={
[internationalization] [internationalization]
locale/translations=PackedStringArray("res://locals/UI.en.translation", "res://locals/UI.zh.translation", "res://locals/Error.zh.translation", "res://locals/Error.en.translation", "res://locals/slogan.en.translation", "res://locals/slogan.zh.translation", "res://locals/Log.en.translation", "res://locals/Log.zh.translation", "res://locals/Weapon.en.translation", "res://locals/Weapon.zh.translation", "res://locals/InputMapping.en.translation", "res://locals/InputMapping.zh.translation", "res://locals/Error.jp.translation", "res://locals/InputMapping.jp.translation", "res://locals/Log.jp.translation", "res://locals/slogan.jp.translation", "res://locals/UI.jp.translation", "res://locals/Weapon.jp.translation") locale/translations=PackedStringArray("res://locals/UI.en.translation", "res://locals/UI.zh.translation", "res://locals/Error.zh.translation", "res://locals/Error.en.translation", "res://locals/slogan.en.translation", "res://locals/slogan.zh.translation", "res://locals/Log.en.translation", "res://locals/Log.zh.translation", "res://locals/Weapon.en.translation", "res://locals/Weapon.zh.translation", "res://locals/InputMapping.en.translation", "res://locals/InputMapping.zh.translation", "res://locals/Error.jp.translation", "res://locals/InputMapping.jp.translation", "res://locals/Log.jp.translation", "res://locals/slogan.jp.translation", "res://locals/UI.jp.translation", "res://locals/Weapon.jp.translation")
locale/test="jp"
[layer_names] [layer_names]

View File

@ -156,10 +156,14 @@ grow_horizontal = 0
layout_mode = 2 layout_mode = 2
text = "open_the_export_directory" text = "open_the_export_directory"
[node name="SaveButton" type="Button" parent="HBoxContainer"] [node name="ShowSavePanelButton" type="Button" parent="HBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "save" text = "save"
[node name="ShowLoadPanelButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
text = "load"
[node name="ShowCreateRoomPanelButton" type="Button" parent="HBoxContainer"] [node name="ShowCreateRoomPanelButton" type="Button" parent="HBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "create_room" text = "create_room"
@ -168,7 +172,7 @@ text = "create_room"
layout_mode = 2 layout_mode = 2
text = "close" text = "close"
[node name="SaveOrloadPanel" type="Panel" parent="."] [node name="SaveOrLoadPanel" type="Panel" parent="."]
visible = false visible = false
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
@ -181,7 +185,7 @@ offset_bottom = -155.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="Label" type="Label" parent="SaveOrloadPanel"] [node name="SaveOrLoadPanelTitleLabel" type="Label" parent="SaveOrLoadPanel"]
layout_mode = 1 layout_mode = 1
anchors_preset = 5 anchors_preset = 5
anchor_left = 0.5 anchor_left = 0.5
@ -193,7 +197,7 @@ offset_bottom = 40.0
grow_horizontal = 2 grow_horizontal = 2
text = "save" text = "save"
[node name="LineEdit" type="LineEdit" parent="SaveOrloadPanel"] [node name="FileNameLineEdit" type="LineEdit" parent="SaveOrLoadPanel"]
layout_mode = 1 layout_mode = 1
anchors_preset = 14 anchors_preset = 14
anchor_top = 0.5 anchor_top = 0.5
@ -206,7 +210,7 @@ offset_bottom = 28.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="Label2" type="Label" parent="SaveOrloadPanel"] [node name="Label" type="Label" parent="SaveOrLoadPanel"]
layout_mode = 1 layout_mode = 1
anchors_preset = 4 anchors_preset = 4
anchor_top = 0.5 anchor_top = 0.5
@ -218,7 +222,7 @@ offset_bottom = -13.0
grow_vertical = 2 grow_vertical = 2
text = "filename" text = "filename"
[node name="HBoxContainer" type="HBoxContainer" parent="SaveOrloadPanel"] [node name="HBoxContainer" type="HBoxContainer" parent="SaveOrLoadPanel"]
layout_mode = 1 layout_mode = 1
anchors_preset = 3 anchors_preset = 3
anchor_left = 1.0 anchor_left = 1.0
@ -232,10 +236,10 @@ offset_bottom = -10.0
grow_horizontal = 0 grow_horizontal = 0
grow_vertical = 0 grow_vertical = 0
[node name="CancelButton" type="Button" parent="SaveOrloadPanel/HBoxContainer"] [node name="CancelButton" type="Button" parent="SaveOrLoadPanel/HBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "cancel" text = "cancel"
[node name="ActionButton" type="Button" parent="SaveOrloadPanel/HBoxContainer"] [node name="ActionButton" type="Button" parent="SaveOrLoadPanel/HBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "save" text = "save"

View File

@ -1,18 +0,0 @@
namespace ColdMint.scripts.levelGraphEditor;
public interface IRoomNodeData
{
string? Id { get; set; }
/// <summary>
/// <para>Title</para>
/// <para>标题</para>
/// </summary>
string? Title { get; set; }
/// <summary>
/// <para>描述</para>
/// <para>Description</para>
/// </summary>
string? Description { get; set; }
}

View File

@ -10,5 +10,5 @@ public class LevelGraphEditorSaveData
{ {
public List<ConnectionData>? ConnectionDataList { get; set; } public List<ConnectionData>? ConnectionDataList { get; set; }
public List<IRoomNodeData>? RoomNodeDataList { get; set; } public List<RoomNodeData>? RoomNodeDataList { get; set; }
} }

View File

@ -5,9 +5,9 @@ namespace ColdMint.scripts.levelGraphEditor;
public partial class RoomNode : GraphNode public partial class RoomNode : GraphNode
{ {
private Label? _describeLabel; private Label? _describeLabel;
private IRoomNodeData? _roomNodeData; private RoomNodeData? _roomNodeData;
public IRoomNodeData? RoomNodeData public RoomNodeData? RoomNodeData
{ {
get => _roomNodeData; get => _roomNodeData;
set set

View File

@ -1,6 +1,6 @@
namespace ColdMint.scripts.levelGraphEditor; namespace ColdMint.scripts.levelGraphEditor;
public class RoomNodeData : IRoomNodeData public class RoomNodeData
{ {
public string? Id { get; set; } public string? Id { get; set; }
public string? Title { get; set; } public string? Title { get; set; }

View File

@ -31,12 +31,37 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
private Button? _createRoomButton; private Button? _createRoomButton;
private Button? _returnButton; private Button? _returnButton;
private string? _defaultRoomName; private string? _defaultRoomName;
/// <summary>
/// <para>Index of the room</para>
/// <para>房间的索引</para>
/// </summary>
private int _roomIndex = 1; private int _roomIndex = 1;
private TextEdit? _roomTemplateCollectionTextEdit; private TextEdit? _roomTemplateCollectionTextEdit;
private Label? _roomTemplateTipsLabel; private Label? _roomTemplateTipsLabel;
private Button? _saveButton; private Button? _showSavePanelButton;
private Button? _openExportFolderButton; private Button? _openExportFolderButton;
private HBoxContainer? _hBoxContainer; private HBoxContainer? _hBoxContainer;
private Panel? _saveOrLoadPanel;
private Button? _cancelButton;
private Button? _actionButton;
private Label? _saveOrLoadPanelTitleLabel;
private LineEdit? _fileNameLineEdit;
private Button? _showLoadPanelButton;
/// <summary>
/// <para>Offset to append when a new node is created.</para>
/// <para>创建新节点时追加的偏移量。</para>
/// </summary>
private Vector2 _positionOffset = new Vector2(100, 100);
/// <summary>
/// <para>Is the press event of an active button saved?</para>
/// <para>活动按钮的按下事件是否为保存?</para>
/// </summary>
private bool _saveMode;
public override void InitializeData() public override void InitializeData()
{ {
@ -68,8 +93,14 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
_openExportFolderButton.Visible = ExplorerUtils.SupportOpenDirectory(); _openExportFolderButton.Visible = ExplorerUtils.SupportOpenDirectory();
} }
_showLoadPanelButton = GetNode<Button>("HBoxContainer/ShowLoadPanelButton");
_saveOrLoadPanelTitleLabel = GetNode<Label>("SaveOrLoadPanel/SaveOrLoadPanelTitleLabel");
_saveOrLoadPanel = GetNode<Panel>("SaveOrLoadPanel");
_fileNameLineEdit = GetNode<LineEdit>("SaveOrLoadPanel/FileNameLineEdit");
_actionButton = GetNode<Button>("SaveOrLoadPanel/HBoxContainer/ActionButton");
_cancelButton = GetNode<Button>("SaveOrLoadPanel/HBoxContainer/CancelButton");
_hBoxContainer = GetNode<HBoxContainer>("HBoxContainer"); _hBoxContainer = GetNode<HBoxContainer>("HBoxContainer");
_saveButton = GetNode<Button>("HBoxContainer/SaveButton"); _showSavePanelButton = GetNode<Button>("HBoxContainer/ShowSavePanelButton");
_roomTemplateCollectionTextEdit = GetNode<TextEdit>("CreateOrEditorPanel/RoomTemplateCollectionTextEdit"); _roomTemplateCollectionTextEdit = GetNode<TextEdit>("CreateOrEditorPanel/RoomTemplateCollectionTextEdit");
_graphEdit = GetNode<GraphEdit>("GraphEdit"); _graphEdit = GetNode<GraphEdit>("GraphEdit");
_showCreateRoomPanelButton = GetNode<Button>("HBoxContainer/ShowCreateRoomPanelButton"); _showCreateRoomPanelButton = GetNode<Button>("HBoxContainer/ShowCreateRoomPanelButton");
@ -87,28 +118,29 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
/// </summary> /// </summary>
/// <param name="roomNodeData"></param> /// <param name="roomNodeData"></param>
/// <returns></returns> /// <returns></returns>
private bool CreateRoomNode(IRoomNodeData roomNodeData) private RoomNode? CreateRoomNode(RoomNodeData roomNodeData)
{ {
if (_roomNodeScene == null || _graphEdit == null) if (_roomNodeScene == null || _graphEdit == null)
{ {
return false; return null;
} }
var node = _roomNodeScene.Instantiate(); var node = _roomNodeScene.Instantiate();
if (node == null) if (node == null)
{ {
return false; return null;
} }
_graphEdit?.AddChild(node); _graphEdit?.AddChild(node);
if (node is not RoomNode roomNode) if (node is not RoomNode roomNode)
{ {
return false; return null;
} }
roomNode.RoomNodeData = roomNodeData; roomNode.RoomNodeData = roomNodeData;
roomNode.PositionOffset = _positionOffset * _roomIndex;
_roomIndex++; _roomIndex++;
return true; return roomNode;
} }
public override void LoadUiActions() public override void LoadUiActions()
@ -247,113 +279,319 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
Title = _roomNameLineEdit.Text, Title = _roomNameLineEdit.Text,
Description = _roomDescriptionLineEdit.Text Description = _roomDescriptionLineEdit.Text
}; };
var result = CreateRoomNode(roomNodeData); var roomNode = CreateRoomNode(roomNodeData);
if (result) if (roomNode != null)
{ {
HideCreateRoomPanel(); HideCreateRoomPanel();
} }
}; };
} }
if (_saveButton != null) if (_cancelButton != null)
{ {
_saveButton.Pressed += () => _cancelButton.Pressed += () =>
{ {
if (_graphEdit == null) if (_saveOrLoadPanel != null)
{
_saveOrLoadPanel.Visible = false;
}
};
}
if (_actionButton != null)
{
_actionButton.Pressed += () =>
{
if (_saveOrLoadPanel != null)
{
_saveOrLoadPanel.Visible = false;
}
};
}
if (_showLoadPanelButton != null)
{
_showLoadPanelButton.Pressed += () =>
{
if (_saveOrLoadPanel != null)
{
_saveOrLoadPanel.Visible = true;
}
if (_actionButton != null)
{
_actionButton.Text = TranslationServer.Translate("load");
}
if (_fileNameLineEdit != null)
{
_fileNameLineEdit.Text = string.Empty;
}
if (_saveOrLoadPanelTitleLabel != null)
{
_saveOrLoadPanelTitleLabel.Text = TranslationServer.Translate("load");
}
_saveMode = false;
};
}
if (_showSavePanelButton != null)
{
_showSavePanelButton.Pressed += () =>
{
if (_saveOrLoadPanel != null)
{
_saveOrLoadPanel.Visible = true;
}
if (_actionButton != null)
{
_actionButton.Text = TranslationServer.Translate("save");
}
if (_fileNameLineEdit != null)
{
_fileNameLineEdit.Text = string.Empty;
}
if (_saveOrLoadPanelTitleLabel != null)
{
_saveOrLoadPanelTitleLabel.Text = TranslationServer.Translate("save");
}
_saveMode = true;
};
}
if (_actionButton != null)
{
_actionButton.Pressed += () =>
{
if (_fileNameLineEdit == null)
{ {
return; return;
} }
var levelGraphEditorSaveData = new LevelGraphEditorSaveData(); var fileName = _fileNameLineEdit.Text;
//Serialize room node information if (string.IsNullOrEmpty(fileName))
//序列化房间节点信息
var length = _graphEdit.GetChildCount();
if (length <= 0)
{ {
//no room
//没有房间
return; return;
} }
var roomNodeDataList = new List<IRoomNodeData>(); if (_saveMode)
levelGraphEditorSaveData.RoomNodeDataList = roomNodeDataList;
for (var i = 0; i < length; i++)
{ {
var node = _graphEdit.GetChild(i); SaveFile(fileName);
if (node is not RoomNode roomNode) continue;
var data = roomNode.RoomNodeData;
if (data == null)
{
continue;
}
roomNodeDataList.Add(data);
} }
//Serialized connection information else
//序列化连接信息
Array<Dictionary> connectionList = _graphEdit.GetConnectionList();
var connectionDataList = new List<ConnectionData>();
levelGraphEditorSaveData.ConnectionDataList = connectionDataList;
if (connectionList.Count > 0)
{ {
foreach (var dictionary in connectionList) LoadFile(fileName);
{
if (dictionary == null)
{
continue;
}
var keys = dictionary.Keys;
if (keys.Count == 0)
{
continue;
}
var connectionData = new ConnectionData();
foreach (var variant in keys)
{
var typeStr = variant.ToString();
switch (typeStr)
{
case "from_node":
var fromRoomNodeData = GetRoomNodeData(dictionary[variant].AsString());
if (fromRoomNodeData == null)
{
continue;
}
connectionData.FromId = fromRoomNodeData.Id;
break;
case "from_port":
connectionData.FromPort = dictionary[variant].AsInt32();
break;
case "to_node":
var toRoomNodeData = GetRoomNodeData(dictionary[variant].AsString());
if (toRoomNodeData == null)
{
continue;
}
connectionData.ToId = toRoomNodeData.Id;
break;
case "to_port":
connectionData.ToPort = dictionary[variant].AsInt32();
break;
}
}
connectionDataList.Add(connectionData);
}
} }
var filePath = Path.Join(Config.GetLevelGraphExportDirectory(), GuidUtils.GetGuid() + ".json");
File.WriteAllText(filePath, JsonSerialization.Serialize(levelGraphEditorSaveData));
}; };
} }
} }
/// <summary>
/// <para>save file</para>
/// <para>保存文件</para>
/// </summary>
/// <param name="fileName">
///<para>filename</para>
///<para>文件名</para>
/// </param>
private async void SaveFile(string fileName)
{
if (_graphEdit == null)
{
return;
}
var levelGraphEditorSaveData = new LevelGraphEditorSaveData();
//Serialize room node information
//序列化房间节点信息
var length = _graphEdit.GetChildCount();
if (length <= 0)
{
//no room
//没有房间
return;
}
var roomNodeDataList = new List<RoomNodeData>();
levelGraphEditorSaveData.RoomNodeDataList = roomNodeDataList;
for (var i = 0; i < length; i++)
{
var node = _graphEdit.GetChild(i);
if (node is not RoomNode roomNode) continue;
var data = roomNode.RoomNodeData;
if (data == null)
{
continue;
}
roomNodeDataList.Add(data);
}
//Serialized connection information
//序列化连接信息
Array<Dictionary> connectionList = _graphEdit.GetConnectionList();
var connectionDataList = new List<ConnectionData>();
levelGraphEditorSaveData.ConnectionDataList = connectionDataList;
if (connectionList.Count > 0)
{
foreach (var dictionary in connectionList)
{
if (dictionary == null)
{
continue;
}
var keys = dictionary.Keys;
if (keys.Count == 0)
{
continue;
}
var connectionData = new ConnectionData();
foreach (var variant in keys)
{
var typeStr = variant.ToString();
switch (typeStr)
{
case "from_node":
var fromRoomNodeData = GetRoomNodeData(dictionary[variant].AsString());
if (fromRoomNodeData == null)
{
continue;
}
connectionData.FromId = fromRoomNodeData.Id;
break;
case "from_port":
connectionData.FromPort = dictionary[variant].AsInt32();
break;
case "to_node":
var toRoomNodeData = GetRoomNodeData(dictionary[variant].AsString());
if (toRoomNodeData == null)
{
continue;
}
connectionData.ToId = toRoomNodeData.Id;
break;
case "to_port":
connectionData.ToPort = dictionary[variant].AsInt32();
break;
}
}
connectionDataList.Add(connectionData);
}
}
var filePath = Path.Join(Config.GetLevelGraphExportDirectory(), FileNameToActualName(fileName));
await File.WriteAllTextAsync(filePath, JsonSerialization.Serialize(levelGraphEditorSaveData));
}
/// <summary>
/// <para>Filename to real name</para>
/// <para>文件名到真实名称</para>
/// </summary>
/// <param name="fileName">
///<para>If the filename does not have a file format, add the file format.</para>
///<para>若文件名没有文件格式,那么加上文件格式。</para>
/// </param>
/// <returns></returns>
private string FileNameToActualName(string fileName)
{
string actualName;
if (fileName.EndsWith(".json"))
{
actualName = fileName;
}
else
{
actualName = fileName + ".json";
}
return actualName;
}
private async void LoadFile(string fileName)
{
if (_graphEdit == null)
{
return;
}
var filePath = Path.Join(Config.GetLevelGraphExportDirectory(), FileNameToActualName(fileName));
if (!File.Exists(filePath))
{
//file does not exist
//文件不存在。
return;
}
var levelGraphEditorSaveData =
await JsonSerialization.ReadJsonFileToObj<LevelGraphEditorSaveData>(filePath);
if (levelGraphEditorSaveData == null)
{
//Deserialization failed.
//反序列化失败。
return;
}
//Do not call DeleteAllChildAsync; this will raise "ERROR: Caller thread can't call this function in this node."
//不要调用DeleteAllChildAsync方法这会引发“ERROR: Caller thread can't call this function in this node”。
NodeUtils.DeleteAllChild(_graphEdit);
_roomIndex = 1;
var roomNodeDataList = levelGraphEditorSaveData.RoomNodeDataList;
if (roomNodeDataList != null)
{
foreach (var roomNodeData in roomNodeDataList)
{
if (string.IsNullOrEmpty(roomNodeData.Id))
{
continue;
}
//Instantiate the room node.
//实例化房间节点。
var roomNode = CreateRoomNode(roomNodeData);
if (roomNode != null)
{
roomNode.Name = roomNodeData.Id;
}
}
}
var connectionDataList = levelGraphEditorSaveData.ConnectionDataList;
if (connectionDataList != null)
{
foreach (var connectionData in connectionDataList)
{
if (string.IsNullOrEmpty(connectionData.FromId))
{
continue;
}
if (string.IsNullOrEmpty(connectionData.ToId))
{
continue;
}
//Connecting rooms
//连接房间
_graphEdit.ConnectNode(connectionData.FromId, connectionData.FromPort, connectionData.ToId,
connectionData.ToPort);
}
}
}
/// <summary> /// <summary>
/// <para>Get node data by name</para> /// <para>Get node data by name</para>
/// <para>根据名称获取节点数据</para> /// <para>根据名称获取节点数据</para>
@ -363,7 +601,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
///<para>名称</para> ///<para>名称</para>
/// </param> /// </param>
/// <returns></returns> /// <returns></returns>
private IRoomNodeData? GetRoomNodeData(string name) private RoomNodeData? GetRoomNodeData(string name)
{ {
if (_graphEdit == null) if (_graphEdit == null)
{ {