add LdtkFieldInstance and LdtkEntityMap classes; update LdtkSceneBuilder to support entity mapping

This commit is contained in:
2025-09-23 02:21:18 +02:00
parent 22271e741d
commit 43e5bfcdce
11 changed files with 278 additions and 10 deletions

View File

@@ -0,0 +1,11 @@
using Godot;
using Godot.Collections;
namespace CSharpLdtkImporter;
[Tool]
[GlobalClass]
public partial class LdtkEntityMap : Resource
{
[Export] public Dictionary<string, PackedScene> EntitySceneMap { get; set; } = new();
}

View File

@@ -0,0 +1 @@
uid://c5wadsimbi1re

View File

@@ -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<LdtkEntityMap>() : 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" },
}
};

View File

@@ -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<int, TileSet> _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<string>())}");
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<Node2D>();
}
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)
{

View File

@@ -9,4 +9,7 @@ public class LdtkEntityInstance
[JsonPropertyName("px")]
public int[] Px { get; set; }
[JsonPropertyName("fieldInstances")]
public LdtkFieldInstance[] FieldInstances { get; set; }
}

View File

@@ -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; }
}

View File

@@ -0,0 +1 @@
uid://dgwt3rdvkdypq

11
assets/MyEntityMap.tres Normal file
View File

@@ -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"

8
assets/square.tscn Normal file
View File

@@ -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")

View File

@@ -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",

View File

@@ -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")