Cookbook
Copy-paste recipes.
Move, tilt, and limit the view.
Where tiles come from, including your own server.
Lines, shapes, and real-world metres.
Extra imagery on top of the base map.
How many tiles stay in graphics memory.
Sharpness versus bandwidth.
Place names and your own text.
Pins, prefabs, clusters, clicks.
Adds a blue dot. Follow Position keeps the map on the device (or a simulated walk in the editor).
var follow = gameObject.AddComponent<LocationFollowController>(); follow.Map = Map; follow.FollowPosition = true; follow.RotateWithHeading = false;
Moves center and zoom together. Duration is seconds. Leave zoom out to keep the current level.
Map.FlyTo(48.8584, 2.2945, zoom: 16, durationSeconds: 0.8);
Same call, no zoom argument - only the center eases. CancelFly if the user grabs the map.
Map.FlyTo(51.5074, -0.1278, durationSeconds: 0.4); // Map.CancelFly();
These live on MapInteractionController, not the view. They clamp the user, not a script that sets Map.Zoom.
var input = GetComponent<MapInteractionController>(); input.MinZoom = 5; input.MaxZoom = 18;
Pitch maxes out at 60 degrees so the horizon stays off-screen. Rotation is counterclockwise, 0 = north up.
Map.PitchDeg = 45; Map.RotationDeg = 30;
North is rotation 0. Assign it. There is no separate reset call.
Map.RotationDeg = 0;
Pinch, twist and two-finger pitch are on when you add MapInteractionController. These flags turn pieces off.
var input = GetComponent<MapInteractionController>(); input.TouchEnabled = true; input.MouseEnabled = true; input.MaxPitchDeg = 60;
The view is five numbers. Save them, write them back.
PlayerPrefs.SetString("map-view", Map.Latitude + "," + Map.Longitude + "," + Map.Zoom + "," + Map.RotationDeg + "," + Map.PitchDeg);
Any {z}/{x}/{y} template. Put the API key in as {key}, not in the URL - an inline key poisons the disk cache.
Map.Provider = TileProvider.Custom; Map.CustomUrlTemplate = "https://tiles.example.com/{z}/{x}/{y}.png?key={key}"; Map.ApiKey = "YOUR_KEY"; Map.CustomAttribution = "(c) Example Tiles"; Map.CustomTileSizePx = 256;
Rebuilds the tile pipeline. Markers, labels and drawings stay. Each template has its own disk cache, so you will not see the old provider's tiles.
Map.SetProvider(TileProvider.MapTiler, style: "streets-v2", apiKey: "YOUR_KEY", retina512: true);
One field. Placeholder is {key}. Setting ApiKey after Start does nothing until the next SetProvider.
Map.ApiKey = "YOUR_KEY"; Map.SetProvider(Map.Provider, Map.ProviderStyle, Map.ApiKey);
Platform Default sizes the cache for the machine (up to 10 GB desktop, 2 GB phone). Manual uses the megabytes you type.
Map.CacheSizeMode = DiskCacheSizeMode.Manual; Map.DiskCacheMB = 512;
Sharper, fewer requests, if the provider actually serves 512 px tiles. Ignored on 256-only providers.
Map.Retina512Tiles = true;
Width is screen pixels. Color32 helpers live in LansMap.Markers.Bulk. At least two points.
Map.Polylines.Store.AddPolyline(new[] { new LatLon(48.8584, 2.2945), new LatLon(48.8606, 2.3376) }, widthPx: 4, color: new Color32(255, 80, 80, 255));
No circle primitive. Walk a ring with GeoUtils.DestinationPoint and fill it. Holes are not an API yet.
var center = new LatLon(48.8584, 2.2945); var ring = new LatLon[32]; for (int i = 0; i < ring.Length; i++) ring[i] = GeoUtils.DestinationPoint(center, 400, i * (360.0 / ring.Length)); Map.Polygons.Store.AddPolygon(ring, new Color32(32, 160, 255, 90));
A second tile source over the base (hillshade, traffic, a private grid). Set this before Play. OverlayEnabled is read when the pipeline is built.
Map.OverlayEnabled = true; Map.OverlayProvider = TileProvider.OpenTopoMap; Map.OverlayOpacity = 0.45f;
OverlayEnabled is not a live switch. OverlayOpacity is: 0 hides it, the old value shows it again, with no rebuild.
Map.OverlayOpacity = Map.OverlayOpacity > 0 ? 0f : 0.45f;
The overlay is the second layer. The base map has no opacity of its own.
Map.OverlayOpacity = 0.3f;
Tiles downloaded, served from disk, and in graphics memory. Off by default - debug, not for end users.
Map.TileCounterDebugPanelEnabled = true;
Automatic (the default) picks how many tiles stay in graphics memory from the screen, max tilt, and GPU memory. Leave it. Use Manual plus LayerCount only after you have measured that you must.
Map.CapacityMode = TileCapacityMode.Automatic; // Manual override, only after you have measured a need: // Map.CapacityMode = TileCapacityMode.Manual; // Map.LayerCount = 256;
Same fields as "Cache tiles on disk". The cap is what the index will serve. Evicted files stay until a rewrite.
Map.CacheSizeMode = DiskCacheSizeMode.Manual; Map.DiskCacheMB = 256; Map.OverlayCacheMB = 64;
First codec this device supports wins. Work happens off the main thread. Empty list means uncompressed RGBA32.
Map.TileCompressionPreference = new List<string> { "astc4x4", "etc2", "bc1" };
Presets that serve 512 px honor Retina512Tiles. A custom URL uses CustomTileSizePx of 256 or 512, matching the server.
Map.Retina512Tiles = true; Map.CustomTileSizePx = 512;
There is no mip-bias slider. TileMipsEnabled builds a mip chain and uses trilinear. Off by default: about +33% tile-array memory. Set it before Play, or call SetProvider after a flip.
Map.TileMipsEnabled = true;
0.2 seconds on every load is the default. 0 makes every switch instant. Both can change while running.
Map.TileFadeSeconds = 0.2; Map.FadeAllLoads = true;
Upright place names over whatever raster base you picked. Set BaseMapLabels to false if the tiles already have names and you do not want them twice.
Map.PlaceLabelsEnabled = true; Map.BaseMapLabels = false;
A label hangs off a marker id, not a free coordinate. Add the marker first, then name it.
int id = Map.Markers.Store.Add( new LatLon(48.8584, 2.2945), 0, 14f, MarkerSizeMode.SceneSize, new Color32(255, 64, 64, 255)); Map.Labels.SetLabel(id, "Eiffel Tower");
Place names and marker labels stay upright. Names baked into the tiles rotate with the map - turn those off with BaseMapLabels.
Map.PlaceLabelsEnabled = true; Map.BaseMapLabels = false;
One pin at a coordinate. See "Scale a marker with zoom" for constant-on-screen vs metres-on-the-ground.
Map.Markers.Store.Add( new LatLon(48.8584, 2.2945), 0, 14f, MarkerSizeMode.SceneSize, new Color32(255, 64, 64, 255));
A prefab at a coordinate, real-world scale. The handle stays on the map as the view moves.
WorldMarkerInstance pin = worldMarkerManager.Spawn(pinPrefab, 45.4642, 9.1900, altitudeMeters: 0);
Nearby markers collapse into one while zoomed out, then split on the way in. Options: getting-started page.
var cluster = gameObject.AddComponent<MarkerClusterController>(); cluster.Map = Map; cluster.MaxClusterZoom = 13;
SceneSize is a constant size on screen (a pin). RealSize is metres on the ground, so it shrinks as you zoom out.
Map.Markers.Store.Add( new LatLon(48.8584, 2.2945), 0, 40f, MarkerSizeMode.RealSize, new Color32(255, 64, 64, 255));
MarkerHoverLabel is the built-in tooltip. For your own click, pick against the current view. You get a slot, not a handle.
gameObject.AddComponent<MarkerHoverLabel>().Map = Map;if (Input.GetMouseButtonDown(0)) { var sp = new ScreenPoint(Input.mousePosition.x, Input.mousePosition.y); if (Map.Markers.TryPick(Map.CurrentView, sp, 16, out int slot)) Debug.Log(“hit slot “ + slot); }
Keep the id from Add and write a new coordinate each frame. Same slot. Nothing spawned or destroyed.
int id = Map.Markers.Store.Add( start, 0, 14f, MarkerSizeMode.SceneSize, new Color32(255, 64, 64, 255));Map.Markers.Store.UpdatePosition(id, next, altitudeMeters: 0);
Same as "Add your own labels". The text stays upright as the map rotates.
Map.Labels.SetLabel(id, "Depot 12");
minZoom and maxZoom are on the packed-color Add, not the Color32 one. Size 0 also hides a live marker without removing it.
Map.Markers.Store.Add( new LatLon(48.8584, 2.2945), 0, 14f, MarkerSizeMode.SceneSize, 0xFF4040FFu, MarkerShape.Circle, false, minZoom: 12f, maxZoom: 18f);
Add returns a MarkerHandle - keep it and mutate the marker directly, instead of tracking the id yourself and calling back into the store.
MarkerHandle pin = Map.Markers.Store.Add( new LatLon(48.8584, 2.2945), 0, 14f, MarkerSizeMode.SceneSize, 0xFF4040FFu);// later, once the caller decides this marker is selected pin.OutlineRgba = 0xFFFFFFFFu; pin.ShowStem = true;
WebGPU detected. The build is about 8 MB.
This demo needs WebGPU. Try a recent Chrome, Edge, or Safari on iOS/macOS 26+.