From 43e5bfcdce623cdbf749160ce5a904a26025ccb5 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Tue, 23 Sep 2025 02:21:18 +0200 Subject: [PATCH] add LdtkFieldInstance and LdtkEntityMap classes; update LdtkSceneBuilder to support entity mapping --- addons/csharp_ldtk_importer/LdtkEntityMap.cs | 11 ++ .../csharp_ldtk_importer/LdtkEntityMap.cs.uid | 1 + .../LdtkResourceImporter.cs | 11 +- .../csharp_ldtk_importer/LdtkSceneBuilder.cs | 56 +++++- .../Models/LdtkEntityInstance.cs | 3 + .../Models/LdtkFieldInstance.cs | 16 ++ .../Models/LdtkFieldInstance.cs.uid | 1 + assets/MyEntityMap.tres | 11 ++ assets/square.tscn | 8 + assets/test.ldtk | 167 +++++++++++++++++- assets/test.ldtk.import | 3 +- 11 files changed, 278 insertions(+), 10 deletions(-) create mode 100644 addons/csharp_ldtk_importer/LdtkEntityMap.cs create mode 100644 addons/csharp_ldtk_importer/LdtkEntityMap.cs.uid create mode 100644 addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs create mode 100644 addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs.uid create mode 100644 assets/MyEntityMap.tres create mode 100644 assets/square.tscn diff --git a/addons/csharp_ldtk_importer/LdtkEntityMap.cs b/addons/csharp_ldtk_importer/LdtkEntityMap.cs new file mode 100644 index 0000000..54859e5 --- /dev/null +++ b/addons/csharp_ldtk_importer/LdtkEntityMap.cs @@ -0,0 +1,11 @@ +using Godot; +using Godot.Collections; + +namespace CSharpLdtkImporter; + +[Tool] +[GlobalClass] +public partial class LdtkEntityMap : Resource +{ + [Export] public Dictionary EntitySceneMap { get; set; } = new(); +} \ No newline at end of file diff --git a/addons/csharp_ldtk_importer/LdtkEntityMap.cs.uid b/addons/csharp_ldtk_importer/LdtkEntityMap.cs.uid new file mode 100644 index 0000000..91f5d1a --- /dev/null +++ b/addons/csharp_ldtk_importer/LdtkEntityMap.cs.uid @@ -0,0 +1 @@ +uid://c5wadsimbi1re diff --git a/addons/csharp_ldtk_importer/LdtkResourceImporter.cs b/addons/csharp_ldtk_importer/LdtkResourceImporter.cs index 321ea03..cbb988e 100644 --- a/addons/csharp_ldtk_importer/LdtkResourceImporter.cs +++ b/addons/csharp_ldtk_importer/LdtkResourceImporter.cs @@ -44,9 +44,11 @@ public partial class LdtkResourceImporter : EditorImportPlugin GD.PushError("Parsed LDTK data is null."); return Error.Failed; } + + var entityMap = options.TryGetValue("entity_map", out var map) ? map.As() : null; // 3. Use the Scene Builder to generate the Godot scene - var builder = new LdtkSceneBuilder(ldtkData, sourceFile); + var builder = new LdtkSceneBuilder(ldtkData, sourceFile, entityMap); var rootNode = builder.BuildLdtkProjectRoot(); var scene = new PackedScene(); @@ -78,6 +80,13 @@ public partial class LdtkResourceImporter : EditorImportPlugin { "name", "import_tilemaps" }, { "default_value", true }, { "usage", (int)(PropertyUsageFlags.Default | PropertyUsageFlags.UpdateAllIfModified) } + }, + new() + { + { "name", "entity_map" }, + { "default_value", new LdtkEntityMap() }, + { "property_hint", (int)PropertyHint.ResourceType }, + { "hint_string", "LdtkEntityMap" }, } }; diff --git a/addons/csharp_ldtk_importer/LdtkSceneBuilder.cs b/addons/csharp_ldtk_importer/LdtkSceneBuilder.cs index fb4593c..04e15df 100644 --- a/addons/csharp_ldtk_importer/LdtkSceneBuilder.cs +++ b/addons/csharp_ldtk_importer/LdtkSceneBuilder.cs @@ -1,4 +1,6 @@ +using System; using System.Linq; +using System.Text.Json; using CSharpLdtkImporter.Models; using Godot; @@ -8,12 +10,14 @@ public class LdtkSceneBuilder { private readonly LdtkData _ldtkData; private readonly string _basePath; + private readonly LdtkEntityMap _entityMap; private readonly Godot.Collections.Dictionary _tileSetCache = new(); - public LdtkSceneBuilder(LdtkData ldtkData, string sourceFile) + public LdtkSceneBuilder(LdtkData ldtkData, string sourceFile, LdtkEntityMap entityMap) { _ldtkData = ldtkData; _basePath = sourceFile.GetBaseDir(); + _entityMap = entityMap; } public Node2D BuildLdtkProjectRoot() @@ -94,17 +98,57 @@ public class LdtkSceneBuilder private Node2D BuildEntityLayer(LdtkLayerInstance layer) { var entityLayerRoot = new Node2D { Name = layer.Identifier }; + foreach (var entity in layer.EntityInstances) { - var marker = new Marker2D + Node2D instance = null; + + GD.Print($"EntityMap keys: {string.Join(", ", _entityMap?.EntitySceneMap.Keys ?? Array.Empty())}"); + GD.Print($"Entity Identifier: {entity.Identifier}"); + GD.Print($"Is Entity Mapped: {_entityMap?.EntitySceneMap.ContainsKey(entity.Identifier)}"); + + if (_entityMap?.EntitySceneMap.TryGetValue(entity.Identifier, out var packedScene) == true && + packedScene != null) { - Name = entity.Identifier, - Position = new Vector2(entity.Px[0], entity.Px[1]) - }; - entityLayerRoot.AddChild(marker); + GD.Print($"Instantiating entity '{entity.Identifier}' from mapped scene."); + instance = packedScene.Instantiate(); + } + else + { + GD.Print($"Defaulting to Marker2D for unmapped entity '{entity.Identifier}'."); + instance = new Marker2D(); + } + + instance.Name = entity.Identifier; + instance.Position = new Vector2(entity.Px[0], entity.Px[1]); + + foreach (var field in entity.FieldInstances) + { + instance.SetMeta(field.Identifier, ConvertJsonElementToVariant(field.Value)); + } + + entityLayerRoot.AddChild(instance); } + return entityLayerRoot; } + + private static Variant ConvertJsonElementToVariant(JsonElement element) + { + return element.ValueKind switch + { + JsonValueKind.String => element.GetString(), + JsonValueKind.Number => element.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => default, + // For LDTK points (which are objects) or arrays, we can serialize them back to a JSON string. + // A more advanced implementation could convert these to Vector2 or Array types. + JsonValueKind.Object => Json.Stringify(Json.ParseString(element.GetRawText())), + JsonValueKind.Array => Json.Stringify(Json.ParseString(element.GetRawText())), + _ => default + }; + } private TileSet GetOrCreateTileSet(int tilesetDefUid) { diff --git a/addons/csharp_ldtk_importer/Models/LdtkEntityInstance.cs b/addons/csharp_ldtk_importer/Models/LdtkEntityInstance.cs index 299324b..22714bc 100644 --- a/addons/csharp_ldtk_importer/Models/LdtkEntityInstance.cs +++ b/addons/csharp_ldtk_importer/Models/LdtkEntityInstance.cs @@ -9,4 +9,7 @@ public class LdtkEntityInstance [JsonPropertyName("px")] public int[] Px { get; set; } + + [JsonPropertyName("fieldInstances")] + public LdtkFieldInstance[] FieldInstances { get; set; } } \ No newline at end of file diff --git a/addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs b/addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs new file mode 100644 index 0000000..e28332b --- /dev/null +++ b/addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs @@ -0,0 +1,16 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace CSharpLdtkImporter.Models; + +public class LdtkFieldInstance +{ + [JsonPropertyName("__identifier")] + public string Identifier { get; set; } + + [JsonPropertyName("__type")] + public string Type { get; set; } + + [JsonPropertyName("__value")] + public JsonElement Value { get; set; } +} \ No newline at end of file diff --git a/addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs.uid b/addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs.uid new file mode 100644 index 0000000..0371826 --- /dev/null +++ b/addons/csharp_ldtk_importer/Models/LdtkFieldInstance.cs.uid @@ -0,0 +1 @@ +uid://dgwt3rdvkdypq diff --git a/assets/MyEntityMap.tres b/assets/MyEntityMap.tres new file mode 100644 index 0000000..e3f9b86 --- /dev/null +++ b/assets/MyEntityMap.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="LdtkEntityMap" load_steps=3 format=3 uid="uid://cqu47e7mrbwfr"] + +[ext_resource type="Script" uid="uid://c5wadsimbi1re" path="res://addons/csharp_ldtk_importer/LdtkEntityMap.cs" id="1_0e1w3"] +[ext_resource type="PackedScene" uid="uid://bmtfa3t0gbg3e" path="res://assets/square.tscn" id="1_1cxp4"] + +[resource] +script = ExtResource("1_0e1w3") +EntitySceneMap = Dictionary[String, PackedScene]({ +"Player": ExtResource("1_1cxp4") +}) +metadata/_custom_type_script = "uid://c5wadsimbi1re" diff --git a/assets/square.tscn b/assets/square.tscn new file mode 100644 index 0000000..9ac9c55 --- /dev/null +++ b/assets/square.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=3 uid="uid://bmtfa3t0gbg3e"] + +[ext_resource type="Texture2D" uid="uid://dmrfe4r8rw46w" path="res://icon.svg" id="1_a73l4"] + +[node name="Square" type="Node2D"] + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("1_a73l4") diff --git a/assets/test.ldtk b/assets/test.ldtk index a0b1bf1..f1645bd 100644 --- a/assets/test.ldtk +++ b/assets/test.ldtk @@ -11,7 +11,7 @@ "iid": "2c2f2ff0-8560-11f0-9745-f307d35f41a0", "jsonVersion": "1.5.3", "appBuildId": 488493, - "nextUid": 25, + "nextUid": 27, "identifierStyle": "Capitalize", "toc": [], "worldLayout": "Free", @@ -41,6 +41,41 @@ "customCommands": [], "flags": [], "defs": { "layers": [ + { + "__type": "Entities", + "identifier": "Entities", + "type": "Entities", + "uid": 26, + "doc": null, + "uiColor": null, + "gridSize": 16, + "guideGridWid": 0, + "guideGridHei": 0, + "displayOpacity": 1, + "inactiveOpacity": 0.6, + "hideInList": false, + "hideFieldsWhenInactive": true, + "canSelectWhenInactive": true, + "renderInWorldView": true, + "pxOffsetX": 0, + "pxOffsetY": 0, + "parallaxFactorX": 0, + "parallaxFactorY": 0, + "parallaxScaling": true, + "requiredTags": [], + "excludedTags": [], + "autoTilesKilledByOtherLayerUid": null, + "uiFilterTags": [], + "useAsyncRender": false, + "intGridValues": [], + "intGridValuesGroups": [], + "autoRuleGroups": [], + "autoSourceLayerDefUid": null, + "tilesetDefUid": null, + "tilePivotX": 0, + "tilePivotY": 0, + "biomeFieldUid": null + }, { "__type": "IntGrid", "identifier": "IntGrid", @@ -669,7 +704,43 @@ "tilePivotY": 0, "biomeFieldUid": null } - ], "entities": [], "tilesets": [ + ], "entities": [ + { + "identifier": "Player", + "uid": 25, + "tags": [], + "exportToToc": false, + "allowOutOfBounds": false, + "doc": null, + "width": 16, + "height": 16, + "resizableX": false, + "resizableY": false, + "minWidth": null, + "maxWidth": null, + "minHeight": null, + "maxHeight": null, + "keepAspectRatio": false, + "tileOpacity": 1, + "fillOpacity": 1, + "lineOpacity": 1, + "hollow": false, + "color": "#BE4A2F", + "renderMode": "Rectangle", + "showName": true, + "tilesetId": null, + "tileRenderMode": "FitInside", + "tileRect": null, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [] + } + ], "tilesets": [ { "__cWid": 16, "__cHei": 12, @@ -715,6 +786,48 @@ "externalRelPath": null, "fieldInstances": [], "layerInstances": [ + { + "__identifier": "Entities", + "__type": "Entities", + "__cWid": 16, + "__cHei": 16, + "__gridSize": 16, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": null, + "__tilesetRelPath": null, + "iid": "332d3780-8560-11f0-9745-0103de4db213", + "levelId": 0, + "layerDefUid": 26, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 4277998, + "overrideTilesetUid": null, + "gridTiles": [], + "entityInstances": [ + { + "__identifier": "Player", + "__grid": [7,8], + "__pivot": [0,0], + "__tags": [], + "__tile": null, + "__smartColor": "#BE4A2F", + "iid": "3a675850-8560-11f0-9745-41d5f1b372f4", + "width": 16, + "height": 16, + "defUid": 25, + "px": [112,128], + "fieldInstances": [], + "__worldX": 112, + "__worldY": 128 + } + ] + }, { "__identifier": "IntGrid", "__type": "IntGrid", @@ -915,6 +1028,31 @@ "externalRelPath": null, "fieldInstances": [], "layerInstances": [ + { + "__identifier": "Entities", + "__type": "Entities", + "__cWid": 16, + "__cHei": 16, + "__gridSize": 16, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": null, + "__tilesetRelPath": null, + "iid": "332d5e90-8560-11f0-9745-d935367db062", + "levelId": 23, + "layerDefUid": 26, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 8641114, + "overrideTilesetUid": null, + "gridTiles": [], + "entityInstances": [] + }, { "__identifier": "IntGrid", "__type": "IntGrid", @@ -1100,6 +1238,31 @@ "externalRelPath": null, "fieldInstances": [], "layerInstances": [ + { + "__identifier": "Entities", + "__type": "Entities", + "__cWid": 16, + "__cHei": 16, + "__gridSize": 16, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": null, + "__tilesetRelPath": null, + "iid": "332d5e91-8560-11f0-9745-af9a0227805e", + "levelId": 24, + "layerDefUid": 26, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 4469038, + "overrideTilesetUid": null, + "gridTiles": [], + "entityInstances": [] + }, { "__identifier": "IntGrid", "__type": "IntGrid", diff --git a/assets/test.ldtk.import b/assets/test.ldtk.import index 03dc0e8..8905e4b 100644 --- a/assets/test.ldtk.import +++ b/assets/test.ldtk.import @@ -2,7 +2,7 @@ importer="ldtk.importer" type="PackedScene" -uid="uid://c4xhtpmir26ah" +uid="uid://ckbephcqvg24h" path="res://.godot/imported/test.ldtk-5da58debf60b2ac4adee332e85c3c4bc.tscn" [deps] @@ -14,3 +14,4 @@ dest_files=["res://.godot/imported/test.ldtk-5da58debf60b2ac4adee332e85c3c4bc.ts import_entities=true import_tilemaps=true +entity_map=Resource("uid://cqu47e7mrbwfr", "res://assets/MyEntityMap.tres")