MapVisualizer.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. public class MapVisualizer : MonoBehaviour
  4. {
  5. [Header("Terrain Sprites")]
  6. public Sprite defaultSprite; // Default square sprite
  7. public Sprite plainsSprite;
  8. public Sprite forestSprite;
  9. public Sprite oceanSprite;
  10. public Sprite lakeSprite;
  11. public Sprite shoreSprite;
  12. public Sprite riverSprite;
  13. public Sprite mountainSprite;
  14. public Sprite forestRiverSprite; // New hybrid sprite
  15. [Header("Feature Sprites")]
  16. public Sprite roadSprite;
  17. public Sprite bridgeSprite;
  18. public Sprite tunnelSprite;
  19. public Sprite harbourSprite;
  20. public Sprite townSprite;
  21. public Sprite villageSprite;
  22. [Header("Settings")]
  23. public float tileSize = 1f;
  24. public Transform mapParent;
  25. private Dictionary<Vector2Int, GameObject> terrainTiles = new Dictionary<Vector2Int, GameObject>();
  26. private Dictionary<Vector2Int, GameObject> featureTiles = new Dictionary<Vector2Int, GameObject>();
  27. private Sprite generatedDefaultSprite; // Generated sprite for fallback
  28. void Start()
  29. {
  30. if (mapParent == null)
  31. {
  32. GameObject mapContainer = new GameObject("MapContainer");
  33. mapParent = mapContainer.transform;
  34. }
  35. // Create a default white square sprite if none exist
  36. CreateDefaultSprite();
  37. }
  38. private void CreateDefaultSprite()
  39. {
  40. // Create a simple white square texture if defaultSprite is not assigned
  41. if (generatedDefaultSprite == null)
  42. {
  43. Texture2D texture = new Texture2D(32, 32);
  44. Color[] pixels = new Color[32 * 32];
  45. for (int i = 0; i < pixels.Length; i++)
  46. {
  47. pixels[i] = Color.white;
  48. }
  49. texture.SetPixels(pixels);
  50. texture.Apply();
  51. // Use 32 pixels per unit instead of 100 to make tiles bigger
  52. generatedDefaultSprite = Sprite.Create(texture, new Rect(0, 0, 32, 32), new Vector2(0.5f, 0.5f), 32f);
  53. }
  54. }
  55. public void VisualizeMap(MapData mapData)
  56. {
  57. // Clear existing tiles that are out of bounds
  58. ClearOutOfBoundsTiles(mapData);
  59. // Generate visual tiles for the map
  60. for (int x = 0; x < mapData.Width; x++)
  61. {
  62. for (int y = 0; y < mapData.Height; y++)
  63. {
  64. Vector2Int pos = new Vector2Int(x, y);
  65. MapTile tile = mapData.GetTile(x, y);
  66. // Create or update terrain tile
  67. CreateOrUpdateTerrainTile(pos, tile);
  68. // Create or update feature tile
  69. CreateOrUpdateFeatureTile(pos, tile);
  70. }
  71. }
  72. // Position camera to see the map
  73. PositionCameraToMap(mapData);
  74. }
  75. private void PositionCameraToMap(MapData mapData)
  76. {
  77. Camera mainCam = Camera.main;
  78. if (mainCam != null)
  79. {
  80. // Check if we're in exploration mode and need to account for world coordinate offset
  81. Vector3 mapCenter;
  82. // Try to get exploration manager from MapMaker2 to determine proper camera positioning
  83. MapMaker2 mapMaker = Object.FindFirstObjectByType<MapMaker2>();
  84. if (mapMaker != null && mapMaker.useExplorationSystem)
  85. {
  86. var explorationManager = mapMaker.GetExplorationManager();
  87. if (explorationManager != null)
  88. {
  89. // Get the explored bounds to find the actual world coordinate center
  90. var bounds = explorationManager.GetExploredBoundsForCamera();
  91. // Center camera on the actual world coordinates of the explored area
  92. mapCenter = new Vector3(
  93. (bounds.x + bounds.width / 2f) * tileSize,
  94. (bounds.y + bounds.height / 2f) * tileSize,
  95. mainCam.transform.position.z
  96. );
  97. Debug.Log($"🎥 Camera positioned for exploration: bounds({bounds.x}, {bounds.y}, {bounds.width}, {bounds.height}) → center({mapCenter.x:F1}, {mapCenter.y:F1})");
  98. }
  99. else
  100. {
  101. // Fallback to standard positioning
  102. mapCenter = new Vector3(
  103. (mapData.Width * tileSize) / 2f,
  104. (mapData.Height * tileSize) / 2f,
  105. mainCam.transform.position.z
  106. );
  107. }
  108. }
  109. else
  110. {
  111. // Standard positioning for non-exploration mode
  112. mapCenter = new Vector3(
  113. (mapData.Width * tileSize) / 2f,
  114. (mapData.Height * tileSize) / 2f,
  115. mainCam.transform.position.z
  116. );
  117. }
  118. mainCam.transform.position = mapCenter;
  119. // Adjust orthographic size to fit map - make it bigger so you can zoom out more
  120. if (mainCam.orthographic)
  121. {
  122. float mapHeight = mapData.Height * tileSize;
  123. float mapWidth = mapData.Width * tileSize;
  124. float aspectRatio = (float)Screen.width / Screen.height;
  125. float requiredHeight = mapHeight / 2f;
  126. float requiredWidth = mapWidth / (2f * aspectRatio);
  127. // Calculate required size but don't override if camera is already zoomed out further
  128. float suggestedSize = Mathf.Max(requiredHeight, requiredWidth) + 20f;
  129. // Only set the camera size if it's smaller than what we need
  130. // This preserves user's zoom level while ensuring the map is initially visible
  131. if (mainCam.orthographicSize < suggestedSize)
  132. {
  133. mainCam.orthographicSize = suggestedSize;
  134. Debug.Log($"🎥 Camera zoom adjusted to fit map: {suggestedSize:F1}");
  135. }
  136. else
  137. {
  138. Debug.Log($"🎥 Camera zoom preserved: {mainCam.orthographicSize:F1} (suggested: {suggestedSize:F1})");
  139. }
  140. }
  141. }
  142. }
  143. /// <summary>
  144. /// Calculate world position for a tile, accounting for exploration bounds offset
  145. /// </summary>
  146. private Vector3 CalculateWorldPosition(Vector2Int visiblePos)
  147. {
  148. // Try to get exploration manager to determine world coordinate offset
  149. MapMaker2 mapMaker = Object.FindFirstObjectByType<MapMaker2>();
  150. if (mapMaker != null && mapMaker.useExplorationSystem)
  151. {
  152. var explorationManager = mapMaker.GetExplorationManager();
  153. if (explorationManager != null)
  154. {
  155. // Get the explored bounds to find the world coordinate offset
  156. var bounds = explorationManager.GetExploredBoundsForCamera();
  157. // Convert visible coordinates to world coordinates
  158. Vector3 worldPos = new Vector3(
  159. (bounds.x + visiblePos.x) * tileSize,
  160. 0,
  161. (bounds.y + visiblePos.y) * tileSize
  162. );
  163. // Debug log for the first few tiles to verify positioning
  164. if (visiblePos.x < 3 && visiblePos.y < 3)
  165. {
  166. Debug.Log($"🔧 Tile positioning: visible({visiblePos.x}, {visiblePos.y}) + bounds({bounds.x}, {bounds.y}) → world({worldPos.x:F1}, {worldPos.z:F1})");
  167. }
  168. return worldPos;
  169. }
  170. }
  171. // Fallback to direct positioning for non-exploration mode
  172. return new Vector3(visiblePos.x * tileSize, 0, visiblePos.y * tileSize);
  173. }
  174. private void ClearOutOfBoundsTiles(MapData mapData)
  175. {
  176. List<Vector2Int> tilesToRemove = new List<Vector2Int>();
  177. // Check terrain tiles
  178. foreach (var kvp in terrainTiles)
  179. {
  180. if (kvp.Key.x >= mapData.Width || kvp.Key.y >= mapData.Height ||
  181. kvp.Key.x < 0 || kvp.Key.y < 0)
  182. {
  183. tilesToRemove.Add(kvp.Key);
  184. }
  185. }
  186. foreach (var pos in tilesToRemove)
  187. {
  188. if (terrainTiles[pos] != null)
  189. DestroyImmediate(terrainTiles[pos]);
  190. terrainTiles.Remove(pos);
  191. }
  192. tilesToRemove.Clear();
  193. // Check feature tiles
  194. foreach (var kvp in featureTiles)
  195. {
  196. if (kvp.Key.x >= mapData.Width || kvp.Key.y >= mapData.Height ||
  197. kvp.Key.x < 0 || kvp.Key.y < 0)
  198. {
  199. tilesToRemove.Add(kvp.Key);
  200. }
  201. }
  202. foreach (var pos in tilesToRemove)
  203. {
  204. if (featureTiles[pos] != null)
  205. DestroyImmediate(featureTiles[pos]);
  206. featureTiles.Remove(pos);
  207. }
  208. }
  209. private void CreateOrUpdateTerrainTile(Vector2Int pos, MapTile tile)
  210. {
  211. GameObject tileObject;
  212. if (!terrainTiles.ContainsKey(pos))
  213. {
  214. // Create new tile
  215. tileObject = new GameObject($"Terrain_{pos.x}_{pos.y}");
  216. tileObject.transform.parent = mapParent;
  217. // Calculate world position accounting for exploration bounds offset
  218. Vector3 worldPosition = CalculateWorldPosition(pos);
  219. tileObject.transform.position = worldPosition;
  220. SpriteRenderer sr = tileObject.AddComponent<SpriteRenderer>();
  221. sr.sortingOrder = 0;
  222. // Scale the tile to make it visible
  223. tileObject.transform.localScale = Vector3.one * tileSize;
  224. // Rotate sprite to be flat on the ground for top-down view
  225. tileObject.transform.rotation = Quaternion.Euler(90f, 0f, 0f);
  226. terrainTiles[pos] = tileObject;
  227. }
  228. else
  229. {
  230. tileObject = terrainTiles[pos];
  231. }
  232. // Update sprite and color based on terrain type
  233. SpriteRenderer spriteRenderer = tileObject.GetComponent<SpriteRenderer>();
  234. // Use assigned sprite or fallback to default sprite
  235. Sprite terrainSprite = GetTerrainSprite(tile.terrainType);
  236. spriteRenderer.sprite = terrainSprite != null ? terrainSprite : (defaultSprite != null ? defaultSprite : generatedDefaultSprite);
  237. spriteRenderer.color = GetTerrainColor(tile.terrainType);
  238. // For ForestRiver tiles, add a simple river overlay if no custom sprite is provided
  239. if (tile.terrainType == TerrainType.ForestRiver && forestRiverSprite == null)
  240. {
  241. CreateSimpleForestRiverEffect(tileObject);
  242. }
  243. }
  244. private void CreateOrUpdateFeatureTile(Vector2Int pos, MapTile tile)
  245. {
  246. if (tile.featureType == FeatureType.None)
  247. {
  248. // Remove feature tile if it exists
  249. if (featureTiles.ContainsKey(pos))
  250. {
  251. if (featureTiles[pos] != null)
  252. DestroyImmediate(featureTiles[pos]);
  253. featureTiles.Remove(pos);
  254. }
  255. return;
  256. }
  257. GameObject featureObject;
  258. if (!featureTiles.ContainsKey(pos))
  259. {
  260. // Create new feature tile
  261. featureObject = new GameObject($"Feature_{pos.x}_{pos.y}_{tile.featureType}");
  262. featureObject.transform.parent = mapParent;
  263. // Calculate world position accounting for exploration bounds offset
  264. Vector3 worldPosition = CalculateWorldPosition(pos);
  265. featureObject.transform.position = worldPosition;
  266. SpriteRenderer sr = featureObject.AddComponent<SpriteRenderer>();
  267. sr.sortingOrder = 1; // Above terrain
  268. // Scale the tile to make it visible
  269. featureObject.transform.localScale = Vector3.one * tileSize;
  270. // Rotate sprite to be flat on the ground for top-down view
  271. featureObject.transform.rotation = Quaternion.Euler(90f, 0f, 0f);
  272. featureTiles[pos] = featureObject;
  273. }
  274. else
  275. {
  276. featureObject = featureTiles[pos];
  277. }
  278. // Update sprite and color based on feature type
  279. SpriteRenderer spriteRenderer = featureObject.GetComponent<SpriteRenderer>();
  280. // Use assigned sprite or fallback sprite
  281. Sprite featureSprite = GetFeatureSprite(tile.featureType);
  282. spriteRenderer.sprite = featureSprite != null ? featureSprite : (defaultSprite != null ? defaultSprite : generatedDefaultSprite);
  283. spriteRenderer.color = GetFeatureColor(tile.featureType);
  284. }
  285. private Sprite GetTerrainSprite(TerrainType terrainType)
  286. {
  287. return terrainType switch
  288. {
  289. TerrainType.Plains => plainsSprite,
  290. TerrainType.Forest => forestSprite,
  291. TerrainType.Ocean => oceanSprite,
  292. TerrainType.Lake => lakeSprite,
  293. TerrainType.Shore => shoreSprite,
  294. TerrainType.River => riverSprite,
  295. TerrainType.Mountain => mountainSprite,
  296. TerrainType.ForestRiver => forestRiverSprite != null ? forestRiverSprite : riverSprite,
  297. _ => null
  298. };
  299. }
  300. private Sprite GetFeatureSprite(FeatureType featureType)
  301. {
  302. return featureType switch
  303. {
  304. FeatureType.Road => roadSprite,
  305. FeatureType.Bridge => bridgeSprite,
  306. FeatureType.Tunnel => tunnelSprite,
  307. FeatureType.Harbour => harbourSprite,
  308. FeatureType.Ferry => roadSprite, // Use road sprite for ferry routes (will be colored differently)
  309. FeatureType.Town => townSprite,
  310. FeatureType.Village => villageSprite,
  311. _ => null
  312. };
  313. }
  314. private Color GetTerrainColor(TerrainType terrainType)
  315. {
  316. return terrainType switch
  317. {
  318. TerrainType.Plains => new Color(0.6f, 0.8f, 0.4f), // Light green
  319. TerrainType.Forest => new Color(0.2f, 0.6f, 0.2f), // Dark green
  320. TerrainType.Ocean => new Color(0.2f, 0.4f, 0.8f), // Blue
  321. TerrainType.Lake => new Color(0.3f, 0.5f, 0.9f), // Light blue
  322. TerrainType.Shore => new Color(0.8f, 0.7f, 0.5f), // Sandy
  323. TerrainType.River => new Color(0.4f, 0.6f, 1f), // River blue
  324. TerrainType.Mountain => new Color(0.5f, 0.5f, 0.5f), // Gray
  325. TerrainType.ForestRiver => new Color(0.3f, 0.5f, 0.7f), // Blue-green mix
  326. _ => Color.white
  327. };
  328. }
  329. private Color GetFeatureColor(FeatureType featureType)
  330. {
  331. return featureType switch
  332. {
  333. FeatureType.Road => new Color(0.6f, 0.4f, 0.2f), // Brown
  334. FeatureType.Bridge => new Color(0.9f, 0.8f, 0.6f), // Light tan/wood color
  335. FeatureType.Tunnel => new Color(0.3f, 0.3f, 0.3f), // Dark gray
  336. FeatureType.Harbour => new Color(0.4f, 0.3f, 0.2f), // Dark brown
  337. FeatureType.Ferry => new Color(0.7f, 0.9f, 1.0f), // Light blue/cyan for water routes
  338. FeatureType.Town => new Color(0.8f, 0.2f, 0.2f), // Red
  339. FeatureType.Village => new Color(0.9f, 0.7f, 0.3f), // Yellow
  340. _ => Color.white
  341. };
  342. }
  343. public void ClearAllTiles()
  344. {
  345. foreach (var tile in terrainTiles.Values)
  346. {
  347. if (tile != null) DestroyImmediate(tile);
  348. }
  349. foreach (var tile in featureTiles.Values)
  350. {
  351. if (tile != null) DestroyImmediate(tile);
  352. }
  353. terrainTiles.Clear();
  354. featureTiles.Clear();
  355. }
  356. private void CreateSimpleForestRiverEffect(GameObject forestRiverTile)
  357. {
  358. // Create a simple blue overlay to indicate river flowing through forest
  359. GameObject riverEffect = new GameObject("RiverEffect");
  360. riverEffect.transform.parent = forestRiverTile.transform;
  361. riverEffect.transform.localPosition = Vector3.zero;
  362. riverEffect.transform.localScale = new Vector3(0.6f, 0.6f, 1f); // Smaller than the base tile
  363. SpriteRenderer effectRenderer = riverEffect.AddComponent<SpriteRenderer>();
  364. effectRenderer.sprite = defaultSprite != null ? defaultSprite : generatedDefaultSprite;
  365. effectRenderer.sortingOrder = 1; // Above the forest base
  366. effectRenderer.color = new Color(0.4f, 0.6f, 1f, 0.7f); // Semi-transparent river blue
  367. }
  368. }