using UnityEngine; using System.Collections; using System.Collections.Generic; using System; using System.Linq; using UnityEngine.SceneManagement; #if AT_STREAMER2 using Atavism; #endif namespace WorldStreamer2 { /// /// Streams async scene tiles /// /// Activates/deactivates streamer. /// [Tooltip("This checkbox deactivates streamer and unload or doesn't load it's data.")] public bool streamerActive = true; /// /// The streamer tag. /// public static string STREAMERTAG = "SceneStreamer"; // [HideInInspector] [Tooltip("Drag and drop here your scene collection prefab. You could find it in catalogue with scenes which were generated by scene splitter.")] /// /// The scene collection of tiles. /// public List sceneCollectionManagers; [Header("Settings")] [Tooltip("Frequancy in seconds in which you want to check if grid element is close /far enough to load/unload.")] /// /// How often streamer checks player position. /// public float positionCheckTime = 0.1f; [Tooltip("Time in seconds after which grid element will be unloaded.")] /// /// Destroys unloaded tiles after seconds. /// public float destroyTileDelay = 2; [Tooltip("Number of empty frames between loading actions.")] /// /// The async scene load wait frames. /// public int sceneLoadWaitFrames = 2; [Space(10)] [Tooltip("If you want to fix small holes from LODs system at unity terrain borders, drag and drop object here from scene hierarchy that contains our \"Terrain Neighbours\" script.")] /// /// The terrain neighbours manager. /// public TerrainNeighbours terrainNeighbours; [Space(10)] [Tooltip("Enable looping system, each layer is streamed independently, so if you want to synchronize them, they should have the same XYZ size. More info at manual.")] /// /// Is world looping on. /// public bool looping = false; /// /// Override scene split range limit /// public bool overideRangeLimit = true; /// /// Override scene limits in scene collections /// public bool overideScenesLimits = true; [Space(10)] [Header("Player Settings")] [Tooltip("Drag and drop here, an object that system have to follow during streaming process.")] /// /// The player transform. /// [SerializeField] private Transform _player; public Transform player { get { if (spawnedPlayer && _player == null && !string.IsNullOrEmpty(playerTag)) { GameObject playerGO = GameObject.FindGameObjectWithTag(playerTag); if (playerGO != null) _player = playerGO.transform; } return _player; } set => _player = value; } [Tooltip("Streamer will wait for player spawn and fill it automatically")] /// /// Streamer will wait for player spawn and fill it automatically /// public bool spawnedPlayer; [HideInInspector] public string playerTag = "Player"; [HideInInspector] /// /// The show loading screen on start? /// public bool showLoadingScreen = true; [HideInInspector] /// /// The loading screen UI. /// public UILoadingStreamer loadingStreamer; [HideInInspector] public bool initialized = false; [HideInInspector] /// /// The tiles to load. /// public int tilesToLoad = int.MaxValue; [HideInInspector] /// /// The tiles loaded. /// public int tilesLoaded; /// /// Gets the loading progress. /// /// The loading progress. public float LoadingProgress { get { return (tilesToLoad > 0) ? tilesLoaded / (float)tilesToLoad : 1; } } /// /// The world mover. /// [HideInInspector] public WorldMover worldMover; [HideInInspector] /// /// The current move. /// public Vector3 currentMove = Vector3.zero; [HideInInspector] public bool xLimitsOverrideIs = false; [HideInInspector] public bool yLimitsOverrideIs = false; [HideInInspector] public bool zLimitsOverrideIs = false; [HideInInspector] public Vector2Int xLimitsOverride = new Vector2Int(-100000, 100000); [HideInInspector] public Vector2Int yLimitsOverride = new Vector2Int(-100000, 100000); [HideInInspector] public Vector2Int zLimitsOverride = new Vector2Int(-100000, 100000); /// /// The scenes to load. /// List scenesToLoad = new List(); /// /// The scene load frame next. /// int sceneLoadFrameNext = 0; /// /// The scene load frames next waited. /// bool sceneLoadFramesNextWaited = false; public static StreamerLoadingManager loadingManager; static bool canUnload = true; static float waitTillNextUnload = 20; static bool unloadNext = false; /// /// Awakes this instance and resets player position; /// void Awake() { //if (spawnedPlayer) //{ // player = null; //} #if AT_STREAMER2 if (ClientAPI.GetPlayerObject() != null) { GameObject go = ClientAPI.GetPlayerObject().GameObject; if (go != null) player = go.transform; } #endif foreach (var sceneCollectionManager in sceneCollectionManagers) { sceneCollectionManager.ResetPosition(); } } /// /// Start this instance, prepares scene collection into scene array, starts player position checker /// void Start() { Debug.Log($"loadingManager {loadingManager}"); if (loadingManager != null) Debug.Log($"loadingManager streamer {loadingManager.Streamer}"); loadingManager = new StreamerLoadingManager() { Streamer = this }; Debug.Log($"New streamer loading manager {loadingManager.Streamer}"); if (sceneCollectionManagers != null && sceneCollectionManagers.Count > 0) { PrepareScenesArray(); StartCoroutine(PositionChecker()); canUnload = true; } /*else { enabled = false; Debug.LogError("No scene collection in streamer"); }*/ } #region prepare scene /// /// Prepares the scenes array from collection /// void PrepareScenesArray() { Vector2Int xLimits = Vector2Int.zero; Vector2Int yLimits = Vector2Int.zero; Vector2Int zLimits = Vector2Int.zero; if (sceneCollectionManagers.Count > 1) { sceneCollectionManagers[0].GetSceneCollectionWolrdLimits(out xLimits, out yLimits, out zLimits); } if (xLimitsOverrideIs) { xLimits = xLimitsOverride; } if (yLimitsOverrideIs) { yLimits = yLimitsOverride; } if (zLimitsOverrideIs) { zLimits = zLimitsOverride; } Debug.Log(xLimits + " " + yLimits + " " + zLimits); foreach (var sceneCollectionManager in sceneCollectionManagers) { sceneCollectionManager.unloadRangeConnectParent = null; } foreach (var sceneCollectionManager in sceneCollectionManagers) { if (sceneCollectionManager.useUnloadRangeConnect && sceneCollectionManager.unloadRangeConnect != null) { sceneCollectionManager.unloadRangeConnect.unloadRangeConnectParent = sceneCollectionManager; } if (overideScenesLimits && sceneCollectionManagers.Count > 1) { sceneCollectionManager.CalculateLoadingLimits(looping, looping && overideRangeLimit, looping && overideScenesLimits, xLimits, yLimits, zLimits); sceneCollectionManager.PrepareScenesArray(overideRangeLimit, overideScenesLimits, xLimits, yLimits, zLimits); } else { sceneCollectionManager.CalculateLoadingLimits(looping, looping && overideRangeLimit); sceneCollectionManager.PrepareScenesArray(looping && overideRangeLimit); } } } /// /// Converts scene name into position /// /// Scene name. /// Position x. /// Position y. /// Position z. public static void SceneNameToPos(SceneCollectionManager sceneCollectionManager, string sceneName, out int posX, out int posY, out int posZ) { posX = 0; posY = 0; posZ = 0; string[] values = sceneName.Replace(sceneCollectionManager.prefixScene, "").Replace(".unity", "").Split(new char[] { '_' }, System.StringSplitOptions.RemoveEmptyEntries); foreach (var item in values) { if (item[0] == 'x') { posX = int.Parse(item.Replace("x", "")); } if (item[0] == 'y') { posY = int.Parse(item.Replace("y", "")); } if (item[0] == 'z') { posZ = int.Parse(item.Replace("z", "")); } } } #endregion public static int mod(int x, int m) { return (x % m + m) % m; } #region scene loading // called second public void OnSceneLoaded(Scene scene, SceneSplit split) { GameObject[] rootGameObjects = scene.GetRootGameObjects(); if (rootGameObjects.Length > 0) { AddSceneGoMin(split, rootGameObjects[0]); } } public void AddSceneGoMin(SceneSplit split, GameObject sceneGo) { SceneCollectionManager sceneCollectionManager = split.sceneCollectionManager; int posX = 0; int posY = 0; int posZ = 0; SceneNameToPos(split.sceneCollectionManager, split.sceneName, out posX, out posY, out posZ); Vector3Int posInt = new Vector3Int(posX, posY, posZ); SceneSplitManager sceneSplitManager = sceneGo.GetComponent(); tilesLoaded++; float x = split.posX - posInt.x; float y = split.posY - posInt.y; float z = split.posZ - posInt.z; Vector3 posSplit = new Vector3(sceneCollectionManager.xSize * x, sceneCollectionManager.ySize * y, sceneCollectionManager.zSize * z) + currentMove + new Vector3(split.posXLimitMove, split.posYLimitMove, split.posZLimitMove); if (split.sceneGo != null) { Debug.Log("Scene already loaded " + split.sceneName); //return; } // Debug.Log("Added " + posInt + " " + posIntMoved); BaseSplitSetup(split, sceneGo); sceneGo.transform.position = posSplit; sceneGo.name += $"split loaded on pos {posInt.x} {posInt.y} {posInt.z}"; SceneCollectionManager unloadSceneCollectionManager = null; if (sceneCollectionManager.useUnloadRangeConnect && sceneCollectionManager.unloadRangeConnect != null) { unloadSceneCollectionManager = sceneCollectionManager.unloadRangeConnect; } if (sceneCollectionManager.unloadRangeConnectParent != null && sceneCollectionManager.unloadRangeConnectParent.useUnloadRangeConnect) { unloadSceneCollectionManager = sceneCollectionManager.unloadRangeConnectParent; } if (unloadSceneCollectionManager != null) { SceneSplit splitCheck; float count = unloadSceneCollectionManager.loadedScenes.Count; for (int i = 0; i < count; i++) { splitCheck = unloadSceneCollectionManager.loadedScenes[i]; if (splitCheck.loadingFinished) { if (split.basePosX == splitCheck.basePosX && split.basePosY == splitCheck.basePosY && split.basePosZ == splitCheck.basePosZ) { if (posSplit == splitCheck.sceneGo.transform.position) { //Debug.Log("----Unloaded " + split.sceneName); UnloadScenesSync(splitCheck); break; } } } } } } public static void BaseSplitSetup(SceneSplit split, GameObject sceneGo) { split.sceneGo = sceneGo; split.scene = sceneGo.scene; split.loadingFinished = true; split.sceneCollectionManager.currentlySceneLoading--; } #endregion #region update functions /// /// Update this instance, starts load level async /// void Update() { LoadLevelAsyncManage(); loadingManager.Update(); } /// /// Manages async scene loading /// void LoadLevelAsyncManage() { //Debug.Log("--------------------------------" + scenesToLoad.Count); if (scenesToLoad.Count > 0) { if (LoadingProgress < 1 || sceneLoadFramesNextWaited && sceneLoadFrameNext <= 0) { //Debug.Log(LoadingProgress); sceneLoadFramesNextWaited = false; sceneLoadFrameNext = sceneLoadWaitFrames; scenesToLoad = scenesToLoad.OrderBy(x => x.sceneCollectionManager.priority).ToList(); int i = 0; while (scenesToLoad.Count > 0 && i < scenesToLoad.Count) { SceneSplit split = scenesToLoad[i]; if (split.sceneCollectionManager.currentlySceneLoading < split.sceneCollectionManager.maxParallelSceneLoading) { //Debug.Log(split.sceneName); scenesToLoad.Remove(split); split.sceneCollectionManager.currentlySceneLoading++; loadingManager.LoadSceneAsync(split); i--; } i++; } } else { sceneLoadFramesNextWaited = true; sceneLoadFrameNext--; } } } /// /// Coroutine checks player position /// /// The checker. IEnumerator PositionChecker() { yield return new WaitForSeconds(positionCheckTime); while (true) { if (spawnedPlayer && player == null && !string.IsNullOrEmpty(playerTag)) { GameObject playerGO = GameObject.FindGameObjectWithTag(playerTag); #if AT_STREAMER2 if(ClientAPI.GetPlayerObject() != null && playerGO==null) playerGO = ClientAPI.GetPlayerObject().GameObject; #endif if (playerGO != null) player = playerGO.transform; } if (streamerActive && player != null) { CheckPositionTiles(); } else { #if !AT_STREAMER2 bool loadedScenes = false; foreach (var sceneCollectionManager in sceneCollectionManagers) { if (sceneCollectionManager.loadedScenes.Count > 0) { loadedScenes = true; sceneCollectionManager.ResetPosition(); } } if (loadedScenes) UnloadAllScenes(); #endif } yield return new WaitForSeconds(positionCheckTime); } } /// /// Checks the position of player in tiles. /// public void CheckPositionTiles() { Vector3 pos = player.position; pos -= currentMove; bool changed = false; foreach (var sceneCollectionManager in sceneCollectionManagers) { if (sceneCollectionManager.CheckPosition(pos)) changed = true; } if (changed) { SceneLoading(); Invoke(nameof(SceneUnloading), destroyTileDelay); if (worldMover != null) { worldMover.CheckMoverDistance(sceneCollectionManagers[0].xPos, sceneCollectionManagers[0].yPos, sceneCollectionManagers[0].zPos); } } } #endregion #region loading and unloading /// /// Loads tiles in range /// void SceneLoading() { //show splash screen if (showLoadingScreen && loadingStreamer != null) { showLoadingScreen = false; if (tilesLoaded >= tilesToLoad) { tilesToLoad = int.MaxValue; tilesLoaded = 0; } } int tilesToLoadNew = 0; int count = 0; foreach (var scm in sceneCollectionManagers) { if (!scm.active) { continue; } int x = scm.xPos; int y = scm.yPos; int z = scm.zPos; int LoadingRangeX = (int)scm.loadingRange.x * 2 + 1; int LoadingRangeZ = (int)scm.loadingRange.z * 2 + 1; int xs = 0, zs = 0, dx = 0, dy = -1; int t = Math.Max(LoadingRangeX, LoadingRangeZ); int maxI = t * t; //Debug.Log(LoadingRangeX); //Debug.Log(LoadingRangeZ); SceneSplit split; for (int i = 0; i < maxI; i++) { if ((-LoadingRangeX / 2 <= xs) && (xs <= LoadingRangeX / 2) && (-LoadingRangeZ / 2 <= zs) && (zs <= LoadingRangeZ / 2)) { for (int ys = 0; ys <= scm.loadingRange.y; ys++) { for (int yi = -1; yi <= 1; yi += 2) { if (ys == 0 && yi != -1) continue; count++; if (scm.useLoadingRangeMin) if (xs >= -scm.loadingRangeMin.x && xs <= scm.loadingRangeMin.x && ys >= -scm.loadingRangeMin.y && ys <= scm.loadingRangeMin.y && zs >= -scm.loadingRangeMin.z && zs <= scm.loadingRangeMin.z) { continue; } x = xs + scm.xPos; y = ys * yi + scm.yPos; z = zs + scm.zPos; Vector3Int sceneID = new Vector3Int(x, y, z); //Debug.Log(sceneID); float xMoveLimit = 0; int xDeloadLimit = 0; float yMoveLimit = 0; int yDeloadLimit = 0; float zMoveLimit = 0; int zDeloadLimit = 0; //set scene possition according to looping if (looping) { if (scm.xSplitIs) { int xFinal = mod((x + Mathf.Abs(scm.xLoadingLimitx)), scm.xLoadingRange) + scm.xLoadingLimitx; xDeloadLimit = (int)Math.Ceiling((x - scm.xLoadingLimity) / (float)scm.xLoadingRange) * scm.xLoadingRange; xMoveLimit = xDeloadLimit * scm.xSize; sceneID[0] = xFinal; } if (scm.ySplitIs) { int yFinal = mod((y + Mathf.Abs(scm.yLoadingLimitx)), scm.yLoadingRange) + scm.yLoadingLimitx; yDeloadLimit = (int)Math.Ceiling((y - scm.yLoadingLimity) / (float)scm.yLoadingRange) * scm.yLoadingRange; yMoveLimit = yDeloadLimit * scm.ySize; sceneID[1] = yFinal; } if (scm.zSplitIs) { int zFinal = mod((z + Mathf.Abs(scm.zLoadingLimitx)), scm.zLoadingRange) + scm.zLoadingLimitx; zDeloadLimit = (int)Math.Ceiling((z - scm.zLoadingLimity) / (float)scm.zLoadingRange) * scm.zLoadingRange; zMoveLimit = zDeloadLimit * scm.zSize; sceneID[2] = zFinal; } } //Debug.Log(sceneID[0] + " " + sceneID[1] + " " + sceneID[2]); //load scene if scene array contains it and set up scene offset position according to looping if (scm.scenesArray.TryGetValue(sceneID, out split)) { // Debug.Log(sceneID[0] + " " + sceneID[1] + " " + sceneID[2]); if (!split.loaded) { split.loaded = true; split.posXLimitMove = xMoveLimit; split.xDeloadLimit = xDeloadLimit; split.posYLimitMove = yMoveLimit; split.yDeloadLimit = yDeloadLimit; split.posZLimitMove = zMoveLimit; split.zDeloadLimit = zDeloadLimit; scenesToLoad.Add(split); scm.loadedScenes.Add(split); tilesToLoadNew++; } } else { //Debug.Log(scm.name+ " no scene id " + sceneID); } } } } if ((xs == zs) || ((xs < 0) && (xs == -zs)) || ((xs > 0) && (xs == 1 - zs))) { t = dx; dx = -dy; dy = t; } xs += dx; zs += dy; } } tilesToLoad = tilesToLoadNew; initialized = true; } /// /// Unloads tiles out of range /// void SceneUnloading() { List scenesToDestroy = new List(); SceneSplit splitCheck; float count; foreach (var scm in sceneCollectionManagers) { count = scm.loadedScenes.Count; for (int i = 0; i < count; i++) { splitCheck = scm.loadedScenes[i]; if (!scm.active) { scenesToDestroy.Add(splitCheck); continue; } if (scm.unloadRangeConnectParent != null) { SceneCollectionManager connectedScm = scm.unloadRangeConnectParent; //Debug.Log($"unloadRangeConnectParent {splitCheck.sceneName} {splitCheck.sceneGo.name} {splitCheck.sceneGo.scene.name}"); //Debug.Log(Mathf.Abs(splitCheck.posX + splitCheck.xDeloadLimit - scm.xPos) > scm.deloadingRange.x); if ((Mathf.Abs(splitCheck.posX + splitCheck.xDeloadLimit - scm.xPos) > scm.deloadingRange.x || Mathf.Abs(splitCheck.posY + splitCheck.yDeloadLimit - scm.yPos) > scm.deloadingRange.y || Mathf.Abs(splitCheck.posZ + splitCheck.zDeloadLimit - scm.zPos) > scm.deloadingRange.z) && (Mathf.Abs(splitCheck.posX + splitCheck.xDeloadLimit - scm.xPos) > connectedScm.deloadingRange.x || Mathf.Abs(splitCheck.posY + splitCheck.yDeloadLimit - scm.yPos) > connectedScm.deloadingRange.y || Mathf.Abs(splitCheck.posZ + splitCheck.zDeloadLimit - scm.zPos) > connectedScm.deloadingRange.z)) if (splitCheck.sceneGo != null) { //Debug.Log($"split {splitCheck.sceneName} {splitCheck.sceneGo.name} {splitCheck.sceneGo.scene.name}"); scenesToDestroy.Add(splitCheck); } continue; } if (Mathf.Abs(splitCheck.posX + splitCheck.xDeloadLimit - scm.xPos) > (int)scm.deloadingRange.x || Mathf.Abs(splitCheck.posY + splitCheck.yDeloadLimit - scm.yPos) > (int)scm.deloadingRange.y || Mathf.Abs(splitCheck.posZ + splitCheck.zDeloadLimit - scm.zPos) > (int)scm.deloadingRange.z) if (splitCheck.sceneGo != null) { // Debug.Log($"splitCheck.sceneName {splitCheck.sceneName} {splitCheck.sceneGo.name} {splitCheck.sceneGo.scene.name} base"); scenesToDestroy.Add(splitCheck); } if (scm.useLoadingRangeMin && !scm.useUnloadRangeConnect) { //Debug.Log($"splitCheck.sceneName {splitCheck.sceneName} {splitCheck.sceneGo.name} {splitCheck.sceneGo.scene.name} !useUnloadRangeConnect"); if (Mathf.Abs(splitCheck.posX + splitCheck.xDeloadLimit - scm.xPos) <= scm.loadingRangeMin.x && Mathf.Abs(splitCheck.posY + splitCheck.yDeloadLimit - scm.yPos) <= scm.loadingRangeMin.y && Mathf.Abs(splitCheck.posZ + splitCheck.zDeloadLimit - scm.zPos) <= scm.loadingRangeMin.z) if (splitCheck.sceneGo != null) { //Debug.Log($"splitCheck.sceneName {splitCheck.sceneName} {splitCheck.sceneGo.name} {splitCheck.sceneGo.scene.name} useUnloadRangeConnect"); scenesToDestroy.Add(splitCheck); } } } } UnloadScenes(scenesToDestroy); scenesToDestroy.Clear(); Streamer.UnloadAssets(this); } private void UnloadScenesSync(SceneSplit item) { if (item.sceneGo != null) { Terrain childTerrain = item.sceneGo.GetComponentInChildren(); if (childTerrain) { GameObject childTerrainGo = childTerrain.gameObject; Destroy(childTerrain); childTerrain = null; Destroy(childTerrainGo); childTerrainGo = null; } } item.loaded = false; item.loadingFinished = false; item.sceneCollectionManager.loadedScenes.Remove(item); item.sceneGo = null; loadingManager.UnloadSplitAsync(item); } private void UnloadScenes(List scenesToDestroy) { foreach (var item in scenesToDestroy) { item.loaded = false; item.loadingFinished = false; item.sceneCollectionManager.loadedScenes.Remove(item); item.sceneGo = null; #if AT_STREAMER2 tilesLoaded--; #endif loadingManager.UnloadSplitAsync(item); } if (terrainNeighbours) terrainNeighbours.CreateNeighbours(); } /// /// Unloads all tiles of streamer /// public void UnloadAllScenes() { Debug.Log("UnloadAllScenes start"); foreach (var sceneCollectionManager in sceneCollectionManagers) { foreach (var item in sceneCollectionManager.scenesArray) { if (item.Value.sceneGo != null) { //Terrain childTerrain = item.Value.sceneGo.GetComponentInChildren(); //if (childTerrain) //{ // GameObject childTerrainGO = childTerrain.gameObject; // Destroy(childTerrain); // childTerrain = null; // Destroy(childTerrainGO); // childTerrainGO = null; //} try { loadingManager.UnloadSplitAsync(item.Value); } catch (System.Exception ex) { Debug.Log(item.Value.sceneName); Debug.Log(item.Value.sceneGo.name); Debug.Log(item.Value.sceneGo.scene.name); Debug.LogError(ex.Message); } } item.Value.loaded = false; item.Value.loadingFinished = false; item.Value.sceneGo = null; } sceneCollectionManager.loadedScenes.Clear(); } if (terrainNeighbours) terrainNeighbours.CreateNeighbours(); Streamer.UnloadAssets(this); Debug.Log("UnloadAllScenes finish"); } public static void UnloadAssets(Streamer streamer) { if (Streamer.canUnload) { Streamer.canUnload = false; streamer.StartCoroutine(streamer.UnloadAssetsWait()); } else unloadNext = true; } public IEnumerator UnloadAssetsWait() { do { unloadNext = false; //Debug.Log("Resources.UnloadUnusedAssets"); Resources.UnloadUnusedAssets(); yield return new WaitForSeconds(waitTillNextUnload); } while (unloadNext); canUnload = true; } #endregion //// called first //void OnEnable() //{ // SceneManager.sceneLoaded += OnSceneLoaded; //} //void OnDisable() //{ // SceneManager.sceneLoaded -= OnSceneLoaded; //} void OnDrawGizmosSelected() { if (sceneCollectionManagers == null) return; Vector3 size = Vector3.zero; Vector3 position = Vector3.zero; Vector3Int loadingRange = Vector3Int.zero; foreach (var scm in sceneCollectionManagers) { if (scm && scm.showDebug && scm.active) { Gizmos.color = scm.color; size.x = scm.xSize == 0 ? 2 : scm.xSize; size.y = scm.ySize == 0 ? 2 : scm.ySize; size.z = scm.zSize == 0 ? 2 : scm.zSize; loadingRange.x = !scm.xSplitIs ? 0 : scm.loadingRange.x; loadingRange.y = !scm.ySplitIs ? 0 : scm.loadingRange.y; loadingRange.z = !scm.zSplitIs ? 0 : scm.loadingRange.z; for (int x = -(int)loadingRange.x + scm.xPos; x <= (int)loadingRange.x + scm.xPos; x++) { for (int y = -(int)loadingRange.y + scm.yPos; y <= (int)loadingRange.y + scm.yPos; y++) { for (int z = -(int)loadingRange.z + scm.zPos; z <= (int)loadingRange.z + scm.zPos; z++) { if (scm.useLoadingRangeMin && x - scm.xPos >= -scm.loadingRangeMin.x && x - scm.xPos <= scm.loadingRangeMin.x && y - scm.yPos >= -scm.loadingRangeMin.y && y - scm.yPos <= scm.loadingRangeMin.y && z - scm.zPos >= -scm.loadingRangeMin.z && z - scm.zPos <= scm.loadingRangeMin.z) { continue; } else { if (Application.isPlaying) { position.x = x * size.x; position.y = y * size.y; position.z = z * size.z; } else { position.x = (x - scm.xPos) * size.x; position.y = (y - scm.yPos) * size.y; position.z = (z - scm.zPos) * size.z; } Gizmos.DrawWireCube(position + size * 0.5f + currentMove, size); } } } } Gizmos.color = Color.green; if (Application.isPlaying) Gizmos.DrawWireCube(new Vector3(scm.xPos * size.x, scm.yPos * size.y, scm.zPos * size.z) + size * 0.5f + currentMove, size); else Gizmos.DrawWireCube(size * 0.5f, size); } } } #if AT_STREAMER2 public override int GetTilesToLoad() { return tilesToLoad; } public override int GetTilesLoaded() { return tilesLoaded; } public override float GetLoadingProgress() { return (tilesToLoad > 0) ? tilesLoaded / (float)tilesToLoad : 1; } #endif } }