fr.jeanf.scenemanagement 0.3.2 → 0.3.4
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/SceneLoader.cs +0 -76
- package/Runtime/AdditiveLoading/WorldManager.cs +11 -13
- package/Runtime/DynamicLoading/Editor/VolumeConnectivityVisualizer.cs +271 -158
- package/Runtime/DynamicLoading/VolumeSystem.cs +105 -114
- package/Samples/Example/Scenes/Main.unity +4 -4
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
using System;
|
|
2
1
|
using System.Collections.Concurrent;
|
|
3
2
|
using System.Collections.Generic;
|
|
4
3
|
using System.Threading;
|
|
@@ -63,7 +62,6 @@ namespace jeanf.scenemanagement
|
|
|
63
62
|
LoadSceneRequest += QueueLoadScene;
|
|
64
63
|
UnLoadSceneRequest += QueueUnloadScene;
|
|
65
64
|
UnloadAllScenesRequest += QueueUnloadAllScenes;
|
|
66
|
-
FlushScenesRequest += () => IncrementalMemoryFlush().Forget();
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
private void Unsubscribe()
|
|
@@ -105,51 +103,6 @@ namespace jeanf.scenemanagement
|
|
|
105
103
|
|
|
106
104
|
ProcessUnloadQueue().Forget();
|
|
107
105
|
}
|
|
108
|
-
|
|
109
|
-
private async UniTaskVoid IncrementalMemoryFlush()
|
|
110
|
-
{
|
|
111
|
-
if (_isFlushingMemory) return;
|
|
112
|
-
_isFlushingMemory = true;
|
|
113
|
-
|
|
114
|
-
try
|
|
115
|
-
{
|
|
116
|
-
await UniTask.Delay(TimeSpan.FromSeconds(memoryFlushDelay), DelayType.Realtime);
|
|
117
|
-
|
|
118
|
-
var unloadOperation = Resources.UnloadUnusedAssets();
|
|
119
|
-
await unloadOperation.ToUniTask();
|
|
120
|
-
|
|
121
|
-
if (enableIncrementalGC)
|
|
122
|
-
{
|
|
123
|
-
await IncrementalGarbageCollection();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
finally
|
|
127
|
-
{
|
|
128
|
-
_isFlushingMemory = false;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private async UniTask IncrementalGarbageCollection()
|
|
133
|
-
{
|
|
134
|
-
if (GC.MaxGeneration >= 2)
|
|
135
|
-
{
|
|
136
|
-
for (int generation = 0; generation <= GC.MaxGeneration; generation++)
|
|
137
|
-
{
|
|
138
|
-
GC.Collect(generation, GCCollectionMode.Optimized, false);
|
|
139
|
-
|
|
140
|
-
for (int frame = 0; frame < gcFrameSpread; frame++)
|
|
141
|
-
{
|
|
142
|
-
await UniTask.Yield();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
else
|
|
147
|
-
{
|
|
148
|
-
GC.Collect(0, GCCollectionMode.Optimized, false);
|
|
149
|
-
await UniTask.Yield();
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
106
|
private async UniTaskVoid ProcessLoadQueue()
|
|
154
107
|
{
|
|
155
108
|
if (_isProcessingLoadQueue) return;
|
|
@@ -234,11 +187,6 @@ namespace jeanf.scenemanagement
|
|
|
234
187
|
}
|
|
235
188
|
finally
|
|
236
189
|
{
|
|
237
|
-
if (_loadedScenes.Count == 0)
|
|
238
|
-
{
|
|
239
|
-
IncrementalMemoryFlush().Forget();
|
|
240
|
-
}
|
|
241
|
-
|
|
242
190
|
_isProcessingUnloadQueue = false;
|
|
243
191
|
if (!_isProcessingLoadQueue && _loadQueue.Count == 0)
|
|
244
192
|
{
|
|
@@ -276,30 +224,6 @@ namespace jeanf.scenemanagement
|
|
|
276
224
|
}
|
|
277
225
|
}
|
|
278
226
|
|
|
279
|
-
private void OnApplicationFocus(bool hasFocus)
|
|
280
|
-
{
|
|
281
|
-
if (!hasFocus && enableIncrementalGC)
|
|
282
|
-
{
|
|
283
|
-
IncrementalMemoryFlush().Forget();
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
private void OnApplicationPause(bool pauseStatus)
|
|
288
|
-
{
|
|
289
|
-
if (pauseStatus && enableIncrementalGC)
|
|
290
|
-
{
|
|
291
|
-
IncrementalMemoryFlush().Forget();
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
public void ForceMemoryFlush()
|
|
296
|
-
{
|
|
297
|
-
if (!_isFlushingMemory)
|
|
298
|
-
{
|
|
299
|
-
IncrementalMemoryFlush().Forget();
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
227
|
public bool IsCurrentlyLoading()
|
|
304
228
|
{
|
|
305
229
|
return _isProcessingLoadQueue || _isProcessingUnloadQueue || _isFlushingMemory;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
using System.Collections.Generic;
|
|
2
2
|
using UnityEngine;
|
|
3
3
|
using jeanf.EventSystem;
|
|
4
|
-
using jeanf.propertyDrawer;
|
|
5
4
|
using jeanf.universalplayer;
|
|
5
|
+
using Unity.Collections;
|
|
6
6
|
|
|
7
7
|
namespace jeanf.scenemanagement
|
|
8
8
|
{
|
|
@@ -31,8 +31,8 @@ namespace jeanf.scenemanagement
|
|
|
31
31
|
[SerializeField] private StringEventChannelSO regionChangeRequestChannel;
|
|
32
32
|
[SerializeField] private SendTeleportTarget sendTeleportTarget;
|
|
33
33
|
|
|
34
|
-
[ReadOnly] [SerializeField] private Zone _currentPlayerZone;
|
|
35
|
-
[ReadOnly] [SerializeField] private Region _currentPlayerRegion;
|
|
34
|
+
[propertyDrawer.ReadOnly] [SerializeField] private Zone _currentPlayerZone;
|
|
35
|
+
[propertyDrawer.ReadOnly] [SerializeField] private Region _currentPlayerRegion;
|
|
36
36
|
|
|
37
37
|
private static WorldManager Instance;
|
|
38
38
|
private static bool _isRegionTransitioning = false;
|
|
@@ -212,15 +212,15 @@ namespace jeanf.scenemanagement
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
public static void NotifyZoneChangeFromECS(
|
|
215
|
+
public static void NotifyZoneChangeFromECS(FixedString128Bytes zoneId)
|
|
216
216
|
{
|
|
217
217
|
if (Instance != null && !_isRegionTransitioning)
|
|
218
218
|
{
|
|
219
|
-
Instance.OnZoneChangedFromECS(zoneId);
|
|
219
|
+
Instance.OnZoneChangedFromECS(zoneId.ToString());
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
public static void NotifyRegionChangeFromECS(
|
|
223
|
+
public static void NotifyRegionChangeFromECS(FixedString128Bytes regionId)
|
|
224
224
|
{
|
|
225
225
|
if (Instance != null && !_isRegionTransitioning)
|
|
226
226
|
{
|
|
@@ -230,7 +230,6 @@ namespace jeanf.scenemanagement
|
|
|
230
230
|
|
|
231
231
|
private void OnZoneChangedFromECS(string zoneId)
|
|
232
232
|
{
|
|
233
|
-
// GC ALLOCATION FIX: Early exit to avoid string operations
|
|
234
233
|
if (string.IsNullOrEmpty(zoneId) || _lastNotifiedZone == zoneId) return;
|
|
235
234
|
|
|
236
235
|
if (!_zoneDictionary.TryGetValue(zoneId, out var zone)) return;
|
|
@@ -238,19 +237,18 @@ namespace jeanf.scenemanagement
|
|
|
238
237
|
_lastNotifiedZone = zoneId;
|
|
239
238
|
_currentPlayerZone = zone;
|
|
240
239
|
|
|
241
|
-
// GC ALLOCATION FIX: Only invoke if delegates are not null
|
|
242
240
|
PublishCurrentZoneId?.Invoke(zone.id);
|
|
243
241
|
PublishAppList(zone);
|
|
244
242
|
}
|
|
245
243
|
|
|
246
|
-
private void OnRegionChangedFromECS(
|
|
244
|
+
private void OnRegionChangedFromECS(FixedString128Bytes regionId)
|
|
247
245
|
{
|
|
248
|
-
|
|
249
|
-
if (string.IsNullOrEmpty(
|
|
246
|
+
var id = regionId.ToString();
|
|
247
|
+
if (string.IsNullOrEmpty(id) || _lastNotifiedRegion == regionId) return;
|
|
250
248
|
|
|
251
|
-
if (!_regionDictionary.TryGetValue(
|
|
249
|
+
if (!_regionDictionary.TryGetValue(id, out var region)) return;
|
|
252
250
|
|
|
253
|
-
_lastNotifiedRegion =
|
|
251
|
+
_lastNotifiedRegion = id;
|
|
254
252
|
_currentPlayerRegion = region;
|
|
255
253
|
|
|
256
254
|
OnRegionChange(region);
|
|
@@ -107,12 +107,13 @@ namespace jeanf.scenemanagement
|
|
|
107
107
|
{
|
|
108
108
|
if (zone == null) continue;
|
|
109
109
|
|
|
110
|
+
// CHANGED: Collect ALL volumes for this zone, not just the first one
|
|
110
111
|
foreach (var volumeAuth in volumeAuthorings)
|
|
111
112
|
{
|
|
112
113
|
if (volumeAuth.zone != null && volumeAuth.zone.id.Equals(zone.id))
|
|
113
114
|
{
|
|
114
115
|
regionVolumeMap[region].Add(volumeAuth);
|
|
115
|
-
break
|
|
116
|
+
// REMOVED: break statement - now collects all volumes for the zone
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
}
|
|
@@ -125,35 +126,45 @@ namespace jeanf.scenemanagement
|
|
|
125
126
|
var volumes = kvp.Value;
|
|
126
127
|
var color = _regionColors[region];
|
|
127
128
|
|
|
128
|
-
// Draw volumes
|
|
129
|
-
|
|
129
|
+
// Draw all volumes for this region
|
|
130
|
+
foreach (var volume in volumes)
|
|
130
131
|
{
|
|
131
|
-
|
|
132
|
-
if (currentVolume == null) continue;
|
|
132
|
+
if (volume == null) continue;
|
|
133
133
|
|
|
134
|
-
var
|
|
135
|
-
var
|
|
134
|
+
var pos = volume.transform.position;
|
|
135
|
+
var scale = volume.transform.localScale;
|
|
136
136
|
|
|
137
137
|
Handles.color = color;
|
|
138
|
-
Handles.DrawWireCube(
|
|
138
|
+
Handles.DrawWireCube(pos, scale);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
// Draw connections between
|
|
142
|
-
|
|
141
|
+
// CHANGED: Draw connections between zone centers, not individual volumes
|
|
142
|
+
var zoneVolumeGroups = new Dictionary<string, List<VolumeAuthoring>>();
|
|
143
|
+
foreach (var volume in volumes)
|
|
143
144
|
{
|
|
144
|
-
|
|
145
|
-
if (currentVolume == null) continue;
|
|
145
|
+
if (volume?.zone == null) continue;
|
|
146
146
|
|
|
147
|
-
var
|
|
147
|
+
var zoneId = volume.zone.id.ToString();
|
|
148
|
+
if (!zoneVolumeGroups.ContainsKey(zoneId))
|
|
149
|
+
zoneVolumeGroups[zoneId] = new List<VolumeAuthoring>();
|
|
148
150
|
|
|
149
|
-
|
|
151
|
+
zoneVolumeGroups[zoneId].Add(volume);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Draw connections between zone centers (red dotted lines)
|
|
155
|
+
var zoneIds = zoneVolumeGroups.Keys.ToList();
|
|
156
|
+
for (int i = 0; i < zoneIds.Count; i++)
|
|
157
|
+
{
|
|
158
|
+
for (int j = i + 1; j < zoneIds.Count; j++)
|
|
150
159
|
{
|
|
151
|
-
var
|
|
152
|
-
|
|
160
|
+
var zoneA = zoneVolumeGroups[zoneIds[i]];
|
|
161
|
+
var zoneB = zoneVolumeGroups[zoneIds[j]];
|
|
162
|
+
|
|
163
|
+
var centerA = CalculateZoneCenter(zoneA);
|
|
164
|
+
var centerB = CalculateZoneCenter(zoneB);
|
|
153
165
|
|
|
154
|
-
var otherPos = otherVolume.transform.position;
|
|
155
166
|
Handles.color = Color.red;
|
|
156
|
-
Handles.DrawDottedLine(
|
|
167
|
+
Handles.DrawDottedLine(centerA, centerB, 5f);
|
|
157
168
|
}
|
|
158
169
|
}
|
|
159
170
|
}
|
|
@@ -163,14 +174,15 @@ namespace jeanf.scenemanagement
|
|
|
163
174
|
{
|
|
164
175
|
if (landing.landingZone == null || landing.region == null) continue;
|
|
165
176
|
|
|
166
|
-
|
|
167
|
-
|
|
177
|
+
// CHANGED: Find ALL volumes for the landing zone
|
|
178
|
+
var landingVolumes = System.Array.FindAll(volumeAuthorings,
|
|
179
|
+
v => v.zone != null && v.zone.id.Equals(landing.landingZone.id)).ToList();
|
|
168
180
|
|
|
169
|
-
if (
|
|
181
|
+
if (landingVolumes.Count == 0) continue;
|
|
170
182
|
|
|
171
|
-
var
|
|
183
|
+
var landingCenter = CalculateZoneCenter(landingVolumes);
|
|
172
184
|
|
|
173
|
-
// Connect landing zone to volumes in OTHER regions only
|
|
185
|
+
// Connect landing zone center to volumes in OTHER regions only
|
|
174
186
|
foreach (var kvp in regionVolumeMap)
|
|
175
187
|
{
|
|
176
188
|
var region = kvp.Key;
|
|
@@ -178,13 +190,16 @@ namespace jeanf.scenemanagement
|
|
|
178
190
|
|
|
179
191
|
if (region.id.Equals(landing.region.id)) continue; // Skip same region
|
|
180
192
|
|
|
181
|
-
|
|
193
|
+
// Group volumes by zone and connect to zone centers
|
|
194
|
+
var zoneGroups = volumes.GroupBy(v => v.zone?.id.ToString()).Where(g => !string.IsNullOrEmpty(g.Key));
|
|
195
|
+
|
|
196
|
+
foreach (var zoneGroup in zoneGroups)
|
|
182
197
|
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
var zoneVolumes = zoneGroup.ToList();
|
|
199
|
+
var zoneCenter = CalculateZoneCenter(zoneVolumes);
|
|
200
|
+
|
|
201
|
+
Handles.color = Color.yellow;
|
|
202
|
+
Handles.DrawLine(landingCenter, zoneCenter);
|
|
188
203
|
}
|
|
189
204
|
}
|
|
190
205
|
}
|
|
@@ -194,18 +209,36 @@ namespace jeanf.scenemanagement
|
|
|
194
209
|
{
|
|
195
210
|
if (landing.landingZone == null || landing.region == null) continue;
|
|
196
211
|
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
// CHANGED: Find ALL volumes for the landing zone
|
|
213
|
+
var landingVolumes = System.Array.FindAll(volumeAuthorings,
|
|
214
|
+
v => v.zone != null && v.zone.id.Equals(landing.landingZone.id)).ToList();
|
|
199
215
|
|
|
200
|
-
if (
|
|
216
|
+
if (landingVolumes.Count == 0) continue;
|
|
201
217
|
|
|
202
|
-
|
|
203
|
-
var
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
218
|
+
// Draw all landing zone volumes in blue, actual size
|
|
219
|
+
foreach (var landingVolume in landingVolumes)
|
|
220
|
+
{
|
|
221
|
+
var landingPos = landingVolume.transform.position;
|
|
222
|
+
var landingScale = landingVolume.transform.localScale;
|
|
223
|
+
|
|
224
|
+
Handles.color = Color.blue;
|
|
225
|
+
Handles.DrawWireCube(landingPos, landingScale);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Helper method to calculate the center point of a multi-volume zone
|
|
231
|
+
private Vector3 CalculateZoneCenter(List<VolumeAuthoring> volumes)
|
|
232
|
+
{
|
|
233
|
+
if (volumes.Count == 0) return Vector3.zero;
|
|
234
|
+
if (volumes.Count == 1) return volumes[0].transform.position;
|
|
235
|
+
|
|
236
|
+
Vector3 sum = Vector3.zero;
|
|
237
|
+
foreach (var volume in volumes)
|
|
238
|
+
{
|
|
239
|
+
sum += volume.transform.position;
|
|
208
240
|
}
|
|
241
|
+
return sum / volumes.Count;
|
|
209
242
|
}
|
|
210
243
|
|
|
211
244
|
private void DrawLandingZoneConnections(RegionConnectivity connectivity, VolumeAuthoring[] volumeAuthorings, Dictionary<Region, List<VolumeAuthoring>> regionVolumeMap)
|
|
@@ -214,18 +247,26 @@ namespace jeanf.scenemanagement
|
|
|
214
247
|
{
|
|
215
248
|
if (landing.landingZone == null || landing.region == null) continue;
|
|
216
249
|
|
|
217
|
-
|
|
218
|
-
|
|
250
|
+
// CHANGED: Find ALL volumes for the landing zone
|
|
251
|
+
var landingVolumes = System.Array.FindAll(volumeAuthorings,
|
|
252
|
+
v => v.zone != null && v.zone.id.Equals(landing.landingZone.id)).ToList();
|
|
253
|
+
|
|
254
|
+
if (landingVolumes.Count == 0) continue;
|
|
219
255
|
|
|
220
|
-
|
|
256
|
+
var landingCenter = CalculateZoneCenter(landingVolumes);
|
|
221
257
|
|
|
222
|
-
|
|
223
|
-
var
|
|
258
|
+
// Draw all landing zone volumes
|
|
259
|
+
foreach (var landingVolume in landingVolumes)
|
|
260
|
+
{
|
|
261
|
+
var landingPos = landingVolume.transform.position;
|
|
262
|
+
var landingScale = landingVolume.transform.localScale;
|
|
263
|
+
|
|
264
|
+
Handles.color = Color.white;
|
|
265
|
+
Handles.DrawWireCube(landingPos, landingScale * 1.2f);
|
|
266
|
+
}
|
|
224
267
|
|
|
225
|
-
Handles.
|
|
226
|
-
|
|
227
|
-
Handles.Label(landingPos + Vector3.up * (landingScale.y * 0.8f),
|
|
228
|
-
$"LANDING\n{landing.landingZone.zoneName}",
|
|
268
|
+
Handles.Label(landingCenter + Vector3.up * 2f,
|
|
269
|
+
$"LANDING\n{landing.landingZone.zoneName}\n({landingVolumes.Count} volumes)",
|
|
229
270
|
new GUIStyle(GUI.skin.label) {
|
|
230
271
|
normal = { textColor = Color.white },
|
|
231
272
|
fontStyle = FontStyle.Bold
|
|
@@ -238,13 +279,16 @@ namespace jeanf.scenemanagement
|
|
|
238
279
|
|
|
239
280
|
if (region.id.Equals(landing.region.id)) continue;
|
|
240
281
|
|
|
241
|
-
|
|
282
|
+
// Group volumes by zone and connect to zone centers
|
|
283
|
+
var zoneGroups = volumes.GroupBy(v => v.zone?.id.ToString()).Where(g => !string.IsNullOrEmpty(g.Key));
|
|
284
|
+
|
|
285
|
+
foreach (var zoneGroup in zoneGroups)
|
|
242
286
|
{
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
287
|
+
var zoneVolumes = zoneGroup.ToList();
|
|
288
|
+
var zoneCenter = CalculateZoneCenter(zoneVolumes);
|
|
289
|
+
|
|
290
|
+
Handles.color = Color.yellow;
|
|
291
|
+
Handles.DrawLine(landingCenter, zoneCenter);
|
|
248
292
|
}
|
|
249
293
|
}
|
|
250
294
|
}
|
|
@@ -344,11 +388,13 @@ namespace jeanf.scenemanagement
|
|
|
344
388
|
{
|
|
345
389
|
if (zone == null) continue;
|
|
346
390
|
|
|
391
|
+
// CHANGED: Collect ALL volumes for this zone
|
|
347
392
|
foreach (var volumeAuth in volumeAuthorings)
|
|
348
393
|
{
|
|
349
394
|
if (volumeAuth.zone != null && volumeAuth.zone.id.Equals(zone.id))
|
|
350
395
|
{
|
|
351
396
|
regionVolumeMap[region].Add(volumeAuth);
|
|
397
|
+
// REMOVED: break statement
|
|
352
398
|
}
|
|
353
399
|
}
|
|
354
400
|
}
|
|
@@ -362,26 +408,51 @@ namespace jeanf.scenemanagement
|
|
|
362
408
|
|
|
363
409
|
Handles.color = color;
|
|
364
410
|
|
|
365
|
-
|
|
411
|
+
// Draw all volumes in this region
|
|
412
|
+
foreach (var volume in volumes)
|
|
366
413
|
{
|
|
367
|
-
|
|
368
|
-
if (currentVolume == null) continue;
|
|
414
|
+
if (volume == null) continue;
|
|
369
415
|
|
|
370
|
-
var currentPos =
|
|
371
|
-
var currentScale =
|
|
416
|
+
var currentPos = volume.transform.position;
|
|
417
|
+
var currentScale = volume.transform.localScale;
|
|
372
418
|
|
|
373
419
|
Handles.DrawWireCube(currentPos, currentScale);
|
|
374
|
-
Handles.Label(currentPos + Vector3.up * (currentScale.y * 0.6f),
|
|
375
|
-
$"{region.levelName}\n{currentVolume.zone.zoneName}",
|
|
376
|
-
new GUIStyle(GUI.skin.label) { normal = { textColor = color } });
|
|
377
420
|
|
|
378
|
-
|
|
421
|
+
// Only show label for first volume of each zone to avoid clutter
|
|
422
|
+
var isFirstVolumeOfZone = volumes.Where(v => v?.zone?.id == volume.zone?.id).First() == volume;
|
|
423
|
+
if (isFirstVolumeOfZone)
|
|
379
424
|
{
|
|
380
|
-
|
|
381
|
-
|
|
425
|
+
Handles.Label(currentPos + Vector3.up * (currentScale.y * 0.6f),
|
|
426
|
+
$"{region.levelName}\n{volume.zone.zoneName}",
|
|
427
|
+
new GUIStyle(GUI.skin.label) { normal = { textColor = color } });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// CHANGED: Draw connections between zone centers
|
|
432
|
+
var zoneVolumeGroups = new Dictionary<string, List<VolumeAuthoring>>();
|
|
433
|
+
foreach (var volume in volumes)
|
|
434
|
+
{
|
|
435
|
+
if (volume?.zone == null) continue;
|
|
436
|
+
|
|
437
|
+
var zoneId = volume.zone.id.ToString();
|
|
438
|
+
if (!zoneVolumeGroups.ContainsKey(zoneId))
|
|
439
|
+
zoneVolumeGroups[zoneId] = new List<VolumeAuthoring>();
|
|
440
|
+
|
|
441
|
+
zoneVolumeGroups[zoneId].Add(volume);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
var zoneIds = zoneVolumeGroups.Keys.ToList();
|
|
445
|
+
for (int i = 0; i < zoneIds.Count; i++)
|
|
446
|
+
{
|
|
447
|
+
for (int j = i + 1; j < zoneIds.Count; j++)
|
|
448
|
+
{
|
|
449
|
+
var zoneA = zoneVolumeGroups[zoneIds[i]];
|
|
450
|
+
var zoneB = zoneVolumeGroups[zoneIds[j]];
|
|
451
|
+
|
|
452
|
+
var centerA = CalculateZoneCenter(zoneA);
|
|
453
|
+
var centerB = CalculateZoneCenter(zoneB);
|
|
382
454
|
|
|
383
|
-
|
|
384
|
-
Handles.DrawDottedLine(currentPos, otherPos, 5f);
|
|
455
|
+
Handles.DrawDottedLine(centerA, centerB, 5f);
|
|
385
456
|
}
|
|
386
457
|
}
|
|
387
458
|
}
|
|
@@ -389,24 +460,45 @@ namespace jeanf.scenemanagement
|
|
|
389
460
|
DrawLandingZoneConnections(connectivity, volumeAuthorings, regionVolumeMap);
|
|
390
461
|
}
|
|
391
462
|
|
|
463
|
+
private Vector3 CalculateZoneCenter(List<VolumeAuthoring> volumes)
|
|
464
|
+
{
|
|
465
|
+
if (volumes.Count == 0) return Vector3.zero;
|
|
466
|
+
if (volumes.Count == 1) return volumes[0].transform.position;
|
|
467
|
+
|
|
468
|
+
Vector3 sum = Vector3.zero;
|
|
469
|
+
foreach (var volume in volumes)
|
|
470
|
+
{
|
|
471
|
+
sum += volume.transform.position;
|
|
472
|
+
}
|
|
473
|
+
return sum / volumes.Count;
|
|
474
|
+
}
|
|
475
|
+
|
|
392
476
|
private void DrawLandingZoneConnections(RegionConnectivity connectivity, VolumeAuthoring[] volumeAuthorings, Dictionary<Region, List<VolumeAuthoring>> regionVolumeMap)
|
|
393
477
|
{
|
|
394
478
|
foreach (var landing in connectivity.landingZones)
|
|
395
479
|
{
|
|
396
480
|
if (landing.landingZone == null || landing.region == null) continue;
|
|
397
481
|
|
|
398
|
-
|
|
399
|
-
|
|
482
|
+
// CHANGED: Find ALL volumes for the landing zone
|
|
483
|
+
var landingVolumes = System.Array.FindAll(volumeAuthorings,
|
|
484
|
+
v => v.zone != null && v.zone.id.Equals(landing.landingZone.id)).ToList();
|
|
485
|
+
|
|
486
|
+
if (landingVolumes.Count == 0) continue;
|
|
400
487
|
|
|
401
|
-
|
|
488
|
+
var landingCenter = CalculateZoneCenter(landingVolumes);
|
|
402
489
|
|
|
403
|
-
|
|
404
|
-
var
|
|
490
|
+
// Draw all landing zone volumes
|
|
491
|
+
foreach (var landingVolume in landingVolumes)
|
|
492
|
+
{
|
|
493
|
+
var landingPos = landingVolume.transform.position;
|
|
494
|
+
var landingScale = landingVolume.transform.localScale;
|
|
495
|
+
|
|
496
|
+
Handles.color = Color.white;
|
|
497
|
+
Handles.DrawWireCube(landingPos, landingScale * 1.2f);
|
|
498
|
+
}
|
|
405
499
|
|
|
406
|
-
Handles.
|
|
407
|
-
|
|
408
|
-
Handles.Label(landingPos + Vector3.up * (landingScale.y * 0.8f),
|
|
409
|
-
$"LANDING\n{landing.landingZone.zoneName}",
|
|
500
|
+
Handles.Label(landingCenter + Vector3.up * 2f,
|
|
501
|
+
$"LANDING\n{landing.landingZone.zoneName}\n({landingVolumes.Count} volumes)",
|
|
410
502
|
new GUIStyle(GUI.skin.label) {
|
|
411
503
|
normal = { textColor = Color.white },
|
|
412
504
|
fontStyle = FontStyle.Bold
|
|
@@ -419,13 +511,16 @@ namespace jeanf.scenemanagement
|
|
|
419
511
|
|
|
420
512
|
if (region.id.Equals(landing.region.id)) continue;
|
|
421
513
|
|
|
422
|
-
|
|
514
|
+
// Group by zone and connect to zone centers
|
|
515
|
+
var zoneGroups = volumes.GroupBy(v => v.zone?.id.ToString()).Where(g => !string.IsNullOrEmpty(g.Key));
|
|
516
|
+
|
|
517
|
+
foreach (var zoneGroup in zoneGroups)
|
|
423
518
|
{
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
519
|
+
var zoneVolumes = zoneGroup.ToList();
|
|
520
|
+
var zoneCenter = CalculateZoneCenter(zoneVolumes);
|
|
521
|
+
|
|
522
|
+
Handles.color = Color.yellow;
|
|
523
|
+
Handles.DrawLine(landingCenter, zoneCenter);
|
|
429
524
|
}
|
|
430
525
|
}
|
|
431
526
|
}
|
|
@@ -623,99 +718,100 @@ namespace jeanf.scenemanagement
|
|
|
623
718
|
}
|
|
624
719
|
|
|
625
720
|
var regionColor = _regionColors[currentRegion];
|
|
626
|
-
|
|
627
|
-
|
|
721
|
+
|
|
722
|
+
// CHANGED: Calculate center of the selected zone (which may have multiple volumes)
|
|
723
|
+
var selectedZoneVolumes = System.Array.FindAll(allVolumeAuthorings,
|
|
724
|
+
v => v.zone != null && v.zone.id.Equals(currentZone.id)).ToList();
|
|
725
|
+
var selectedZoneCenter = CalculateZoneCenter(selectedZoneVolumes);
|
|
628
726
|
|
|
629
727
|
// Priority 3: Draw other connections first (same region) - red dotted lines
|
|
630
728
|
foreach (var zone in currentRegion.zonesInThisRegion)
|
|
631
729
|
{
|
|
632
730
|
if (zone == null || zone == currentZone) continue;
|
|
633
731
|
|
|
634
|
-
|
|
732
|
+
// CHANGED: Find ALL volumes for this zone and draw connections to zone center
|
|
733
|
+
var zoneVolumes = System.Array.FindAll(allVolumeAuthorings,
|
|
734
|
+
v => v.zone != null && v.zone.id.Equals(zone.id)).ToList();
|
|
735
|
+
|
|
736
|
+
if (zoneVolumes.Count == 0) continue;
|
|
737
|
+
|
|
738
|
+
var zoneCenter = CalculateZoneCenter(zoneVolumes);
|
|
739
|
+
|
|
740
|
+
// Draw all volumes in this zone
|
|
741
|
+
foreach (var volume in zoneVolumes)
|
|
635
742
|
{
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (volumeAuth.zone.id.Equals(zone.id))
|
|
639
|
-
{
|
|
640
|
-
var otherPos = volumeAuth.transform.position;
|
|
641
|
-
var otherScale = volumeAuth.transform.localScale;
|
|
642
|
-
|
|
643
|
-
// Draw the volume
|
|
644
|
-
Handles.color = regionColor;
|
|
645
|
-
Handles.DrawWireCube(otherPos, otherScale);
|
|
646
|
-
|
|
647
|
-
// Draw potential connection (red dotted line)
|
|
648
|
-
Handles.color = Color.red;
|
|
649
|
-
Handles.DrawDottedLine(selectedPos, otherPos, 5f);
|
|
650
|
-
break; // Only draw one volume per zone
|
|
651
|
-
}
|
|
743
|
+
Handles.color = regionColor;
|
|
744
|
+
Handles.DrawWireCube(volume.transform.position, volume.transform.localScale);
|
|
652
745
|
}
|
|
746
|
+
|
|
747
|
+
// Draw connection line between zone centers
|
|
748
|
+
Handles.color = Color.red;
|
|
749
|
+
Handles.DrawDottedLine(selectedZoneCenter, zoneCenter, 5f);
|
|
653
750
|
}
|
|
654
751
|
|
|
655
|
-
//
|
|
752
|
+
// Handle landing zone connections
|
|
656
753
|
var landingConnection = _foundConnectivity.landingZones.FirstOrDefault(l => l.landingZone == currentZone);
|
|
657
754
|
if (landingConnection != null)
|
|
658
755
|
{
|
|
659
|
-
// This
|
|
756
|
+
// This zone IS a landing zone - show connections to OTHER regions
|
|
660
757
|
foreach (var region in _foundConnectivity.activeRegions)
|
|
661
758
|
{
|
|
662
|
-
if (region == null || region.id.Equals(currentRegion.id)) continue;
|
|
663
|
-
|
|
664
|
-
if (!_regionColors.ContainsKey(region))
|
|
665
|
-
{
|
|
666
|
-
var colorIndex = _regionColors.Count % _predefinedColors.Length;
|
|
667
|
-
_regionColors[region] = _predefinedColors[colorIndex];
|
|
668
|
-
}
|
|
759
|
+
if (region == null || region.id.Equals(currentRegion.id)) continue;
|
|
669
760
|
|
|
670
761
|
foreach (var zone in region.zonesInThisRegion)
|
|
671
762
|
{
|
|
672
763
|
if (zone == null) continue;
|
|
673
764
|
|
|
674
|
-
|
|
765
|
+
var zoneVolumes = System.Array.FindAll(allVolumeAuthorings,
|
|
766
|
+
v => v.zone != null && v.zone.id.Equals(zone.id)).ToList();
|
|
767
|
+
|
|
768
|
+
if (zoneVolumes.Count == 0) continue;
|
|
769
|
+
|
|
770
|
+
var zoneCenter = CalculateZoneCenter(zoneVolumes);
|
|
771
|
+
|
|
772
|
+
// Draw zone volumes
|
|
773
|
+
foreach (var volume in zoneVolumes)
|
|
675
774
|
{
|
|
676
|
-
if (
|
|
677
|
-
|
|
678
|
-
if (volumeAuth.zone.id.Equals(zone.id))
|
|
775
|
+
if (!_regionColors.ContainsKey(region))
|
|
679
776
|
{
|
|
680
|
-
var
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
// Draw the target volume
|
|
684
|
-
Handles.color = _regionColors[region];
|
|
685
|
-
Handles.DrawWireCube(otherPos, otherScale);
|
|
686
|
-
|
|
687
|
-
// Draw ACTUAL landing zone connection (yellow solid line)
|
|
688
|
-
Handles.color = Color.yellow;
|
|
689
|
-
Handles.DrawLine(selectedPos, otherPos);
|
|
690
|
-
break; // Only draw one volume per zone
|
|
777
|
+
var colorIndex = _regionColors.Count % _predefinedColors.Length;
|
|
778
|
+
_regionColors[region] = _predefinedColors[colorIndex];
|
|
691
779
|
}
|
|
780
|
+
|
|
781
|
+
Handles.color = _regionColors[region];
|
|
782
|
+
Handles.DrawWireCube(volume.transform.position, volume.transform.localScale);
|
|
692
783
|
}
|
|
784
|
+
|
|
785
|
+
// Draw landing connection
|
|
786
|
+
Handles.color = Color.yellow;
|
|
787
|
+
Handles.DrawLine(selectedZoneCenter, zoneCenter);
|
|
693
788
|
}
|
|
694
789
|
}
|
|
695
790
|
}
|
|
696
791
|
|
|
697
|
-
//
|
|
792
|
+
// Draw connections FROM other landing zones TO this zone
|
|
698
793
|
foreach (var landing in _foundConnectivity.landingZones)
|
|
699
794
|
{
|
|
700
795
|
if (landing.landingZone == null || landing.region == null) continue;
|
|
701
|
-
if (landing.landingZone == currentZone) continue;
|
|
796
|
+
if (landing.landingZone == currentZone) continue;
|
|
797
|
+
|
|
798
|
+
var landingVolumes = System.Array.FindAll(allVolumeAuthorings,
|
|
799
|
+
v => v.zone != null && v.zone.id.Equals(landing.landingZone.id)).ToList();
|
|
800
|
+
|
|
801
|
+
if (landingVolumes.Count == 0) continue;
|
|
702
802
|
|
|
703
|
-
|
|
704
|
-
|
|
803
|
+
var landingCenter = CalculateZoneCenter(landingVolumes);
|
|
804
|
+
|
|
805
|
+
// Draw landing zone volumes in blue
|
|
806
|
+
foreach (var volume in landingVolumes)
|
|
705
807
|
{
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
if (volumeAuth.zone.id.Equals(landing.landingZone.id))
|
|
709
|
-
{
|
|
710
|
-
var landingPos = volumeAuth.transform.position;
|
|
711
|
-
var landingScale = volumeAuth.transform.localScale;
|
|
712
|
-
|
|
713
|
-
// Draw ACTUAL connection from landing zone to current volume (yellow solid line)
|
|
714
|
-
Handles.color = Color.yellow;
|
|
715
|
-
Handles.DrawLine(landingPos, selectedPos);
|
|
716
|
-
break;
|
|
717
|
-
}
|
|
808
|
+
Handles.color = Color.blue;
|
|
809
|
+
Handles.DrawWireCube(volume.transform.position, volume.transform.localScale);
|
|
718
810
|
}
|
|
811
|
+
|
|
812
|
+
// Draw connection
|
|
813
|
+
Handles.color = Color.yellow;
|
|
814
|
+
Handles.DrawLine(landingCenter, selectedZoneCenter);
|
|
719
815
|
}
|
|
720
816
|
|
|
721
817
|
// Priority 2: Draw landing zones - blue boxes, same size as volume
|
|
@@ -724,34 +820,51 @@ namespace jeanf.scenemanagement
|
|
|
724
820
|
if (landing.landingZone == null || landing.region == null) continue;
|
|
725
821
|
if (landing.landingZone == currentZone) continue; // Skip self (will be drawn as selected)
|
|
726
822
|
|
|
727
|
-
// Find
|
|
728
|
-
|
|
823
|
+
// Find all landing zone volumes
|
|
824
|
+
var landingVolumes = System.Array.FindAll(allVolumeAuthorings,
|
|
825
|
+
v => v.zone != null && v.zone.id.Equals(landing.landingZone.id)).ToList();
|
|
826
|
+
|
|
827
|
+
if (landingVolumes.Count == 0) continue;
|
|
828
|
+
|
|
829
|
+
// Draw landing zone volumes in blue, same size as volume
|
|
830
|
+
foreach (var landingVolume in landingVolumes)
|
|
729
831
|
{
|
|
730
|
-
|
|
832
|
+
var landingPos = landingVolume.transform.position;
|
|
833
|
+
var landingScale = landingVolume.transform.localScale;
|
|
731
834
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
var landingPos = volumeAuth.transform.position;
|
|
735
|
-
var landingScale = volumeAuth.transform.localScale;
|
|
736
|
-
|
|
737
|
-
// Draw landing zone in blue, same size as volume
|
|
738
|
-
Handles.color = Color.blue;
|
|
739
|
-
Handles.DrawWireCube(landingPos, landingScale);
|
|
740
|
-
break;
|
|
741
|
-
}
|
|
835
|
+
Handles.color = Color.blue;
|
|
836
|
+
Handles.DrawWireCube(landingPos, landingScale);
|
|
742
837
|
}
|
|
743
838
|
}
|
|
744
839
|
|
|
745
|
-
// Priority 1: Draw selected
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
840
|
+
// Priority 1: Draw selected zone volumes last - yellow boxes, actual size
|
|
841
|
+
foreach (var selectedVol in selectedZoneVolumes)
|
|
842
|
+
{
|
|
843
|
+
Handles.color = Color.yellow;
|
|
844
|
+
Handles.DrawWireCube(selectedVol.transform.position, selectedVol.transform.localScale);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Add label for the selected zone at the zone center
|
|
848
|
+
Handles.Label(selectedZoneCenter + Vector3.up * 2f,
|
|
849
|
+
$"SELECTED ZONE\n{currentZone.zoneName}\n({selectedZoneVolumes.Count} volumes)",
|
|
750
850
|
new GUIStyle(GUI.skin.label) {
|
|
751
851
|
normal = { textColor = Color.yellow },
|
|
752
852
|
fontStyle = FontStyle.Bold
|
|
753
853
|
});
|
|
754
854
|
}
|
|
855
|
+
|
|
856
|
+
private Vector3 CalculateZoneCenter(List<VolumeAuthoring> volumes)
|
|
857
|
+
{
|
|
858
|
+
if (volumes.Count == 0) return Vector3.zero;
|
|
859
|
+
if (volumes.Count == 1) return volumes[0].transform.position;
|
|
860
|
+
|
|
861
|
+
Vector3 sum = Vector3.zero;
|
|
862
|
+
foreach (var volume in volumes)
|
|
863
|
+
{
|
|
864
|
+
sum += volume.transform.position;
|
|
865
|
+
}
|
|
866
|
+
return sum / volumes.Count;
|
|
867
|
+
}
|
|
755
868
|
}
|
|
756
869
|
|
|
757
870
|
[CustomPropertyDrawer(typeof(LandingZoneData))]
|
|
@@ -25,13 +25,10 @@ namespace jeanf.scenemanagement
|
|
|
25
25
|
private FixedString128Bytes _lastNotifiedRegion;
|
|
26
26
|
|
|
27
27
|
private NativeHashMap<FixedString128Bytes, FixedString128Bytes> _zoneToRegionMap;
|
|
28
|
-
private NativeHashMap<FixedString128Bytes,
|
|
28
|
+
private NativeHashMap<FixedString128Bytes, NativeArray<FixedString128Bytes>> _precomputedCheckableZones;
|
|
29
29
|
private NativeHashSet<FixedString128Bytes> _landingZones;
|
|
30
|
+
private NativeArray<FixedString128Bytes> _allZones;
|
|
30
31
|
private bool _precomputedDataInitialized;
|
|
31
|
-
|
|
32
|
-
// GC ALLOCATION FIX: Cache string conversions
|
|
33
|
-
private FixedString128Bytes _lastZoneStringConverted;
|
|
34
|
-
private FixedString128Bytes _lastRegionStringConverted;
|
|
35
32
|
|
|
36
33
|
[BurstCompile]
|
|
37
34
|
public void OnCreate(ref SystemState state)
|
|
@@ -44,15 +41,13 @@ namespace jeanf.scenemanagement
|
|
|
44
41
|
_toUnloadList = new NativeList<(Entity, LevelInfo)>(10, Allocator.Persistent);
|
|
45
42
|
_checkableZoneIds = new NativeHashSet<FixedString128Bytes>(50, Allocator.Persistent);
|
|
46
43
|
_zoneToRegionMap = new NativeHashMap<FixedString128Bytes, FixedString128Bytes>(100, Allocator.Persistent);
|
|
47
|
-
|
|
44
|
+
_precomputedCheckableZones = new NativeHashMap<FixedString128Bytes, NativeArray<FixedString128Bytes>>(100, Allocator.Persistent);
|
|
48
45
|
_landingZones = new NativeHashSet<FixedString128Bytes>(50, Allocator.Persistent);
|
|
49
46
|
|
|
50
47
|
_currentPlayerZone = new FixedString128Bytes();
|
|
51
48
|
_currentPlayerRegion = new FixedString128Bytes();
|
|
52
49
|
_lastNotifiedZone = new FixedString128Bytes();
|
|
53
50
|
_lastNotifiedRegion = new FixedString128Bytes();
|
|
54
|
-
_lastZoneStringConverted = new FixedString128Bytes();
|
|
55
|
-
_lastRegionStringConverted = new FixedString128Bytes();
|
|
56
51
|
_precomputedDataInitialized = false;
|
|
57
52
|
|
|
58
53
|
_relevantQuery = SystemAPI.QueryBuilder().WithAll<Relevant, LocalToWorld>().Build();
|
|
@@ -68,8 +63,20 @@ namespace jeanf.scenemanagement
|
|
|
68
63
|
if (_toUnloadList.IsCreated) _toUnloadList.Dispose();
|
|
69
64
|
if (_checkableZoneIds.IsCreated) _checkableZoneIds.Dispose();
|
|
70
65
|
if (_zoneToRegionMap.IsCreated) _zoneToRegionMap.Dispose();
|
|
71
|
-
if (_zoneToCheckableIndex.IsCreated) _zoneToCheckableIndex.Dispose();
|
|
72
66
|
if (_landingZones.IsCreated) _landingZones.Dispose();
|
|
67
|
+
if (_allZones.IsCreated) _allZones.Dispose();
|
|
68
|
+
|
|
69
|
+
if (_precomputedCheckableZones.IsCreated)
|
|
70
|
+
{
|
|
71
|
+
var enumerator = _precomputedCheckableZones.GetEnumerator();
|
|
72
|
+
while (enumerator.MoveNext())
|
|
73
|
+
{
|
|
74
|
+
if (enumerator.Current.Value.IsCreated)
|
|
75
|
+
enumerator.Current.Value.Dispose();
|
|
76
|
+
}
|
|
77
|
+
enumerator.Dispose();
|
|
78
|
+
_precomputedCheckableZones.Dispose();
|
|
79
|
+
}
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
[BurstCompile]
|
|
@@ -79,36 +86,28 @@ namespace jeanf.scenemanagement
|
|
|
79
86
|
_toLoadList.Clear();
|
|
80
87
|
_toUnloadList.Clear();
|
|
81
88
|
|
|
82
|
-
// GC ALLOCATION FIX: Use try-finally for guaranteed disposal
|
|
83
89
|
var relevantPositions = _relevantQuery.ToComponentDataArray<LocalToWorld>(Allocator.TempJob);
|
|
84
|
-
|
|
90
|
+
|
|
91
|
+
if (relevantPositions.Length == 0)
|
|
85
92
|
{
|
|
86
|
-
if (relevantPositions.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
var playerPosition = relevantPositions[0].Position;
|
|
92
|
-
|
|
93
|
-
if (!_precomputedDataInitialized)
|
|
94
|
-
{
|
|
95
|
-
LoadPrecomputedData(ref state);
|
|
96
|
-
_precomputedDataInitialized = true;
|
|
97
|
-
}
|
|
93
|
+
if (relevantPositions.IsCreated) relevantPositions.Dispose();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
98
96
|
|
|
99
|
-
|
|
97
|
+
var playerPosition = relevantPositions[0].Position;
|
|
100
98
|
|
|
101
|
-
|
|
102
|
-
CheckForZoneAndRegionChange(newPlayerZone);
|
|
103
|
-
ProcessLevelLoadingStates(ref state);
|
|
104
|
-
}
|
|
105
|
-
finally
|
|
99
|
+
if (!_precomputedDataInitialized)
|
|
106
100
|
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
relevantPositions.Dispose();
|
|
110
|
-
}
|
|
101
|
+
LoadPrecomputedData(ref state);
|
|
102
|
+
_precomputedDataInitialized = true;
|
|
111
103
|
}
|
|
104
|
+
|
|
105
|
+
SetCheckableZones();
|
|
106
|
+
var newPlayerZone = CheckVolumesForPlayerZone(ref state, playerPosition);
|
|
107
|
+
CheckForZoneAndRegionChange(newPlayerZone);
|
|
108
|
+
ProcessLevelLoadingStates(ref state);
|
|
109
|
+
|
|
110
|
+
if (relevantPositions.IsCreated) relevantPositions.Dispose();
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
[BurstCompile]
|
|
@@ -122,10 +121,10 @@ namespace jeanf.scenemanagement
|
|
|
122
121
|
if (!ShouldCheckVolume(volume.ValueRO.ZoneId))
|
|
123
122
|
continue;
|
|
124
123
|
|
|
125
|
-
var range = volume.ValueRO.Scale
|
|
124
|
+
var range = volume.ValueRO.Scale * 0.5f;
|
|
126
125
|
var pos = transform.ValueRO.Position;
|
|
127
126
|
var distance = math.abs(playerPosition - pos);
|
|
128
|
-
var insideAxis =
|
|
127
|
+
var insideAxis = distance < range;
|
|
129
128
|
|
|
130
129
|
if (insideAxis.x && insideAxis.y && insideAxis.z)
|
|
131
130
|
{
|
|
@@ -145,18 +144,28 @@ namespace jeanf.scenemanagement
|
|
|
145
144
|
private void LoadPrecomputedData(ref SystemState state)
|
|
146
145
|
{
|
|
147
146
|
_zoneToRegionMap.Clear();
|
|
148
|
-
_zoneToCheckableIndex.Clear();
|
|
149
147
|
_landingZones.Clear();
|
|
148
|
+
|
|
149
|
+
var enumerator = _precomputedCheckableZones.GetEnumerator();
|
|
150
|
+
while (enumerator.MoveNext())
|
|
151
|
+
{
|
|
152
|
+
if (enumerator.Current.Value.IsCreated)
|
|
153
|
+
enumerator.Current.Value.Dispose();
|
|
154
|
+
}
|
|
155
|
+
enumerator.Dispose();
|
|
156
|
+
_precomputedCheckableZones.Clear();
|
|
150
157
|
|
|
151
158
|
var precomputedEntity = _precomputedDataQuery.GetSingletonEntity();
|
|
152
159
|
var precomputedBuffer = SystemAPI.GetBuffer<PrecomputedVolumeDataBuffer>(precomputedEntity);
|
|
153
160
|
|
|
154
|
-
|
|
161
|
+
var tempZoneList = new NativeList<FixedString128Bytes>(100, Allocator.Temp);
|
|
162
|
+
|
|
155
163
|
foreach (var entry in precomputedBuffer)
|
|
156
164
|
{
|
|
157
165
|
if (entry.isZoneRegionMapping && !entry.zoneId.IsEmpty && !entry.regionId.IsEmpty)
|
|
158
166
|
{
|
|
159
167
|
_zoneToRegionMap.TryAdd(entry.zoneId, entry.regionId);
|
|
168
|
+
tempZoneList.Add(entry.zoneId);
|
|
160
169
|
}
|
|
161
170
|
else if (entry.isLandingZone && !entry.landingZoneId.IsEmpty)
|
|
162
171
|
{
|
|
@@ -164,89 +173,86 @@ namespace jeanf.scenemanagement
|
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
175
|
|
|
167
|
-
|
|
176
|
+
_allZones = tempZoneList.ToArray(Allocator.Persistent);
|
|
177
|
+
tempZoneList.Dispose();
|
|
178
|
+
|
|
179
|
+
BuildPrecomputedCheckableZones(ref state, precomputedBuffer);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
[BurstCompile]
|
|
183
|
+
private void BuildPrecomputedCheckableZones(ref SystemState state, DynamicBuffer<PrecomputedVolumeDataBuffer> precomputedBuffer)
|
|
184
|
+
{
|
|
168
185
|
for (int i = 0; i < precomputedBuffer.Length; i++)
|
|
169
186
|
{
|
|
170
187
|
var entry = precomputedBuffer[i];
|
|
171
188
|
if (entry.isHeader && !entry.primaryZoneId.IsEmpty)
|
|
172
189
|
{
|
|
173
|
-
|
|
190
|
+
var tempList = new NativeList<FixedString128Bytes>(entry.count + _landingZones.Count, Allocator.Temp);
|
|
191
|
+
|
|
192
|
+
for (int j = 0; j < entry.count; j++)
|
|
193
|
+
{
|
|
194
|
+
var dataIndex = entry.startIndex + j;
|
|
195
|
+
if (dataIndex < precomputedBuffer.Length)
|
|
196
|
+
{
|
|
197
|
+
var dataEntry = precomputedBuffer[dataIndex];
|
|
198
|
+
if (dataEntry.isData && !dataEntry.checkableZoneId.IsEmpty)
|
|
199
|
+
{
|
|
200
|
+
tempList.Add(dataEntry.checkableZoneId);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
var landingEnumerator = _landingZones.GetEnumerator();
|
|
206
|
+
while (landingEnumerator.MoveNext())
|
|
207
|
+
{
|
|
208
|
+
tempList.Add(landingEnumerator.Current);
|
|
209
|
+
}
|
|
210
|
+
landingEnumerator.Dispose();
|
|
211
|
+
|
|
212
|
+
var checkableArray = tempList.ToArray(Allocator.Persistent);
|
|
213
|
+
tempList.Dispose();
|
|
214
|
+
|
|
215
|
+
_precomputedCheckableZones.TryAdd(entry.primaryZoneId, checkableArray);
|
|
174
216
|
}
|
|
175
217
|
}
|
|
176
218
|
}
|
|
177
219
|
|
|
178
220
|
[BurstCompile]
|
|
179
|
-
private void
|
|
221
|
+
private void SetCheckableZones()
|
|
180
222
|
{
|
|
181
223
|
_checkableZoneIds.Clear();
|
|
182
224
|
|
|
183
225
|
if (_currentPlayerZone.IsEmpty)
|
|
184
226
|
{
|
|
185
|
-
|
|
186
|
-
// GC ALLOCATION FIX: Use manual enumeration instead of foreach
|
|
187
|
-
var enumerator = _zoneToRegionMap.GetEnumerator();
|
|
188
|
-
while (enumerator.MoveNext())
|
|
227
|
+
for (int i = 0; i < _allZones.Length; i++)
|
|
189
228
|
{
|
|
190
|
-
_checkableZoneIds.Add(
|
|
229
|
+
_checkableZoneIds.Add(_allZones[i]);
|
|
191
230
|
}
|
|
192
|
-
enumerator.Dispose();
|
|
193
231
|
return;
|
|
194
232
|
}
|
|
195
233
|
|
|
196
|
-
|
|
197
|
-
if (_zoneToCheckableIndex.TryGetValue(_currentPlayerZone, out var headerIndex))
|
|
234
|
+
if (_precomputedCheckableZones.TryGetValue(_currentPlayerZone, out var checkableArray))
|
|
198
235
|
{
|
|
199
|
-
|
|
200
|
-
var precomputedBuffer = SystemAPI.GetBuffer<PrecomputedVolumeDataBuffer>(precomputedEntity);
|
|
201
|
-
|
|
202
|
-
if (headerIndex < precomputedBuffer.Length)
|
|
236
|
+
for (int i = 0; i < checkableArray.Length; i++)
|
|
203
237
|
{
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
for (int i = 0; i < header.count; i++)
|
|
207
|
-
{
|
|
208
|
-
var dataIndex = header.startIndex + i;
|
|
209
|
-
if (dataIndex < precomputedBuffer.Length)
|
|
210
|
-
{
|
|
211
|
-
var dataEntry = precomputedBuffer[dataIndex];
|
|
212
|
-
if (dataEntry.isData && !dataEntry.checkableZoneId.IsEmpty)
|
|
213
|
-
{
|
|
214
|
-
_checkableZoneIds.Add(dataEntry.checkableZoneId);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
238
|
+
_checkableZoneIds.Add(checkableArray[i]);
|
|
218
239
|
}
|
|
219
240
|
}
|
|
220
|
-
|
|
221
|
-
// Always add landing zones
|
|
222
|
-
// GC ALLOCATION FIX: Use manual enumeration instead of foreach
|
|
223
|
-
var landingEnumerator = _landingZones.GetEnumerator();
|
|
224
|
-
while (landingEnumerator.MoveNext())
|
|
225
|
-
{
|
|
226
|
-
_checkableZoneIds.Add(landingEnumerator.Current);
|
|
227
|
-
}
|
|
228
|
-
landingEnumerator.Dispose();
|
|
229
241
|
}
|
|
230
242
|
|
|
231
243
|
[BurstCompile]
|
|
232
244
|
private bool ShouldCheckVolume(FixedString128Bytes zoneId)
|
|
233
245
|
{
|
|
234
246
|
if (zoneId.IsEmpty) return false;
|
|
235
|
-
|
|
236
|
-
if (_currentPlayerZone.IsEmpty)
|
|
237
|
-
{
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
|
|
247
|
+
if (_currentPlayerZone.IsEmpty) return true;
|
|
241
248
|
return _checkableZoneIds.Contains(zoneId);
|
|
242
249
|
}
|
|
243
250
|
|
|
244
|
-
// GC ALLOCATION FIX: Remove [BurstCompile] and optimize string conversions
|
|
245
251
|
private void CheckForZoneAndRegionChange(FixedString128Bytes newPlayerZone)
|
|
246
252
|
{
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
var zoneChanged = !_currentPlayerZone.Equals(newPlayerZone);
|
|
254
|
+
var regionChanged = false;
|
|
255
|
+
var newPlayerRegion = new FixedString128Bytes();
|
|
250
256
|
|
|
251
257
|
if (!newPlayerZone.IsEmpty && _zoneToRegionMap.TryGetValue(newPlayerZone, out newPlayerRegion))
|
|
252
258
|
{
|
|
@@ -263,31 +269,16 @@ namespace jeanf.scenemanagement
|
|
|
263
269
|
_currentPlayerRegion = newPlayerRegion;
|
|
264
270
|
}
|
|
265
271
|
|
|
266
|
-
// GC ALLOCATION FIX: Only convert to string when absolutely necessary
|
|
267
272
|
if (zoneChanged && !newPlayerZone.IsEmpty && !_lastNotifiedZone.Equals(newPlayerZone))
|
|
268
273
|
{
|
|
269
274
|
_lastNotifiedZone = newPlayerZone;
|
|
270
|
-
|
|
271
|
-
// Only convert if different from last conversion
|
|
272
|
-
if (!_lastZoneStringConverted.Equals(newPlayerZone))
|
|
273
|
-
{
|
|
274
|
-
_lastZoneStringConverted = newPlayerZone;
|
|
275
|
-
var zoneString = newPlayerZone.ToString();
|
|
276
|
-
WorldManager.NotifyZoneChangeFromECS(zoneString);
|
|
277
|
-
}
|
|
275
|
+
WorldManager.NotifyZoneChangeFromECS(newPlayerZone);
|
|
278
276
|
}
|
|
279
277
|
|
|
280
278
|
if (regionChanged && !newPlayerRegion.IsEmpty && !_lastNotifiedRegion.Equals(newPlayerRegion))
|
|
281
279
|
{
|
|
282
280
|
_lastNotifiedRegion = newPlayerRegion;
|
|
283
|
-
|
|
284
|
-
// Only convert if different from last conversion
|
|
285
|
-
if (!_lastRegionStringConverted.Equals(newPlayerRegion))
|
|
286
|
-
{
|
|
287
|
-
_lastRegionStringConverted = newPlayerRegion;
|
|
288
|
-
var regionString = newPlayerRegion.ToString();
|
|
289
|
-
WorldManager.NotifyRegionChangeFromECS(regionString);
|
|
290
|
-
}
|
|
281
|
+
WorldManager.NotifyRegionChangeFromECS(newPlayerRegion);
|
|
291
282
|
}
|
|
292
283
|
}
|
|
293
284
|
|
|
@@ -299,9 +290,9 @@ namespace jeanf.scenemanagement
|
|
|
299
290
|
.WithEntityAccess())
|
|
300
291
|
{
|
|
301
292
|
bool shouldLoad = false;
|
|
302
|
-
|
|
293
|
+
for (int i = 0; i < volumes.Length; i++)
|
|
303
294
|
{
|
|
304
|
-
if (_activeVolumes.Contains(
|
|
295
|
+
if (_activeVolumes.Contains(volumes[i].volumeEntity))
|
|
305
296
|
{
|
|
306
297
|
shouldLoad = true;
|
|
307
298
|
break;
|
|
@@ -318,21 +309,21 @@ namespace jeanf.scenemanagement
|
|
|
318
309
|
}
|
|
319
310
|
}
|
|
320
311
|
|
|
321
|
-
|
|
312
|
+
for (int i = 0; i < _toLoadList.Length; i++)
|
|
322
313
|
{
|
|
323
|
-
var
|
|
324
|
-
streamingData
|
|
325
|
-
|
|
326
|
-
state.EntityManager.SetComponentData(
|
|
314
|
+
var toLoad = _toLoadList[i];
|
|
315
|
+
var streamingData = toLoad.Item2;
|
|
316
|
+
streamingData.runtimeEntity = SceneSystem.LoadSceneAsync(state.WorldUnmanaged, streamingData.sceneReference);
|
|
317
|
+
state.EntityManager.SetComponentData(toLoad.Item1, streamingData);
|
|
327
318
|
}
|
|
328
319
|
|
|
329
|
-
|
|
320
|
+
for (int i = 0; i < _toUnloadList.Length; i++)
|
|
330
321
|
{
|
|
331
|
-
var
|
|
332
|
-
|
|
333
|
-
|
|
322
|
+
var toUnload = _toUnloadList[i];
|
|
323
|
+
var streamingData = toUnload.Item2;
|
|
324
|
+
SceneSystem.UnloadScene(state.WorldUnmanaged, streamingData.runtimeEntity, SceneSystem.UnloadParameters.DestroyMetaEntities);
|
|
334
325
|
streamingData.runtimeEntity = Entity.Null;
|
|
335
|
-
state.EntityManager.SetComponentData(
|
|
326
|
+
state.EntityManager.SetComponentData(toUnload.Item1, streamingData);
|
|
336
327
|
}
|
|
337
328
|
}
|
|
338
329
|
}
|
|
@@ -547,17 +547,17 @@ PrefabInstance:
|
|
|
547
547
|
- target: {fileID: 7951339239912631404, guid: e87a4cab69290cb40805f373b8ccd8d7,
|
|
548
548
|
type: 3}
|
|
549
549
|
propertyPath: m_LocalPosition.x
|
|
550
|
-
value:
|
|
550
|
+
value: 0
|
|
551
551
|
objectReference: {fileID: 0}
|
|
552
552
|
- target: {fileID: 7951339239912631404, guid: e87a4cab69290cb40805f373b8ccd8d7,
|
|
553
553
|
type: 3}
|
|
554
554
|
propertyPath: m_LocalPosition.y
|
|
555
|
-
value:
|
|
555
|
+
value: 0
|
|
556
556
|
objectReference: {fileID: 0}
|
|
557
557
|
- target: {fileID: 7951339239912631404, guid: e87a4cab69290cb40805f373b8ccd8d7,
|
|
558
558
|
type: 3}
|
|
559
559
|
propertyPath: m_LocalPosition.z
|
|
560
|
-
value:
|
|
560
|
+
value: 0
|
|
561
561
|
objectReference: {fileID: 0}
|
|
562
562
|
- target: {fileID: 7951339239912631404, guid: e87a4cab69290cb40805f373b8ccd8d7,
|
|
563
563
|
type: 3}
|
|
@@ -2114,7 +2114,7 @@ PrefabInstance:
|
|
|
2114
2114
|
- target: {fileID: 2423214379621556911, guid: 15d66d68a4116d243b7bb3b273842708,
|
|
2115
2115
|
type: 3}
|
|
2116
2116
|
propertyPath: m_LocalPosition.y
|
|
2117
|
-
value:
|
|
2117
|
+
value: 1.98
|
|
2118
2118
|
objectReference: {fileID: 0}
|
|
2119
2119
|
- target: {fileID: 2423214379621556911, guid: 15d66d68a4116d243b7bb3b273842708,
|
|
2120
2120
|
type: 3}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fr.jeanf.scenemanagement",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"displayName": "Scene Management",
|
|
5
5
|
"description": "This package contains two scene loading system, one is additive, the other is to load subscenes. \nBoth system are living side-by-side.\nThe dynamic systems handles the loading of all static content (environment) using subscenes.\nThe additive system loads scene additively depending on zone & region and upon scenario load/unload requests. Each region or scenario can have dependency that will remain loaded until either a region or a scenario became irrelevant.",
|
|
6
6
|
"unity": "2021.3",
|