fr.jeanf.scenemanagement 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Runtime/AdditiveLoading/WorldManager.cs +43 -16
- package/Runtime/DynamicLoading/Editor/VolumeDataGenerator.cs +360 -0
- package/Runtime/DynamicLoading/Editor/VolumeDataGenerator.cs.meta +2 -0
- package/Runtime/DynamicLoading/PrecomputedVolumeData.cs +123 -0
- package/Runtime/DynamicLoading/PrecomputedVolumeData.cs.meta +2 -0
- package/Runtime/DynamicLoading/RegionConnectivityAuthoring.cs +101 -5
- package/Runtime/DynamicLoading/VolumeSystem.cs +145 -117
- package/Samples/Example/PrecomputedVolumeData.asset +360 -0
- package/Samples/Example/PrecomputedVolumeData.asset.meta +8 -0
- package/Samples/Example/Scenes/Main.unity +0 -289
- package/Samples/Example/Scenes/World.unity +2 -0
- package/package.json +1 -1
|
@@ -24,8 +24,9 @@ namespace jeanf.scenemanagement
|
|
|
24
24
|
private List<Region> _activeRegions = new List<Region>();
|
|
25
25
|
private bool _mappingInitialized = false;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
private List<
|
|
27
|
+
// GC ALLOCATION FIX: Pre-allocate reusable collections
|
|
28
|
+
private readonly List<string> _tempSceneNames = new List<string>();
|
|
29
|
+
private readonly List<Region> _tempRegionsToRemove = new List<Region>();
|
|
29
30
|
|
|
30
31
|
[SerializeField] private StringEventChannelSO regionChangeRequestChannel;
|
|
31
32
|
[SerializeField] private SendTeleportTarget sendTeleportTarget;
|
|
@@ -126,6 +127,8 @@ namespace jeanf.scenemanagement
|
|
|
126
127
|
_compiledSceneLists.Clear();
|
|
127
128
|
_landingZoneIds.Clear();
|
|
128
129
|
_activeRegions.Clear();
|
|
130
|
+
|
|
131
|
+
// GC ALLOCATION FIX: Clear reusable collections instead of creating new ones
|
|
129
132
|
_tempSceneNames.Clear();
|
|
130
133
|
_tempRegionsToRemove.Clear();
|
|
131
134
|
_mappingInitialized = false;
|
|
@@ -138,8 +141,10 @@ namespace jeanf.scenemanagement
|
|
|
138
141
|
var regionCount = ListOfRegions?.Count ?? 0;
|
|
139
142
|
if (regionCount == 0) return;
|
|
140
143
|
|
|
141
|
-
foreach
|
|
144
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach to avoid enumerator allocation
|
|
145
|
+
for (int i = 0; i < ListOfRegions.Count; i++)
|
|
142
146
|
{
|
|
147
|
+
var region = ListOfRegions[i];
|
|
143
148
|
if (region == null) continue;
|
|
144
149
|
if (!_regionDictionary.TryAdd(region.id, region)) continue;
|
|
145
150
|
|
|
@@ -148,8 +153,10 @@ namespace jeanf.scenemanagement
|
|
|
148
153
|
|
|
149
154
|
if (region.scenariosInThisRegion != null)
|
|
150
155
|
{
|
|
151
|
-
|
|
156
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
157
|
+
for (int j = 0; j < region.scenariosInThisRegion.Count; j++)
|
|
152
158
|
{
|
|
159
|
+
var scenario = region.scenariosInThisRegion[j];
|
|
153
160
|
if (scenario != null)
|
|
154
161
|
{
|
|
155
162
|
ScenarioManager.ScenarioDictionary.TryAdd(scenario.id, scenario);
|
|
@@ -159,8 +166,10 @@ namespace jeanf.scenemanagement
|
|
|
159
166
|
|
|
160
167
|
if (region.zonesInThisRegion != null)
|
|
161
168
|
{
|
|
162
|
-
|
|
169
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
170
|
+
for (int j = 0; j < region.zonesInThisRegion.Count; j++)
|
|
163
171
|
{
|
|
172
|
+
var zone = region.zonesInThisRegion[j];
|
|
164
173
|
if (zone != null)
|
|
165
174
|
{
|
|
166
175
|
_zoneDictionary.TryAdd(zone.id, zone);
|
|
@@ -176,10 +185,13 @@ namespace jeanf.scenemanagement
|
|
|
176
185
|
|
|
177
186
|
private void PrecompileSceneList(Region region)
|
|
178
187
|
{
|
|
188
|
+
// GC ALLOCATION FIX: Use capacity to avoid List resizing
|
|
179
189
|
var sceneNames = new List<string>(region.dependenciesInThisRegion.Count);
|
|
180
|
-
|
|
190
|
+
|
|
191
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
192
|
+
for (int i = 0; i < region.dependenciesInThisRegion.Count; i++)
|
|
181
193
|
{
|
|
182
|
-
sceneNames.Add(
|
|
194
|
+
sceneNames.Add(region.dependenciesInThisRegion[i].SceneName);
|
|
183
195
|
}
|
|
184
196
|
_compiledSceneLists[region.id] = sceneNames;
|
|
185
197
|
}
|
|
@@ -189,8 +201,10 @@ namespace jeanf.scenemanagement
|
|
|
189
201
|
var connectivity = FindObjectOfType<RegionConnectivityAuthoring>();
|
|
190
202
|
if (connectivity?.regionConnectivity?.landingZones == null) return;
|
|
191
203
|
|
|
192
|
-
|
|
204
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
205
|
+
for (int i = 0; i < connectivity.regionConnectivity.landingZones.Count; i++)
|
|
193
206
|
{
|
|
207
|
+
var landing = connectivity.regionConnectivity.landingZones[i];
|
|
194
208
|
if (landing?.landingZone != null)
|
|
195
209
|
{
|
|
196
210
|
_landingZoneIds.Add(landing.landingZone.id);
|
|
@@ -216,6 +230,7 @@ namespace jeanf.scenemanagement
|
|
|
216
230
|
|
|
217
231
|
private void OnZoneChangedFromECS(string zoneId)
|
|
218
232
|
{
|
|
233
|
+
// GC ALLOCATION FIX: Early exit to avoid string operations
|
|
219
234
|
if (string.IsNullOrEmpty(zoneId) || _lastNotifiedZone == zoneId) return;
|
|
220
235
|
|
|
221
236
|
if (!_zoneDictionary.TryGetValue(zoneId, out var zone)) return;
|
|
@@ -223,12 +238,14 @@ namespace jeanf.scenemanagement
|
|
|
223
238
|
_lastNotifiedZone = zoneId;
|
|
224
239
|
_currentPlayerZone = zone;
|
|
225
240
|
|
|
241
|
+
// GC ALLOCATION FIX: Only invoke if delegates are not null
|
|
226
242
|
PublishCurrentZoneId?.Invoke(zone.id);
|
|
227
243
|
PublishAppList(zone);
|
|
228
244
|
}
|
|
229
245
|
|
|
230
246
|
private void OnRegionChangedFromECS(string regionId)
|
|
231
247
|
{
|
|
248
|
+
// GC ALLOCATION FIX: Early exit to avoid unnecessary operations
|
|
232
249
|
if (string.IsNullOrEmpty(regionId) || _lastNotifiedRegion == regionId) return;
|
|
233
250
|
|
|
234
251
|
if (!_regionDictionary.TryGetValue(regionId, out var region)) return;
|
|
@@ -260,18 +277,23 @@ namespace jeanf.scenemanagement
|
|
|
260
277
|
_currentPlayerRegion = region;
|
|
261
278
|
_lastNotifiedRegion = region.id;
|
|
262
279
|
|
|
280
|
+
// GC ALLOCATION FIX: Only invoke if delegate is not null
|
|
263
281
|
PublishCurrentRegionId?.Invoke(_currentPlayerRegion.id);
|
|
264
282
|
|
|
283
|
+
// GC ALLOCATION FIX: Clear and reuse collection instead of creating new
|
|
265
284
|
_tempRegionsToRemove.Clear();
|
|
266
|
-
|
|
285
|
+
|
|
286
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
287
|
+
for (int i = 0; i < _activeRegions.Count; i++)
|
|
267
288
|
{
|
|
268
|
-
var removedRegion = RequestUnLoadForObsoleteRegion(
|
|
289
|
+
var removedRegion = RequestUnLoadForObsoleteRegion(_activeRegions[i]);
|
|
269
290
|
_tempRegionsToRemove.Add(removedRegion);
|
|
270
291
|
}
|
|
271
292
|
|
|
272
|
-
|
|
293
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
294
|
+
for (int i = 0; i < _tempRegionsToRemove.Count; i++)
|
|
273
295
|
{
|
|
274
|
-
_activeRegions.Remove(
|
|
296
|
+
_activeRegions.Remove(_tempRegionsToRemove[i]);
|
|
275
297
|
}
|
|
276
298
|
|
|
277
299
|
RequestLoadForRegionDependencies(region);
|
|
@@ -304,6 +326,8 @@ namespace jeanf.scenemanagement
|
|
|
304
326
|
{
|
|
305
327
|
_currentPlayerZone = firstZone;
|
|
306
328
|
_lastNotifiedZone = firstZone.id;
|
|
329
|
+
|
|
330
|
+
// GC ALLOCATION FIX: Only invoke if delegates are not null
|
|
307
331
|
PublishCurrentZoneId?.Invoke(firstZone.id);
|
|
308
332
|
PublishAppList(firstZone);
|
|
309
333
|
}
|
|
@@ -330,6 +354,7 @@ namespace jeanf.scenemanagement
|
|
|
330
354
|
listToBroadcast = value;
|
|
331
355
|
}
|
|
332
356
|
|
|
357
|
+
// GC ALLOCATION FIX: Only invoke if delegate is not null
|
|
333
358
|
_broadcastAppList?.Invoke(listToBroadcast);
|
|
334
359
|
}
|
|
335
360
|
|
|
@@ -337,9 +362,10 @@ namespace jeanf.scenemanagement
|
|
|
337
362
|
{
|
|
338
363
|
if (!_compiledSceneLists.TryGetValue(region.id, out var sceneNames) || sceneNames.Count <= 0) return;
|
|
339
364
|
|
|
340
|
-
|
|
365
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
366
|
+
for (int i = 0; i < sceneNames.Count; i++)
|
|
341
367
|
{
|
|
342
|
-
_sceneLoader.LoadSceneRequest(
|
|
368
|
+
_sceneLoader.LoadSceneRequest(sceneNames[i]);
|
|
343
369
|
}
|
|
344
370
|
}
|
|
345
371
|
|
|
@@ -347,9 +373,10 @@ namespace jeanf.scenemanagement
|
|
|
347
373
|
{
|
|
348
374
|
if (_compiledSceneLists.TryGetValue(region.id, out var sceneNames))
|
|
349
375
|
{
|
|
350
|
-
|
|
376
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
377
|
+
for (int i = 0; i < sceneNames.Count; i++)
|
|
351
378
|
{
|
|
352
|
-
_sceneLoader.UnLoadSceneRequest(
|
|
379
|
+
_sceneLoader.UnLoadSceneRequest(sceneNames[i]);
|
|
353
380
|
}
|
|
354
381
|
}
|
|
355
382
|
return region;
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
#if UNITY_EDITOR
|
|
2
|
+
using UnityEngine;
|
|
3
|
+
using UnityEditor;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.IO;
|
|
6
|
+
|
|
7
|
+
namespace jeanf.scenemanagement
|
|
8
|
+
{
|
|
9
|
+
public class VolumeDataGenerator : EditorWindow
|
|
10
|
+
{
|
|
11
|
+
private RegionConnectivity sourceRegionConnectivity;
|
|
12
|
+
private PrecomputedVolumeData targetPrecomputedData;
|
|
13
|
+
private string savePath = "Assets/ScriptableObjects/";
|
|
14
|
+
private string fileName = "PrecomputedVolumeData";
|
|
15
|
+
|
|
16
|
+
private Vector2 scrollPosition;
|
|
17
|
+
private bool showPreview = false;
|
|
18
|
+
private Dictionary<string, List<string>> previewData = new Dictionary<string, List<string>>();
|
|
19
|
+
|
|
20
|
+
[MenuItem("Tools/Scene Management/Volume Data Generator")]
|
|
21
|
+
public static void ShowWindow()
|
|
22
|
+
{
|
|
23
|
+
GetWindow<VolumeDataGenerator>("Volume Data Generator");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private void OnGUI()
|
|
27
|
+
{
|
|
28
|
+
EditorGUILayout.LabelField("Volume Data Generator", EditorStyles.boldLabel);
|
|
29
|
+
EditorGUILayout.HelpBox("Generate pre-computed volume connectivity data from RegionConnectivity for maximum runtime performance.", MessageType.Info);
|
|
30
|
+
EditorGUILayout.Space();
|
|
31
|
+
|
|
32
|
+
DrawSourceSelection();
|
|
33
|
+
EditorGUILayout.Space();
|
|
34
|
+
|
|
35
|
+
DrawTargetSelection();
|
|
36
|
+
EditorGUILayout.Space();
|
|
37
|
+
|
|
38
|
+
DrawGenerationControls();
|
|
39
|
+
EditorGUILayout.Space();
|
|
40
|
+
|
|
41
|
+
if (showPreview && previewData.Count > 0)
|
|
42
|
+
{
|
|
43
|
+
DrawPreview();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private void DrawSourceSelection()
|
|
48
|
+
{
|
|
49
|
+
EditorGUILayout.LabelField("Source Data", EditorStyles.boldLabel);
|
|
50
|
+
|
|
51
|
+
sourceRegionConnectivity = (RegionConnectivity)EditorGUILayout.ObjectField(
|
|
52
|
+
"Region Connectivity",
|
|
53
|
+
sourceRegionConnectivity,
|
|
54
|
+
typeof(RegionConnectivity),
|
|
55
|
+
false);
|
|
56
|
+
|
|
57
|
+
if (sourceRegionConnectivity == null)
|
|
58
|
+
{
|
|
59
|
+
EditorGUILayout.HelpBox("Select a RegionConnectivity asset to generate from.", MessageType.Warning);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
EditorGUILayout.BeginVertical(GUI.skin.box);
|
|
64
|
+
EditorGUILayout.LabelField("Source Analysis:", EditorStyles.boldLabel);
|
|
65
|
+
EditorGUILayout.LabelField($"• Active Regions: {sourceRegionConnectivity.activeRegions.Count}");
|
|
66
|
+
EditorGUILayout.LabelField($"• Landing Zones: {sourceRegionConnectivity.landingZones.Count}");
|
|
67
|
+
EditorGUILayout.LabelField($"• Zone Connections: {sourceRegionConnectivity.zoneConnections.Count}");
|
|
68
|
+
|
|
69
|
+
int totalZones = 0;
|
|
70
|
+
foreach (var region in sourceRegionConnectivity.activeRegions)
|
|
71
|
+
{
|
|
72
|
+
if (region?.zonesInThisRegion != null)
|
|
73
|
+
totalZones += region.zonesInThisRegion.Count;
|
|
74
|
+
}
|
|
75
|
+
EditorGUILayout.LabelField($"• Total Zones: {totalZones}");
|
|
76
|
+
EditorGUILayout.EndVertical();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private void DrawTargetSelection()
|
|
80
|
+
{
|
|
81
|
+
EditorGUILayout.LabelField("Target Asset", EditorStyles.boldLabel);
|
|
82
|
+
|
|
83
|
+
targetPrecomputedData = (PrecomputedVolumeData)EditorGUILayout.ObjectField(
|
|
84
|
+
"Precomputed Volume Data",
|
|
85
|
+
targetPrecomputedData,
|
|
86
|
+
typeof(PrecomputedVolumeData),
|
|
87
|
+
false);
|
|
88
|
+
|
|
89
|
+
EditorGUILayout.BeginHorizontal();
|
|
90
|
+
savePath = EditorGUILayout.TextField("Save Path", savePath);
|
|
91
|
+
if (GUILayout.Button("Browse", GUILayout.Width(60)))
|
|
92
|
+
{
|
|
93
|
+
string selectedPath = EditorUtility.SaveFolderPanel("Select Save Folder", savePath, "");
|
|
94
|
+
if (!string.IsNullOrEmpty(selectedPath))
|
|
95
|
+
{
|
|
96
|
+
savePath = FileUtil.GetProjectRelativePath(selectedPath) + "/";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
EditorGUILayout.EndHorizontal();
|
|
100
|
+
|
|
101
|
+
fileName = EditorGUILayout.TextField("File Name", fileName);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private void DrawGenerationControls()
|
|
105
|
+
{
|
|
106
|
+
EditorGUILayout.LabelField("Generation Options", EditorStyles.boldLabel);
|
|
107
|
+
|
|
108
|
+
showPreview = EditorGUILayout.Toggle("Show Preview", showPreview);
|
|
109
|
+
|
|
110
|
+
EditorGUILayout.BeginHorizontal();
|
|
111
|
+
|
|
112
|
+
GUI.enabled = sourceRegionConnectivity != null;
|
|
113
|
+
if (GUILayout.Button("Preview Data"))
|
|
114
|
+
{
|
|
115
|
+
GeneratePreviewData();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (GUILayout.Button("Generate New Asset"))
|
|
119
|
+
{
|
|
120
|
+
GenerateNewAsset();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
GUI.enabled = sourceRegionConnectivity != null && targetPrecomputedData != null;
|
|
124
|
+
if (GUILayout.Button("Update Existing"))
|
|
125
|
+
{
|
|
126
|
+
UpdateExistingAsset();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
GUI.enabled = true;
|
|
130
|
+
EditorGUILayout.EndHorizontal();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private void DrawPreview()
|
|
134
|
+
{
|
|
135
|
+
EditorGUILayout.LabelField("Preview Generated Data", EditorStyles.boldLabel);
|
|
136
|
+
|
|
137
|
+
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(300));
|
|
138
|
+
|
|
139
|
+
foreach (var kvp in previewData)
|
|
140
|
+
{
|
|
141
|
+
EditorGUILayout.LabelField($"Zone: {kvp.Key}", EditorStyles.boldLabel);
|
|
142
|
+
EditorGUI.indentLevel++;
|
|
143
|
+
EditorGUILayout.LabelField($"Checkable zones ({kvp.Value.Count}):");
|
|
144
|
+
foreach (var checkable in kvp.Value)
|
|
145
|
+
{
|
|
146
|
+
EditorGUILayout.LabelField($"• {checkable}");
|
|
147
|
+
}
|
|
148
|
+
EditorGUI.indentLevel--;
|
|
149
|
+
EditorGUILayout.Space();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
EditorGUILayout.EndScrollView();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private void GeneratePreviewData()
|
|
156
|
+
{
|
|
157
|
+
if (sourceRegionConnectivity == null) return;
|
|
158
|
+
|
|
159
|
+
previewData.Clear();
|
|
160
|
+
var generator = new VolumeDataProcessor(sourceRegionConnectivity);
|
|
161
|
+
|
|
162
|
+
foreach (var region in sourceRegionConnectivity.activeRegions)
|
|
163
|
+
{
|
|
164
|
+
if (region?.zonesInThisRegion == null) continue;
|
|
165
|
+
|
|
166
|
+
foreach (var zone in region.zonesInThisRegion)
|
|
167
|
+
{
|
|
168
|
+
if (zone == null) continue;
|
|
169
|
+
|
|
170
|
+
var checkableZones = generator.GetCheckableZonesForZone(zone.id.ToString());
|
|
171
|
+
previewData[zone.id.ToString()] = new List<string>(checkableZones);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
showPreview = true;
|
|
176
|
+
Repaint();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private void GenerateNewAsset()
|
|
180
|
+
{
|
|
181
|
+
if (sourceRegionConnectivity == null) return;
|
|
182
|
+
|
|
183
|
+
var asset = CreateInstance<PrecomputedVolumeData>();
|
|
184
|
+
var generator = new VolumeDataProcessor(sourceRegionConnectivity);
|
|
185
|
+
generator.PopulatePrecomputedData(asset);
|
|
186
|
+
|
|
187
|
+
string fullPath = $"{savePath}{fileName}.asset";
|
|
188
|
+
AssetDatabase.CreateAsset(asset, fullPath);
|
|
189
|
+
AssetDatabase.SaveAssets();
|
|
190
|
+
AssetDatabase.Refresh();
|
|
191
|
+
|
|
192
|
+
targetPrecomputedData = asset;
|
|
193
|
+
EditorUtility.FocusProjectWindow();
|
|
194
|
+
Selection.activeObject = asset;
|
|
195
|
+
|
|
196
|
+
Debug.Log($"Created PrecomputedVolumeData asset at: {fullPath}");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private void UpdateExistingAsset()
|
|
200
|
+
{
|
|
201
|
+
if (sourceRegionConnectivity == null || targetPrecomputedData == null) return;
|
|
202
|
+
|
|
203
|
+
var generator = new VolumeDataProcessor(sourceRegionConnectivity);
|
|
204
|
+
generator.PopulatePrecomputedData(targetPrecomputedData);
|
|
205
|
+
|
|
206
|
+
EditorUtility.SetDirty(targetPrecomputedData);
|
|
207
|
+
AssetDatabase.SaveAssets();
|
|
208
|
+
|
|
209
|
+
Debug.Log($"Updated PrecomputedVolumeData asset: {AssetDatabase.GetAssetPath(targetPrecomputedData)}");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public class VolumeDataProcessor
|
|
214
|
+
{
|
|
215
|
+
private RegionConnectivity sourceData;
|
|
216
|
+
private Dictionary<string, string> zoneToRegionMap = new Dictionary<string, string>();
|
|
217
|
+
private Dictionary<string, HashSet<string>> zoneNeighborsMap = new Dictionary<string, HashSet<string>>();
|
|
218
|
+
private HashSet<string> landingZoneIds = new HashSet<string>();
|
|
219
|
+
|
|
220
|
+
public VolumeDataProcessor(RegionConnectivity source)
|
|
221
|
+
{
|
|
222
|
+
sourceData = source;
|
|
223
|
+
BuildMappings();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private void BuildMappings()
|
|
227
|
+
{
|
|
228
|
+
BuildZoneToRegionMapping();
|
|
229
|
+
BuildZoneNeighborsMapping();
|
|
230
|
+
BuildLandingZoneMapping();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private void BuildZoneToRegionMapping()
|
|
234
|
+
{
|
|
235
|
+
foreach (var region in sourceData.activeRegions)
|
|
236
|
+
{
|
|
237
|
+
if (region?.zonesInThisRegion == null) continue;
|
|
238
|
+
|
|
239
|
+
foreach (var zone in region.zonesInThisRegion)
|
|
240
|
+
{
|
|
241
|
+
if (zone != null)
|
|
242
|
+
{
|
|
243
|
+
zoneToRegionMap[zone.id.ToString()] = region.id.ToString();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private void BuildZoneNeighborsMapping()
|
|
250
|
+
{
|
|
251
|
+
// Build neighbors from region membership
|
|
252
|
+
foreach (var region in sourceData.activeRegions)
|
|
253
|
+
{
|
|
254
|
+
if (region?.zonesInThisRegion == null) continue;
|
|
255
|
+
|
|
256
|
+
foreach (var zoneA in region.zonesInThisRegion)
|
|
257
|
+
{
|
|
258
|
+
if (zoneA == null) continue;
|
|
259
|
+
|
|
260
|
+
var zoneAId = zoneA.id.ToString();
|
|
261
|
+
if (!zoneNeighborsMap.ContainsKey(zoneAId))
|
|
262
|
+
{
|
|
263
|
+
zoneNeighborsMap[zoneAId] = new HashSet<string>();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
foreach (var zoneB in region.zonesInThisRegion)
|
|
267
|
+
{
|
|
268
|
+
if (zoneB == null || zoneA.id.ToString() == zoneB.id.ToString()) continue;
|
|
269
|
+
zoneNeighborsMap[zoneAId].Add(zoneB.id.ToString());
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Add explicit zone connections
|
|
275
|
+
foreach (var connection in sourceData.zoneConnections)
|
|
276
|
+
{
|
|
277
|
+
if (connection.zoneA == null || connection.zoneB == null) continue;
|
|
278
|
+
|
|
279
|
+
var zoneAId = connection.zoneA.id.ToString();
|
|
280
|
+
var zoneBId = connection.zoneB.id.ToString();
|
|
281
|
+
|
|
282
|
+
if (!zoneNeighborsMap.ContainsKey(zoneAId))
|
|
283
|
+
zoneNeighborsMap[zoneAId] = new HashSet<string>();
|
|
284
|
+
if (!zoneNeighborsMap.ContainsKey(zoneBId))
|
|
285
|
+
zoneNeighborsMap[zoneBId] = new HashSet<string>();
|
|
286
|
+
|
|
287
|
+
zoneNeighborsMap[zoneAId].Add(zoneBId);
|
|
288
|
+
|
|
289
|
+
if (connection.isBidirectional)
|
|
290
|
+
{
|
|
291
|
+
zoneNeighborsMap[zoneBId].Add(zoneAId);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private void BuildLandingZoneMapping()
|
|
297
|
+
{
|
|
298
|
+
foreach (var landing in sourceData.landingZones)
|
|
299
|
+
{
|
|
300
|
+
if (landing?.landingZone != null)
|
|
301
|
+
{
|
|
302
|
+
landingZoneIds.Add(landing.landingZone.id.ToString());
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
public HashSet<string> GetCheckableZonesForZone(string zoneId)
|
|
308
|
+
{
|
|
309
|
+
var result = new HashSet<string> { zoneId };
|
|
310
|
+
|
|
311
|
+
if (zoneNeighborsMap.TryGetValue(zoneId, out var neighbors))
|
|
312
|
+
{
|
|
313
|
+
foreach (var neighbor in neighbors)
|
|
314
|
+
{
|
|
315
|
+
result.Add(neighbor);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
foreach (var landingZoneId in landingZoneIds)
|
|
320
|
+
{
|
|
321
|
+
result.Add(landingZoneId);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public void PopulatePrecomputedData(PrecomputedVolumeData target)
|
|
328
|
+
{
|
|
329
|
+
target.Clear();
|
|
330
|
+
|
|
331
|
+
// Generate zone checkable sets
|
|
332
|
+
foreach (var kvp in zoneToRegionMap)
|
|
333
|
+
{
|
|
334
|
+
var zoneId = kvp.Key;
|
|
335
|
+
var checkableZones = GetCheckableZonesForZone(zoneId);
|
|
336
|
+
|
|
337
|
+
var checkableSet = new ZoneCheckableSet(zoneId);
|
|
338
|
+
checkableSet.checkableZoneIds.AddRange(checkableZones);
|
|
339
|
+
|
|
340
|
+
target.zoneCheckableSets.Add(checkableSet);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Add landing zones
|
|
344
|
+
target.landingZoneIds.AddRange(landingZoneIds);
|
|
345
|
+
|
|
346
|
+
// Add zone-region mappings
|
|
347
|
+
foreach (var kvp in zoneToRegionMap)
|
|
348
|
+
{
|
|
349
|
+
target.zoneRegionMappings.Add(new ZoneRegionMapping(kvp.Key, kvp.Value));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Add generation metadata
|
|
353
|
+
target.sourceRegionConnectivityAsset = AssetDatabase.GetAssetPath(sourceData);
|
|
354
|
+
target.generatedDateTime = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
355
|
+
target.totalZones = zoneToRegionMap.Count;
|
|
356
|
+
target.totalRegions = sourceData.activeRegions.Count;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
#endif
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
using System.Collections.Generic;
|
|
2
|
+
using UnityEngine;
|
|
3
|
+
using jeanf.propertyDrawer;
|
|
4
|
+
|
|
5
|
+
namespace jeanf.scenemanagement
|
|
6
|
+
{
|
|
7
|
+
[ScriptableObjectDrawer]
|
|
8
|
+
[CreateAssetMenu(fileName = "PrecomputedVolumeData", menuName = "LoadingSystem/PrecomputedVolumeData")]
|
|
9
|
+
public class PrecomputedVolumeData : ScriptableObject
|
|
10
|
+
{
|
|
11
|
+
[Header("Pre-computed Connectivity Data")]
|
|
12
|
+
[Tooltip("Generated from RegionConnectivity - do not edit manually")]
|
|
13
|
+
public List<ZoneCheckableSet> zoneCheckableSets = new List<ZoneCheckableSet>();
|
|
14
|
+
|
|
15
|
+
[Header("Landing Zones")]
|
|
16
|
+
[Tooltip("Zones that are always checkable regardless of current zone")]
|
|
17
|
+
public List<string> landingZoneIds = new List<string>();
|
|
18
|
+
|
|
19
|
+
[Header("Zone to Region Mapping")]
|
|
20
|
+
[Tooltip("Maps each zone ID to its region ID")]
|
|
21
|
+
public List<ZoneRegionMapping> zoneRegionMappings = new List<ZoneRegionMapping>();
|
|
22
|
+
|
|
23
|
+
[Header("Generation Info")]
|
|
24
|
+
[Tooltip("Information about when this data was generated")]
|
|
25
|
+
public string sourceRegionConnectivityAsset;
|
|
26
|
+
public string generatedDateTime;
|
|
27
|
+
public int totalZones;
|
|
28
|
+
public int totalRegions;
|
|
29
|
+
|
|
30
|
+
// Runtime lookup methods
|
|
31
|
+
public HashSet<string> GetCheckableZoneIds(string currentZone)
|
|
32
|
+
{
|
|
33
|
+
var result = new HashSet<string>();
|
|
34
|
+
|
|
35
|
+
if (string.IsNullOrEmpty(currentZone))
|
|
36
|
+
{
|
|
37
|
+
// Bootstrap state - return all zones
|
|
38
|
+
foreach (var mapping in zoneRegionMappings)
|
|
39
|
+
{
|
|
40
|
+
result.Add(mapping.zoneId);
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Find the checkable set for this zone
|
|
46
|
+
foreach (var checkableSet in zoneCheckableSets)
|
|
47
|
+
{
|
|
48
|
+
if (checkableSet.primaryZoneId == currentZone)
|
|
49
|
+
{
|
|
50
|
+
foreach (var checkableZone in checkableSet.checkableZoneIds)
|
|
51
|
+
{
|
|
52
|
+
result.Add(checkableZone);
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Always add landing zones
|
|
59
|
+
foreach (var landingZone in landingZoneIds)
|
|
60
|
+
{
|
|
61
|
+
result.Add(landingZone);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public string GetRegionForZone(string zoneId)
|
|
68
|
+
{
|
|
69
|
+
foreach (var mapping in zoneRegionMappings)
|
|
70
|
+
{
|
|
71
|
+
if (mapping.zoneId == zoneId)
|
|
72
|
+
{
|
|
73
|
+
return mapping.regionId;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public bool IsLandingZone(string zoneId)
|
|
80
|
+
{
|
|
81
|
+
return landingZoneIds.Contains(zoneId);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public void Clear()
|
|
85
|
+
{
|
|
86
|
+
zoneCheckableSets.Clear();
|
|
87
|
+
landingZoneIds.Clear();
|
|
88
|
+
zoneRegionMappings.Clear();
|
|
89
|
+
sourceRegionConnectivityAsset = "";
|
|
90
|
+
generatedDateTime = "";
|
|
91
|
+
totalZones = 0;
|
|
92
|
+
totalRegions = 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
[System.Serializable]
|
|
97
|
+
public class ZoneCheckableSet
|
|
98
|
+
{
|
|
99
|
+
[Tooltip("The zone that this checkable set applies to")]
|
|
100
|
+
public string primaryZoneId;
|
|
101
|
+
|
|
102
|
+
[Tooltip("All zones that should be checked when in the primary zone")]
|
|
103
|
+
public List<string> checkableZoneIds = new List<string>();
|
|
104
|
+
|
|
105
|
+
public ZoneCheckableSet(string primaryZone)
|
|
106
|
+
{
|
|
107
|
+
primaryZoneId = primaryZone;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[System.Serializable]
|
|
112
|
+
public class ZoneRegionMapping
|
|
113
|
+
{
|
|
114
|
+
public string zoneId;
|
|
115
|
+
public string regionId;
|
|
116
|
+
|
|
117
|
+
public ZoneRegionMapping(string zone, string region)
|
|
118
|
+
{
|
|
119
|
+
zoneId = zone;
|
|
120
|
+
regionId = region;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|