mapbox / mapbox-unity-sdk

Mapbox Unity SDK - https://www.mapbox.com/unity/
Other
725 stars 210 forks source link

when I use slippy map,I move the terrain map, building,street,and other thing will not be destory.The Memory will be increase. #51

Closed veyvin closed 7 years ago

veyvin commented 7 years ago

Can I destory the object outside in my view?

david-rhodes commented 7 years ago

@veyvin Yes, you can. We have a ticket for something similar: https://github.com/mapbox/mapbox-unity-sdk/issues/31.

For streets and buildings, it doesn't make much sense to use an object pool. We're planning the most efficient and developer-friendly design to handle any design requirements that come up.

In the meantime, you can add tile-destroying code to MapController.cs (based on distance or a queue, etc).

Edit: changed Slippy.cs to MapController.cs, since it's the class that has a reference to the tiles.

david-rhodes commented 7 years ago

Possible temporary solution:

namespace Mapbox.Unity.MeshGeneration
{
    using UnityEngine;
    using System.Collections.Generic;
    using Mapbox.Unity.MeshGeneration.Data;
    using Mapbox.Unity;
    using Mapbox.Platform;
    using Mapbox.Unity.Utilities;
    using Utils;

    /// <summary>
    /// MapController is just an helper class imitating the game/app logic controlling the map. It creates and passes the tiles requests to MapVisualization.
    /// </summary>
    public class MapController : MonoBehaviour
    {
        public static RectD ReferenceTileRect { get; set; }
        public static float WorldScaleFactor { get; set; }

        public MapVisualization MapVisualization;
        public float TileSize = 100;

        [SerializeField]
        private bool _snapYToZero = true;

        [Geocode]
        public string LatLng;
        public int Zoom;
        public Vector4 Range;

        [SerializeField]
        int _maxTileCount = 30;

        private GameObject _root;
        private Dictionary<Vector2, UnityTile> _tiles;

        Queue<UnityTile> _tileDestructionQueue = new Queue<UnityTile>();

        /// <summary>
        /// Resets the map controller and initializes the map visualization
        /// </summary>
        public void Awake()
        {
            MapVisualization.Initialize(MapboxAccess.Instance);
            _tiles = new Dictionary<Vector2, UnityTile>();
        }

        public void Start()
        {
            Execute();
        }

        /// <summary>
        /// Pulls the root world object to origin for ease of use/view
        /// </summary>
        public void Update()
        {
            if (_snapYToZero)
            {
                var ray = new Ray(new Vector3(0, 1000, 0), Vector3.down);
                RaycastHit rayhit;
                if (Physics.Raycast(ray, out rayhit))
                {
                    _root.transform.position = new Vector3(0, -rayhit.point.y, 0);
                    _snapYToZero = false;
                }
            }
        }

        public void Execute()
        {
            var parm = LatLng.Split(',');
            Execute(double.Parse(parm[0]), double.Parse(parm[1]), Zoom, Range);
        }

        /// <summary>
        /// World creation call used in the demos. Destroys and existing worlds and recreates another one. 
        /// </summary>
        /// <param name="lat">Latitude of the requested point</param>
        /// <param name="lng">Longitude of the requested point</param>
        /// <param name="zoom">Zoom/Detail level of the world</param>
        /// <param name="frame">Tiles to load around central tile in each direction; west-north-east-south</param>
        public void Execute(double lat, double lng, int zoom, Vector4 frame)
        {
            //frame goes left-top-right-bottom here
            if (_root != null)
            {
                foreach (Transform t in _root.transform)
                {
                    Destroy(t.gameObject);
                }
            }

            _root = new GameObject("worldRoot");

            var v2 = Conversions.GeoToWorldPosition(lat, lng, new Vector2d(0, 0));
            var tms = Conversions.MetersToTile(v2, zoom);
            ReferenceTileRect = Conversions.TileBounds(tms, zoom);
            WorldScaleFactor = (float)(TileSize / ReferenceTileRect.Size.x);
            _root.transform.localScale = Vector3.one * WorldScaleFactor;

            for (int i = (int)(tms.x - frame.x); i <= (tms.x + frame.z); i++)
            {
                for (int j = (int)(tms.y - frame.y); j <= (tms.y + frame.w); j++)
                {
                    var tile = new GameObject("Tile - " + i + " | " + j).AddComponent<UnityTile>();
                    _tiles.Add(new Vector2(i, j), tile);
                    tile.Zoom = zoom;
                    tile.RelativeScale = Conversions.GetTileScaleInMeters(0, Zoom) / Conversions.GetTileScaleInMeters((float)lat, Zoom);
                    tile.TileCoordinate = new Vector2(i, j);
                    tile.Rect = Conversions.TileBounds(tile.TileCoordinate, zoom);
                    tile.transform.position = new Vector3((float)(tile.Rect.Center.x - ReferenceTileRect.Center.x), 0, (float)(tile.Rect.Center.y - ReferenceTileRect.Center.y));
                    tile.transform.SetParent(_root.transform, false);
                    MapVisualization.ShowTile(tile);
                }
            }
        }

        public void Execute(double lat, double lng, int zoom, Vector2 frame)
        {
            Execute(lat, lng, zoom, new Vector4(frame.x, frame.y, frame.x, frame.y));
        }

        public void Execute(double lat, double lng, int zoom, int range)
        {
            Execute(lat, lng, zoom, new Vector4(range, range, range, range));
        }

        /// <summary>
        /// Used for loading new tiles on the existing world. Unlike Execute function, doesn't destroy the existing ones.
        /// </summary>
        /// <param name="pos">Tile coordinates of the requested tile</param>
        /// <param name="zoom">Zoom/Detail level of the requested tile</param>
        public void Request(Vector2 pos, int zoom)
        {
            if (!_tiles.ContainsKey(pos))
            {
                var tile = new GameObject("Tile - " + pos.x + " | " + pos.y).AddComponent<UnityTile>();
                if (_tileDestructionQueue.Count > _maxTileCount)
                {
                    var tileToDestroy = _tileDestructionQueue.Dequeue();
                    _tiles.Remove(tileToDestroy.TileCoordinate);
                    Destroy(tileToDestroy.gameObject);
                }
                _tileDestructionQueue.Enqueue(tile);
                _tiles.Add(pos, tile);
                tile.transform.SetParent(_root.transform, false);
                tile.Zoom = zoom;
                tile.TileCoordinate = new Vector2(pos.x, pos.y);
                tile.Rect = Conversions.TileBounds(tile.TileCoordinate, zoom);
                tile.RelativeScale = Conversions.GetTileScaleInMeters(0, Zoom) /
                    Conversions.GetTileScaleInMeters((float)Conversions.MetersToLatLon(tile.Rect.Center).x, Zoom);
                tile.transform.localPosition = new Vector3((float)(tile.Rect.Center.x - ReferenceTileRect.Center.x),
                                                           0,
                                                           (float)(tile.Rect.Center.y - ReferenceTileRect.Center.y));
                MapVisualization.ShowTile(tile);
            }
        }
    }
}

Note that you would probably want to check the tile status before destroying, as another script might be referencing it (mesh generation, for example).

david-rhodes commented 7 years ago

@veyvin Check slippy.unity here: https://github.com/mapbox/mapbox-unity-sdk/pull/74. This uses object pooling for tiles and you can set range for loading AND unloading tiles! Memory should be stable, now.