Files
godot-ldtk-importer/addons/csharp_ldtk_importer/LdtkSceneBuilder.cs

152 lines
5.1 KiB
C#

using System.Linq;
using CSharpLdtkImporter.Models;
using Godot;
namespace CSharpLdtkImporter;
public class LdtkSceneBuilder
{
private readonly LdtkData _ldtkData;
private readonly string _basePath;
private readonly Godot.Collections.Dictionary<int, TileSet> _tileSetCache = new();
public LdtkSceneBuilder(LdtkData ldtkData, string sourceFile)
{
_ldtkData = ldtkData;
_basePath = sourceFile.GetBaseDir();
}
public Node2D BuildLdtkProjectRoot()
{
var root = new Node2D { Name = "LDTKProject" };
// Step 1: Build the entire node hierarchy in memory.
foreach (var level in _ldtkData.Levels)
{
var levelNode = BuildLevel(level);
root.AddChild(levelNode);
}
// Step 2: After the tree is built, set the owner for all descendants.
// This is the crucial step that fixes the "Invalid owner" error.
SetOwnerRecursive(root, root);
return root;
}
// A helper function to recursively set the owner on all children.
private void SetOwnerRecursive(Node node, Node owner)
{
foreach (var child in node.GetChildren())
{
child.Owner = owner;
SetOwnerRecursive(child, owner);
}
}
private Node2D BuildLevel(LdtkLevel level)
{
var levelRoot = new Node2D { Name = level.Identifier };
foreach (var layer in level.LayerInstances.Reverse())
{
var layerNode = layer.Type switch
{
"Tiles" => BuildTileMapLayer(layer),
"AutoLayer" => BuildTileMapLayer(layer),
"Entities" => BuildEntityLayer(layer),
_ => new Node2D { Name = $"{layer.Identifier}_Unsupported" }
};
levelRoot.AddChild(layerNode);
}
return levelRoot;
}
private TileMapLayer BuildTileMapLayer(LdtkLayerInstance layer)
{
var tileMapLayer = new TileMapLayer { Name = layer.Identifier };
if (!layer.TilesetDefUid.HasValue) return tileMapLayer;
var tileSet = GetOrCreateTileSet(layer.TilesetDefUid.Value);
if (tileSet == null) return tileMapLayer;
tileMapLayer.TileSet = tileSet;
var allTiles = layer.GridTiles.Concat(layer.AutoLayerTiles);
if (tileMapLayer.TileSet.GetSource(0) is not TileSetAtlasSource atlasSource) return tileMapLayer;
int atlasWidthInTiles = atlasSource.GetAtlasGridSize().X;
if (atlasWidthInTiles == 0) return tileMapLayer;
foreach (var tile in allTiles)
{
var gridCoords = new Vector2I(tile.Px[0] / layer.GridSize, tile.Px[1] / layer.GridSize);
var atlasCoords = new Vector2I(tile.TileId % atlasWidthInTiles, tile.TileId / atlasWidthInTiles);
long alternativeId = 0;
if ((tile.FlipBits & 1) == 1) alternativeId |= TileSetAtlasSource.TransformFlipH;
if ((tile.FlipBits & 2) == 2) alternativeId |= TileSetAtlasSource.TransformFlipV;
tileMapLayer.SetCell(gridCoords, 0, atlasCoords, (int)alternativeId);
}
return tileMapLayer;
}
private Node2D BuildEntityLayer(LdtkLayerInstance layer)
{
var entityLayerRoot = new Node2D { Name = layer.Identifier };
foreach (var entity in layer.EntityInstances)
{
var marker = new Marker2D
{
Name = entity.Identifier,
Position = new Vector2(entity.Px[0], entity.Px[1])
};
entityLayerRoot.AddChild(marker);
}
return entityLayerRoot;
}
private TileSet GetOrCreateTileSet(int tilesetDefUid)
{
if (_tileSetCache.TryGetValue(tilesetDefUid, out var cachedTileSet)) return cachedTileSet;
var tilesetDef = _ldtkData.Defs.Tilesets.FirstOrDefault(t => t.Uid == tilesetDefUid);
if (tilesetDef?.RelPath == null) return null;
var texturePath = _basePath.PathJoin(tilesetDef.RelPath);
var texture = ResourceLoader.Load<Texture2D>(texturePath);
if (texture == null)
{
GD.PushError($"LDTK Importer: Could not load texture at path: {texturePath}");
return null;
}
var newTileSet = new TileSet
{
TileShape = TileSet.TileShapeEnum.Square,
TileLayout = TileSet.TileLayoutEnum.Stacked,
TileSize = new Vector2I(tilesetDef.TileGridSize, tilesetDef.TileGridSize)
};
var atlasSource = new TileSetAtlasSource
{
Texture = texture,
TextureRegionSize = new Vector2I(tilesetDef.TileGridSize, tilesetDef.TileGridSize)
};
newTileSet.AddSource(atlasSource);
var (widthInTiles, heightInTiles) = (texture.GetWidth() / tilesetDef.TileGridSize, texture.GetHeight() / tilesetDef.TileGridSize);
for (int x = 0; x < widthInTiles; x++)
{
for (int y = 0; y < heightInTiles; y++)
{
atlasSource.CreateTile(new Vector2I(x, y));
}
}
_tileSetCache[tilesetDefUid] = newTileSet;
return newTileSet;
}
}