-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathLDtkProjectImporter.cs
More file actions
441 lines (371 loc) · 16.9 KB
/
LDtkProjectImporter.cs
File metadata and controls
441 lines (371 loc) · 16.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Tilemaps;
using Object = UnityEngine.Object;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;
#else
using UnityEditor.Experimental.AssetImporters;
#endif
#pragma warning disable 0414
#pragma warning disable 0649
namespace LDtkUnity.Editor
{
[HelpURL(LDtkHelpURL.IMPORTER_LDTK_PROJECT)]
[ScriptedImporter(LDtkImporterConsts.PROJECT_VERSION, LDtkImporterConsts.PROJECT_EXT, LDtkImporterConsts.PROJECT_ORDER)]
internal sealed class LDtkProjectImporter : LDtkJsonImporter<LDtkProjectFile>
{
public const string JSON = nameof(_jsonFile);
public const string PIXELS_PER_UNIT = nameof(_pixelsPerUnit);
public const string CUSTOM_LEVEL_PREFAB = nameof(_customLevelPrefab);
public const string INTGRID_VISIBLE = nameof(_intGridValueColorsVisible);
public const string USE_COMPOSITE_COLLIDER = nameof(_useCompositeCollider);
public const string GEOMETRY_TYPE = nameof(_geometryType);
public const string CREATE_BACKGROUND_COLOR = nameof(_createBackgroundColor);
public const string CREATE_LEVEL_BOUNDS_TRIGGER = nameof(_createLevelBoundsTrigger);
public const string USE_PARALLAX = nameof(_useParallax);
public const string SCALE_ENTITIES = nameof(_scaleEntities);
public const string INTGRID = nameof(_intGridValues);
public const string ENTITIES = nameof(_entities);
public const string ENUM_GENERATE = nameof(_enumGenerate);
public const string ENUM_PATH = nameof(_enumPath);
public const string ENUM_NAMESPACE = nameof(_enumNamespace);
public const string USE_LAYER_SORTING_ORDERS = nameof(_useLayerCustomSortingOrders);
public const string LAYER_SORTING_ORDERS = nameof(_layerCustomSortingOrders);
/// <summary>
/// This is cached into the meta file upon an import. Could be null if the import was a failure. Invisible to the inspector.
/// </summary>
[SerializeField] private LDtkProjectFile _jsonFile;
[SerializeField] private int _pixelsPerUnit = -1;
[SerializeField] private GameObject _customLevelPrefab = null;
[SerializeField] private bool _intGridValueColorsVisible = false;
[SerializeField] private bool _useCompositeCollider = true;
[SerializeField] private CompositeCollider2D.GeometryType _geometryType = CompositeCollider2D.GeometryType.Outlines;
[SerializeField] private bool _createBackgroundColor = true;
[SerializeField] private bool _createLevelBoundsTrigger = false;
[SerializeField] private bool _useParallax = true;
[SerializeField] private bool _scaleEntities = true;
[SerializeField] private LDtkAssetIntGridValue[] _intGridValues = Array.Empty<LDtkAssetIntGridValue>();
[SerializeField] private LDtkAssetEntity[] _entities = Array.Empty<LDtkAssetEntity>();
[SerializeField] private bool _enumGenerate = false;
[SerializeField] private string _enumPath = null;
[SerializeField] private string _enumNamespace = string.Empty;
[SerializeField] private bool _useLayerCustomSortingOrders = false;
[SerializeField] private LDtkLayerCustomSortingOrder[] _layerCustomSortingOrders = Array.Empty<LDtkLayerCustomSortingOrder>();
public LDtkProjectFile JsonFile => _jsonFile;
public bool IntGridValueColorsVisible => _intGridValueColorsVisible;
public int PixelsPerUnit => _pixelsPerUnit;
public GameObject CustomLevelPrefab => _customLevelPrefab;
public bool UseCompositeCollider => _useCompositeCollider;
public CompositeCollider2D.GeometryType GeometryType => _geometryType;
public bool CreateBackgroundColor => _createBackgroundColor;
public bool CreateLevelBoundsTrigger => _createLevelBoundsTrigger;
public bool UseParallax => _useParallax;
public bool ScaleEntities => _scaleEntities;
public bool UseLayerCustomSortingOrders => _useLayerCustomSortingOrders;
public LDtkLayerCustomSortingOrder[] LayerCustomSortingOrders => _layerCustomSortingOrders;
//all of these are wiped after the entire import is done
private LDtkArtifactAssets _artifacts;
private static string[] _previousDependencies;
public LDtkTableOfContents Toc { get; private set; }
//this will run upon standard reset, but also upon the meta file generation during the first import
private void Reset()
{
LDtkPpuInitializer ppu = new LDtkPpuInitializer(_pixelsPerUnit, assetPath, assetPath);
if (ppu.OnResetImporter())
{
_pixelsPerUnit = ppu.PixelsPerUnit;
EditorUtility.SetDirty(this);
SaveAndReimport();
}
}
private static string[] GatherDependenciesFromSourceFile(string path)
{
if (LDtkPrefs.VerboseLogging)
{
LDtkDebug.Log($"GatherDependenciesFromSourceFile Project {path}");
}
LDtkProfiler.BeginWriting($"GatherDependenciesFromSourceFile/{Path.GetFileName(path)}");
_previousDependencies = LDtkProjectDependencyFactory.GatherProjectDependencies(path);
LDtkProfiler.EndWriting();
return _previousDependencies;
}
protected override string[] GetGatheredDependencies() => _previousDependencies;
protected override void Import()
{
if (IsBackupFile())
{
BufferEditorCache();
FailImport();
return;
}
if (IsVersionOutdated())
{
BufferEditorCache();
FailImport();
return;
}
LDtkProfiler.BeginSample("CreateJsonAsset");
CreateJsonAsset();
LDtkProfiler.EndSample();
if (!TryGetJson(out LdtkJson json))
{
Logger.LogError("Json deserialization error. Not importing.");
BufferEditorCache();
FailImport();
return;
}
LDtkProfiler.BeginSample("CacheSchemaDefs");
CacheSchemaDefs(json);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("CreateArtifactAsset");
CreateArtifactAsset(json);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("MakeDefObjects");
MakeDefObjects(json);
LDtkProfiler.EndSample();
//if for whatever reason (or backwards compatibility), if the ppu is -1 in any capacity
LDtkProfiler.BeginSample("SetPixelsPerUnit");
LDtkPpuInitializer ppu = new LDtkPpuInitializer(_pixelsPerUnit, assetPath, assetPath);
if (ppu.TryInitializePixelsPerUnit(json.DefaultGridSize))
{
_pixelsPerUnit = ppu.PixelsPerUnit;
EditorUtility.SetDirty(this);
}
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("CreateTableOfContents");
TryCreateTableOfContents(json);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("MainBuild");
MainBuild(json);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("TryGenerateEnums");
TryGenerateEnums(json);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("CreateConfigurationFile");
GenerateConfigurationFile(json);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("BufferEditorCache");
BufferEditorCache();
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("ReleaseDefs");
ReleaseDefs();
LDtkProfiler.EndSample();
}
private void MakeDefObjects(LdtkJson json)
{
Dictionary<int, LDtkArtifactAssetsTileset> artifacts = MakeTilesetDict(this, json);
LDtkProfiler.BeginSample("InitializeFromProject");
DefinitionObjects.InitializeFromProject(json.Defs, artifacts);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("Set _definitions");
_artifacts._definitions = DefinitionObjects.Defs;
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("AddAllObjectsToAsset");
foreach (var obj in DefinitionObjects.Defs)
{
if (obj is ILDtkUid uid)
{
ImportContext.AddObjectToAsset($"Uid_{uid.Uid}", obj);
continue;
}
Logger.LogError($"{obj.name} is not a uid! This should never happen", obj);
}
LDtkProfiler.EndSample();
}
private bool TryGetJson(out LdtkJson json)
{
json = FromJson<LdtkJson>();
if (json != null)
{
return true;
}
Logger.LogError("LDtk: Json import error");
return false;
}
private void CreateJsonAsset()
{
_jsonFile = ReadAssetText();
ImportContext.AddObjectToAsset("jsonFile", _jsonFile, LDtkIconUtility.LoadListIcon());
}
/// <summary>
/// Should process after all the definition scriptable objects are created so that they are accessible in this context
/// </summary>
/// <param name="json"></param>
private void TryCreateTableOfContents(LdtkJson json)
{
if (json.Toc.IsNullOrEmpty())
{
return;
}
Toc = ScriptableObject.CreateInstance<LDtkTableOfContents>();
Toc.name += AssetName + "_Toc";
LDtkTocFieldFactory factory = new LDtkTocFieldFactory(json, this, this);
LDtkProfiler.BeginSample("Toc_IndexEntitiesAndFieldsByIdentifiers");
factory.IndexEntitiesAndFieldsByIdentifiers();
LDtkProfiler.EndSample();
Toc.InitializeList(json);
LDtkFieldParser.CacheRecentBuilder(null);
LDtkProfiler.BeginSample("Toc_GenerateAndAddEntries");
foreach (LdtkTableOfContentEntry tocEntry in json.Toc)
{
var output = factory.GenerateFieldsFromTocEntry(tocEntry);
Toc.AddEntry(tocEntry, output.Definition, output.Fields);
}
LDtkProfiler.EndSample();
ImportContext.AddObjectToAsset("toc", Toc, LDtkIconUtility.LoadListIcon());
}
//todo: this should be generated as part of the dependency gather so that there is less import order issues
private void GenerateConfigurationFile(LdtkJson json)
{
//only generate the file if separate levels is used
if (!json.ExternalLevels) return;
//note: should populate any values that should determine if separate levels should reimport
LDtkConfigData config = new LDtkConfigData()
{
PixelsPerUnit = _pixelsPerUnit,
CustomLevelPrefab = _customLevelPrefab,
IntGridValueColorsVisible = _intGridValueColorsVisible,
UseCompositeCollider = _useCompositeCollider,
GeometryType = _geometryType,
CreateBackgroundColor = _createBackgroundColor,
CreateLevelBoundsTrigger = _createLevelBoundsTrigger,
UseParallax = _useParallax,
IntGridValues = _intGridValues,
Entities = _entities,
ScaleEntities = _scaleEntities,
};
string writePath = config.WriteJson(assetPath, out bool isChanged);
if (isChanged)
{
EditorApplication.delayCall += () =>
{
if (File.Exists(writePath))
{
AssetDatabase.ImportAsset(writePath, ImportAssetOptions.ForceUpdate);
}
};
}
}
private void BufferEditorCache()
{
EditorApplication.delayCall += () =>
{
LDtkJsonEditorCache.ForceRefreshJson(assetPath);
};
}
private void MainBuild(LdtkJson json)
{
LDtkProfiler.BeginSample("SetupPreprocessors");
var preAction = new LDtkAssetProcessorActionCache();
LDtkAssetProcessorInvoker.AddPreProcessProject(preAction, json, AssetName);
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("RunPreprocessors");
preAction.Process();
LDtkProfiler.EndSample();
LDtkProfiler.BeginSample("ImportProject");
LDtkBuilderProjectFactory factory = new LDtkBuilderProjectFactory(this);
factory.Import(json);
LDtkProfiler.EndSample();
}
private void TryGenerateEnums(LdtkJson json)
{
//generate enums
if (!_enumGenerate || json.Defs.Enums.IsNullOrEmpty())
{
return;
}
LDtkProjectImporterEnumGenerator enumGenerator = new LDtkProjectImporterEnumGenerator(json.Defs.Enums, ImportContext, _enumPath, _enumNamespace);
enumGenerator.Generate();
}
private void CreateArtifactAsset(LdtkJson json)
{
_artifacts = ScriptableObject.CreateInstance<LDtkArtifactAssets>();
_artifacts.name = AssetName + "_Artifacts";
LDtkProfiler.BeginSample("CreateAllBackgrounds");
LDtkBackgroundSliceCreator bgMaker = new LDtkBackgroundSliceCreator(this);
List<Sprite> allBackgrounds = bgMaker.CreateAllBackgrounds(json);
LDtkProfiler.EndSample();
_artifacts._backgrounds = new List<Sprite>(allBackgrounds);
foreach (Sprite bg in allBackgrounds)
{
//it's possible that some could be null if there was an illegal slice
if (bg == null)
{
continue;
}
ImportContext.AddObjectToAsset($"bg_{bg.name}", bg, (Texture2D)LDtkIconUtility.GetUnityIcon("Sprite"));
}
ImportContext.AddObjectToAsset("artifacts", _artifacts, (Texture2D)LDtkIconUtility.GetUnityIcon("Image"));
}
public TileBase GetIntGridValueTile(string key) => GetSerializedImporterAsset(_intGridValues, key);
public GameObject GetEntity(string key) => GetSerializedImporterAsset(_entities, key);
private T GetSerializedImporterAsset<T>(IEnumerable<LDtkAsset<T>> input, string key) where T : Object //todo these should be indexed too for performance.
{
if (input == null)
{
Logger.LogError("LDtk: Tried getting an asset from the build data but the array was null. Is the project asset properly saved?");
return default;
}
foreach (LDtkAsset<T> asset in input)
{
if (ReferenceEquals(asset, null))
{
Logger.LogError($"LDtk: A field in the build data is null.");
continue;
}
if (asset.Key != key)
{
continue;
}
if (asset.Asset == null)
{
continue;
}
return (T)asset.Asset;
}
return default;
}
public void TryCacheArtifactsAsset(LDtkDebugInstance logger)
{
if (_artifacts != null)
{
return;
}
_artifacts = AssetDatabase.LoadAssetAtPath<LDtkArtifactAssets>(assetPath);
if (_artifacts == null)
{
logger.LogError($"Artifacts was null during the import, likely due to a failed import for the project at \"{assetPath}\". Investigate other problems first.");
}
}
public LDtkArtifactAssets GetArtifactAssets()
{
if (_artifacts == null)
{
Logger.LogError("Project importer's artifact assets was null, this needs to be cached");
return null;
}
return _artifacts;
}
public Sprite GetBackgroundArtifact(Level level)
{
if (_artifacts == null)
{
Logger.LogError("Project importer's artifact assets was null, this needs to be cached");
return null;
}
LDtkArtifactAssets ldtkArtifactAssets = GetArtifactAssets();
string assetName = level.Identifier;
Sprite asset = ldtkArtifactAssets.GetBackgroundSlow(assetName);
if (asset != null)
{
return asset;
}
Logger.LogError($"Tried retrieving a background from the importer's artifacts, but was null: \"{assetName}\"");
return asset;
}
}
}