WorldSpaceLocationNames.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. using UnityEngine;
  2. using TMPro;
  3. using System.Collections.Generic;
  4. using System.Collections;
  5. public class WorldSpaceLocationNames : MonoBehaviour
  6. {
  7. [Header("Display Settings")]
  8. public bool showSettlementNames = true;
  9. public bool showForestNames = true;
  10. public bool showLakeNames = true;
  11. public bool showPlainNames = true;
  12. public bool showMountainNames = true;
  13. public bool showRiverNames = true;
  14. [Header("Visual Settings")]
  15. public Color settlementNameColor = Color.white;
  16. public Color forestNameColor = Color.green;
  17. public Color lakeNameColor = Color.cyan;
  18. public Color plainNameColor = Color.yellow;
  19. public Color mountainNameColor = Color.gray;
  20. public Color riverNameColor = Color.blue;
  21. [Header("3D Text Settings")]
  22. public GameObject textPrefab; // TextMeshPro 3D prefab
  23. public float textSize = 72f; // Large size for visibility (was 10f)
  24. public float heightOffset = 0.5f; // Small offset above terrain (0.5 = just above ground)
  25. public bool faceCamera = false; // Disabled by default to allow manual rotation control
  26. public Vector3 fixedRotation = new Vector3(90, 0, 0); // Fixed rotation for text (X=90 for flat text)
  27. public float maxViewDistance = 0f; // Disabled by default (was 100f) - distance culling causes visibility issues
  28. [Header("Rendering Order")]
  29. public string sortingLayerName = "Default"; // Sorting layer for text rendering
  30. public int sortingOrder = 2; // Order in layer (higher = renders on top). Roads are 1, so use 2+
  31. public bool useBuiltInTextMesh = false; // Fallback to Unity's TextMesh if TextMeshPro fails
  32. [Header("Debug")]
  33. public bool debugMode = true;
  34. private List<GameObject> nameObjects = new List<GameObject>();
  35. private GeographicFeatureManager featureManager;
  36. private MapData mapData;
  37. private MapMaker2 mapMaker;
  38. private Camera mainCamera;
  39. private Transform nameContainer;
  40. private void Start()
  41. {
  42. StartCoroutine(InitializeDelayed());
  43. }
  44. private IEnumerator InitializeDelayed()
  45. {
  46. // Wait for map generation to complete
  47. yield return new WaitForSeconds(1f);
  48. InitializeReferences();
  49. CreateNameContainer();
  50. if (textPrefab == null)
  51. {
  52. CreateDefaultTextPrefab();
  53. // If TextMeshPro setup failed, try built-in TextMesh as fallback
  54. if (textPrefab == null)
  55. {
  56. useBuiltInTextMesh = true;
  57. CreateFallbackTextPrefab();
  58. }
  59. }
  60. yield return new WaitForSeconds(0.5f);
  61. RefreshLocationNames();
  62. // Automatically apply visibility fixes at startup
  63. yield return new WaitForSeconds(0.1f); // Small delay to ensure objects are created
  64. ApplyVisibilityFixesAtStartup();
  65. }
  66. private void InitializeReferences()
  67. {
  68. featureManager = FindFirstObjectByType<GeographicFeatureManager>();
  69. // If GeographicFeatureManager isn't found, try to find it by name
  70. if (featureManager == null)
  71. {
  72. GameObject gfmObject = GameObject.Find("GeographicFeatureManager");
  73. if (gfmObject != null)
  74. {
  75. featureManager = gfmObject.GetComponent<GeographicFeatureManager>();
  76. }
  77. }
  78. mapMaker = FindFirstObjectByType<MapMaker2>();
  79. mainCamera = Camera.main ?? FindFirstObjectByType<Camera>();
  80. if (mapMaker != null)
  81. {
  82. mapData = mapMaker.GetMapData();
  83. }
  84. if (debugMode)
  85. {
  86. Debug.Log($"WorldSpaceLocationNames: Initialized");
  87. Debug.Log($" - FeatureManager: {(featureManager != null ? $"Found ({featureManager.GeographicFeatures.Count} features)" : "NULL")}");
  88. Debug.Log($" - MapData: {(mapData != null ? $"Found ({mapData.Width}x{mapData.Height})" : "NULL")}");
  89. Debug.Log($" - Camera: {(mainCamera != null ? "Found" : "NULL")}");
  90. }
  91. }
  92. private void CreateNameContainer()
  93. {
  94. GameObject containerObj = new GameObject("LocationNameContainer");
  95. containerObj.transform.parent = transform;
  96. containerObj.transform.localPosition = Vector3.zero;
  97. containerObj.transform.localRotation = Quaternion.identity;
  98. containerObj.transform.localScale = Vector3.one;
  99. nameContainer = containerObj.transform;
  100. }
  101. private void CreateDefaultTextPrefab()
  102. {
  103. // Create a simple TextMeshPro 3D prefab if none is assigned
  104. GameObject prefab = new GameObject("DefaultLocationNameText");
  105. TextMeshPro tmp = prefab.AddComponent<TextMeshPro>();
  106. tmp.text = "Sample Text";
  107. tmp.fontSize = textSize; // Use the large font size (72f)
  108. tmp.color = Color.white;
  109. tmp.alignment = TextAlignmentOptions.Center;
  110. tmp.autoSizeTextContainer = false; // Disable auto-sizing based on user findings
  111. // Make sure the text has proper bounds - use larger size based on user findings
  112. tmp.rectTransform.sizeDelta = new Vector2(40, 20);
  113. // Try to assign a font asset - this is crucial for visibility
  114. TMP_FontAsset fontAsset = null;
  115. // First try to get the default font from TMP Settings
  116. if (TMP_Settings.defaultFontAsset != null)
  117. {
  118. fontAsset = TMP_Settings.defaultFontAsset;
  119. }
  120. else
  121. {
  122. // Try to load the default TMP font from Resources
  123. fontAsset = Resources.Load<TMP_FontAsset>("LiberationSans SDF");
  124. if (fontAsset == null)
  125. {
  126. // Try alternative resource paths
  127. fontAsset = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
  128. }
  129. }
  130. if (fontAsset != null)
  131. {
  132. tmp.font = fontAsset;
  133. }
  134. else
  135. {
  136. // If no TMP font is found, we'll rely on the default behavior
  137. Debug.LogWarning("WorldSpaceLocationNames: No TMP_FontAsset found, using default font");
  138. }
  139. // Set up the material properly
  140. MeshRenderer renderer = prefab.GetComponent<MeshRenderer>();
  141. if (renderer != null)
  142. {
  143. // Ensure renderer is enabled
  144. renderer.enabled = true;
  145. renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  146. renderer.receiveShadows = false;
  147. // Try to use the font's material if available
  148. if (tmp.font != null && tmp.font.material != null)
  149. {
  150. renderer.material = tmp.font.material;
  151. }
  152. else
  153. {
  154. // Create a basic material for TextMeshPro with fallbacks
  155. Material tmpMaterial = null;
  156. if (Shader.Find("TextMeshPro/Distance Field") != null)
  157. {
  158. tmpMaterial = new Material(Shader.Find("TextMeshPro/Distance Field"));
  159. tmpMaterial.color = Color.white;
  160. }
  161. else if (Shader.Find("TextMeshPro/Mobile/Distance Field") != null)
  162. {
  163. tmpMaterial = new Material(Shader.Find("TextMeshPro/Mobile/Distance Field"));
  164. tmpMaterial.color = Color.white;
  165. }
  166. else if (Shader.Find("GUI/Text Shader") != null)
  167. {
  168. tmpMaterial = new Material(Shader.Find("GUI/Text Shader"));
  169. tmpMaterial.color = Color.white;
  170. }
  171. else
  172. {
  173. // Last resort - simple unlit color
  174. tmpMaterial = new Material(Shader.Find("Unlit/Color"));
  175. tmpMaterial.color = Color.white;
  176. }
  177. if (tmpMaterial != null)
  178. {
  179. renderer.material = tmpMaterial;
  180. }
  181. }
  182. }
  183. textPrefab = prefab;
  184. if (debugMode)
  185. {
  186. Debug.Log($"WorldSpaceLocationNames: Created default TextMeshPro prefab");
  187. Debug.Log($" - Font: {(tmp.font != null ? tmp.font.name : "NULL")}");
  188. Debug.Log($" - Material: {(renderer.material != null ? renderer.material.name : "NULL")}");
  189. Debug.Log($" - Shader: {(renderer.material != null && renderer.material.shader != null ? renderer.material.shader.name : "NULL")}");
  190. }
  191. }
  192. private void CreateFallbackTextPrefab()
  193. {
  194. // Create a simple Unity TextMesh 3D prefab as fallback
  195. GameObject prefab = new GameObject("FallbackLocationNameText");
  196. TextMesh textMesh = prefab.AddComponent<TextMesh>();
  197. textMesh.text = "Sample Text";
  198. textMesh.fontSize = (int)textSize;
  199. textMesh.color = Color.white;
  200. textMesh.anchor = TextAnchor.MiddleCenter;
  201. textMesh.alignment = TextAlignment.Center;
  202. // Set up the mesh renderer with a simple material
  203. MeshRenderer renderer = prefab.GetComponent<MeshRenderer>();
  204. if (renderer != null)
  205. {
  206. // Use Unity's default font material
  207. Material fontMaterial = Resources.GetBuiltinResource<Material>("Default-Material");
  208. if (fontMaterial != null)
  209. {
  210. renderer.material = fontMaterial;
  211. }
  212. else
  213. {
  214. // Create a simple unlit material
  215. Material simpleMaterial = new Material(Shader.Find("Unlit/Color"));
  216. simpleMaterial.color = Color.white;
  217. renderer.material = simpleMaterial;
  218. }
  219. }
  220. textPrefab = prefab;
  221. if (debugMode) Debug.Log("WorldSpaceLocationNames: Created fallback Unity TextMesh prefab");
  222. }
  223. public void RefreshLocationNames()
  224. {
  225. ClearAllNames();
  226. if (mapData == null || textPrefab == null)
  227. {
  228. if (debugMode) Debug.LogWarning($"WorldSpaceLocationNames: Cannot refresh - mapData: {(mapData != null ? "OK" : "NULL")}, textPrefab: {(textPrefab != null ? "OK" : "NULL")}");
  229. return;
  230. }
  231. if (debugMode) Debug.Log("WorldSpaceLocationNames: Refreshing location names...");
  232. // Display settlement names
  233. if (showSettlementNames)
  234. {
  235. DisplaySettlementNames();
  236. }
  237. // Display geographic feature names
  238. if (featureManager != null && featureManager.GeographicFeatures != null)
  239. {
  240. foreach (var feature in featureManager.GeographicFeatures)
  241. {
  242. if (ShouldDisplayFeature(feature))
  243. {
  244. CreateFeatureName(feature);
  245. }
  246. }
  247. }
  248. if (debugMode)
  249. {
  250. Debug.Log($"WorldSpaceLocationNames: Created {nameObjects.Count} name objects");
  251. // Debug camera information
  252. if (mainCamera != null)
  253. {
  254. Debug.Log($" - Camera position: {mainCamera.transform.position}");
  255. Debug.Log($" - Camera rotation: {mainCamera.transform.rotation.eulerAngles}");
  256. Debug.Log($" - Camera far clip: {mainCamera.farClipPlane}");
  257. }
  258. // Debug first few name objects
  259. for (int i = 0; i < Mathf.Min(3, nameObjects.Count); i++)
  260. {
  261. if (nameObjects[i] != null)
  262. {
  263. Debug.Log($" - Name object {i}: '{nameObjects[i].name}' at {nameObjects[i].transform.position}");
  264. // Check if it has TextMeshPro
  265. TextMeshPro tmp = nameObjects[i].GetComponent<TextMeshPro>();
  266. if (tmp != null)
  267. {
  268. Debug.Log($" - TMP text: '{tmp.text}', size: {tmp.fontSize}, bounds: {tmp.bounds}");
  269. Debug.Log($" - TMP renderer enabled: {tmp.renderer.enabled}");
  270. }
  271. // Check distance to camera
  272. if (mainCamera != null)
  273. {
  274. float distance = Vector3.Distance(mainCamera.transform.position, nameObjects[i].transform.position);
  275. Debug.Log($" - Distance to camera: {distance:F2}");
  276. }
  277. }
  278. }
  279. }
  280. }
  281. private void DisplaySettlementNames()
  282. {
  283. var settlements = mapData.GetAllSettlements();
  284. if (debugMode) Debug.Log($"WorldSpaceLocationNames: Creating {settlements.Count} settlement names");
  285. foreach (var settlement in settlements)
  286. {
  287. CreateSettlementName(settlement);
  288. }
  289. }
  290. private bool ShouldDisplayFeature(GeographicFeature feature)
  291. {
  292. switch (feature.type)
  293. {
  294. case GeographicFeatureType.Forest:
  295. return showForestNames;
  296. case GeographicFeatureType.Lake:
  297. return showLakeNames;
  298. case GeographicFeatureType.Plain:
  299. return showPlainNames;
  300. case GeographicFeatureType.Mountain:
  301. return showMountainNames;
  302. case GeographicFeatureType.River:
  303. return showRiverNames;
  304. default:
  305. return false;
  306. }
  307. }
  308. private void CreateSettlementName(Settlement settlement)
  309. {
  310. // Position text directly at the settlement location with a small height offset
  311. Vector3 worldPosition = new Vector3(settlement.position.x, heightOffset, settlement.position.y);
  312. Color color = settlementNameColor;
  313. float fontSize = settlement.Type == SettlementType.Town ? textSize * 1.2f : textSize;
  314. GameObject nameObj = CreateNameObject(settlement.name, worldPosition, color, fontSize);
  315. if (debugMode)
  316. {
  317. Debug.Log($"WorldSpaceLocationNames: Created settlement name '{settlement.name}' at {worldPosition}");
  318. Debug.Log($" - Settlement position: {settlement.position}");
  319. Debug.Log($" - GameObject position: {nameObj.transform.position}");
  320. Debug.Log($" - Container position: {nameContainer.position}");
  321. Debug.Log($" - Text height: {heightOffset} (directly on terrain)");
  322. }
  323. }
  324. private void CreateFeatureName(GeographicFeature feature)
  325. {
  326. Vector2Int featureCenter = feature.GetCenterPosition();
  327. // Position text directly at the feature center with a small height offset
  328. Vector3 worldPosition = new Vector3(featureCenter.x, heightOffset, featureCenter.y);
  329. Color color = GetFeatureColor(feature.type);
  330. float fontSize = GetFeatureFontSize(feature.type);
  331. GameObject nameObj = CreateNameObject(feature.name, worldPosition, color, fontSize);
  332. // Add opacity for undiscovered features
  333. if (!feature.isDiscovered)
  334. {
  335. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  336. if (tmp != null)
  337. {
  338. Color c = tmp.color;
  339. c.a = 0.6f;
  340. tmp.color = c;
  341. }
  342. }
  343. if (debugMode)
  344. {
  345. Debug.Log($"WorldSpaceLocationNames: Created feature name '{feature.name}' at {worldPosition}");
  346. }
  347. }
  348. private GameObject CreateNameObject(string text, Vector3 position, Color color, float fontSize)
  349. {
  350. GameObject nameObj = Instantiate(textPrefab, nameContainer);
  351. nameObj.name = $"LocationName_{text}";
  352. // Set position after instantiation to ensure it's not reset
  353. nameObj.transform.position = position;
  354. nameObj.transform.rotation = Quaternion.identity;
  355. // Make sure the object is on a visible layer
  356. nameObj.layer = 0; // Default layer
  357. // Try TextMeshPro first
  358. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  359. if (tmp != null)
  360. {
  361. tmp.text = text;
  362. tmp.color = color;
  363. tmp.fontSize = Mathf.Max(fontSize, 72f); // Force minimum 72pt size for visibility
  364. tmp.alignment = TextAlignmentOptions.Center;
  365. // Based on user findings: disable auto-sizing and set manual size
  366. tmp.autoSizeTextContainer = false; // Disable auto-sizing
  367. tmp.rectTransform.sizeDelta = new Vector2(200, 50); // Much larger text area
  368. // Force the text to update and generate mesh
  369. tmp.ForceMeshUpdate();
  370. // Ensure proper material setup and renderer activation
  371. if (tmp.renderer != null)
  372. {
  373. tmp.renderer.enabled = true; // Explicitly enable renderer
  374. tmp.renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  375. tmp.renderer.receiveShadows = false;
  376. // Set sorting layer to render above roads and map tiles
  377. tmp.renderer.sortingLayerName = sortingLayerName;
  378. tmp.renderer.sortingOrder = sortingOrder;
  379. // Force material assignment if missing
  380. if (tmp.renderer.material == null || tmp.renderer.material.shader == null)
  381. {
  382. Material tmpMaterial = null;
  383. // Try to find a working TextMeshPro shader
  384. if (Shader.Find("TextMeshPro/Distance Field") != null)
  385. {
  386. tmpMaterial = new Material(Shader.Find("TextMeshPro/Distance Field"));
  387. }
  388. else if (Shader.Find("TextMeshPro/Mobile/Distance Field") != null)
  389. {
  390. tmpMaterial = new Material(Shader.Find("TextMeshPro/Mobile/Distance Field"));
  391. }
  392. else if (Shader.Find("GUI/Text Shader") != null)
  393. {
  394. tmpMaterial = new Material(Shader.Find("GUI/Text Shader"));
  395. }
  396. else
  397. {
  398. // Last resort - unlit color
  399. tmpMaterial = new Material(Shader.Find("Unlit/Color"));
  400. tmpMaterial.color = color;
  401. }
  402. if (tmpMaterial != null)
  403. {
  404. tmp.renderer.material = tmpMaterial;
  405. }
  406. }
  407. }
  408. // Force another mesh update after material assignment
  409. tmp.ForceMeshUpdate();
  410. if (debugMode)
  411. {
  412. Debug.Log($"WorldSpaceLocationNames: TextMeshPro setup - Text: '{text}', Font Size: {tmp.fontSize}, Color: {color}");
  413. Debug.Log($" - Position: {nameObj.transform.position}");
  414. Debug.Log($" - Renderer enabled: {tmp.renderer.enabled}");
  415. Debug.Log($" - Material: {(tmp.renderer.material != null ? tmp.renderer.material.name : "NULL")}");
  416. Debug.Log($" - Shader: {(tmp.renderer.material != null && tmp.renderer.material.shader != null ? tmp.renderer.material.shader.name : "NULL")}");
  417. Debug.Log($" - Bounds: {tmp.bounds}");
  418. Debug.Log($" - Auto-size enabled: {tmp.autoSizeTextContainer}");
  419. }
  420. }
  421. else
  422. {
  423. // Fallback to Unity TextMesh
  424. TextMesh textMesh = nameObj.GetComponent<TextMesh>();
  425. if (textMesh != null)
  426. {
  427. textMesh.text = text;
  428. textMesh.color = color;
  429. textMesh.fontSize = (int)Mathf.Max(fontSize, 72f); // Force minimum 72pt size for visibility
  430. textMesh.anchor = TextAnchor.MiddleCenter;
  431. textMesh.alignment = TextAlignment.Center;
  432. // Ensure renderer is enabled
  433. MeshRenderer meshRenderer = nameObj.GetComponent<MeshRenderer>();
  434. if (meshRenderer != null)
  435. {
  436. meshRenderer.enabled = true; // Explicitly enable renderer
  437. meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  438. meshRenderer.receiveShadows = false;
  439. // Set sorting layer to render above roads and map tiles
  440. meshRenderer.sortingLayerName = sortingLayerName;
  441. meshRenderer.sortingOrder = sortingOrder;
  442. }
  443. if (debugMode)
  444. {
  445. Debug.Log($"WorldSpaceLocationNames: Unity TextMesh setup - Text: '{text}', Font Size: {textMesh.fontSize}, Color: {color}");
  446. Debug.Log($" - Position: {nameObj.transform.position}");
  447. Debug.Log($" - Renderer enabled: {(meshRenderer != null ? meshRenderer.enabled : false)}");
  448. }
  449. }
  450. }
  451. // Use a more visible scale
  452. nameObj.transform.localScale = Vector3.one;
  453. // Set rotation based on settings
  454. if (faceCamera)
  455. {
  456. // Add camera-facing behavior if enabled
  457. LookAtCamera lookAt = nameObj.GetComponent<LookAtCamera>();
  458. if (lookAt == null)
  459. {
  460. lookAt = nameObj.AddComponent<LookAtCamera>();
  461. }
  462. lookAt.targetCamera = mainCamera;
  463. }
  464. else
  465. {
  466. // Use fixed rotation for better control
  467. nameObj.transform.rotation = Quaternion.Euler(fixedRotation);
  468. if (debugMode)
  469. {
  470. Debug.Log($" - Applied fixed rotation: {fixedRotation}");
  471. }
  472. }
  473. // Distance-based visibility disabled by default (causes visibility issues)
  474. // Only add if maxViewDistance is explicitly set > 0
  475. if (maxViewDistance > 0)
  476. {
  477. DistanceBasedVisibility visibility = nameObj.GetComponent<DistanceBasedVisibility>();
  478. if (visibility == null)
  479. {
  480. visibility = nameObj.AddComponent<DistanceBasedVisibility>();
  481. }
  482. visibility.maxDistance = maxViewDistance;
  483. visibility.targetCamera = mainCamera;
  484. if (debugMode)
  485. {
  486. Debug.Log($" - Added distance visibility with max distance: {maxViewDistance}");
  487. }
  488. }
  489. else if (debugMode)
  490. {
  491. Debug.Log($" - Distance-based visibility disabled (maxViewDistance = {maxViewDistance})");
  492. }
  493. nameObjects.Add(nameObj);
  494. return nameObj;
  495. }
  496. private Color GetFeatureColor(GeographicFeatureType type)
  497. {
  498. switch (type)
  499. {
  500. case GeographicFeatureType.Forest:
  501. return forestNameColor;
  502. case GeographicFeatureType.Lake:
  503. return lakeNameColor;
  504. case GeographicFeatureType.Plain:
  505. return plainNameColor;
  506. case GeographicFeatureType.Mountain:
  507. return mountainNameColor;
  508. case GeographicFeatureType.River:
  509. return riverNameColor;
  510. default:
  511. return Color.white;
  512. }
  513. }
  514. private float GetFeatureFontSize(GeographicFeatureType type)
  515. {
  516. float baseSize = textSize;
  517. switch (type)
  518. {
  519. case GeographicFeatureType.Mountain:
  520. return baseSize * 1.1f;
  521. case GeographicFeatureType.Forest:
  522. case GeographicFeatureType.Lake:
  523. return baseSize;
  524. case GeographicFeatureType.Plain:
  525. case GeographicFeatureType.River:
  526. return baseSize * 0.9f;
  527. default:
  528. return baseSize;
  529. }
  530. }
  531. private void ClearAllNames()
  532. {
  533. if (debugMode && nameObjects.Count > 0)
  534. {
  535. Debug.Log($"WorldSpaceLocationNames: Clearing {nameObjects.Count} existing name objects");
  536. }
  537. foreach (var nameObj in nameObjects)
  538. {
  539. if (nameObj != null)
  540. {
  541. DestroyImmediate(nameObj);
  542. }
  543. }
  544. nameObjects.Clear();
  545. }
  546. // Public methods for toggling different name types
  547. public void ToggleSettlementNames(bool show)
  548. {
  549. showSettlementNames = show;
  550. RefreshLocationNames();
  551. }
  552. public void ToggleForestNames(bool show)
  553. {
  554. showForestNames = show;
  555. RefreshLocationNames();
  556. }
  557. public void ToggleLakeNames(bool show)
  558. {
  559. showLakeNames = show;
  560. RefreshLocationNames();
  561. }
  562. public void TogglePlainNames(bool show)
  563. {
  564. showPlainNames = show;
  565. RefreshLocationNames();
  566. }
  567. public void ToggleMountainNames(bool show)
  568. {
  569. showMountainNames = show;
  570. RefreshLocationNames();
  571. }
  572. public void ToggleRiverNames(bool show)
  573. {
  574. showRiverNames = show;
  575. RefreshLocationNames();
  576. }
  577. [ContextMenu("Refresh Names")]
  578. public void RefreshNamesManual()
  579. {
  580. RefreshLocationNames();
  581. }
  582. private void OnDestroy()
  583. {
  584. ClearAllNames();
  585. }
  586. // Test method to create a simple text object at camera position for debugging
  587. [ContextMenu("Create Test Text")]
  588. public void CreateTestText()
  589. {
  590. if (mainCamera == null)
  591. {
  592. mainCamera = Camera.main ?? FindFirstObjectByType<Camera>();
  593. }
  594. if (textPrefab == null)
  595. {
  596. CreateDefaultTextPrefab();
  597. }
  598. if (nameContainer == null)
  599. {
  600. CreateNameContainer();
  601. }
  602. if (mainCamera != null)
  603. {
  604. // Create test text in front of camera at terrain level
  605. Vector3 testPosition = mainCamera.transform.position + mainCamera.transform.forward * 10f;
  606. testPosition.y = heightOffset; // Same height as location names (on terrain)
  607. GameObject testObj = CreateNameObject("TEST TEXT", testPosition, Color.yellow, textSize);
  608. Debug.Log($"Created test text at {testPosition}");
  609. Debug.Log($"Camera is at {mainCamera.transform.position}");
  610. Debug.Log($"Distance: {Vector3.Distance(mainCamera.transform.position, testPosition):F2}");
  611. }
  612. else
  613. {
  614. Debug.LogError("No camera found for test text creation");
  615. }
  616. }
  617. // Refresh all location names from context menu
  618. [ContextMenu("Refresh Location Names")]
  619. public void ForceRefreshLocationNames()
  620. {
  621. RefreshLocationNames();
  622. }
  623. // Remove all LookAtCamera components to allow manual rotation control
  624. [ContextMenu("Remove Camera Facing")]
  625. public void RemoveCameraFacing()
  626. {
  627. foreach (var nameObj in nameObjects)
  628. {
  629. if (nameObj != null)
  630. {
  631. LookAtCamera lookAt = nameObj.GetComponent<LookAtCamera>();
  632. if (lookAt != null)
  633. {
  634. DestroyImmediate(lookAt);
  635. // Apply fixed rotation
  636. nameObj.transform.rotation = Quaternion.Euler(fixedRotation);
  637. }
  638. }
  639. }
  640. // Also disable face camera for future objects
  641. faceCamera = false;
  642. Debug.Log($"Removed LookAtCamera components from {nameObjects.Count} objects and applied fixed rotation: {fixedRotation}");
  643. }
  644. // Apply all the manual fixes you discovered automatically
  645. [ContextMenu("Fix Text Visibility")]
  646. public void FixTextVisibility()
  647. {
  648. int fixedCount = 0;
  649. foreach (var nameObj in nameObjects)
  650. {
  651. if (nameObj != null)
  652. {
  653. // Remove distance-based visibility component
  654. DistanceBasedVisibility distanceVis = nameObj.GetComponent<DistanceBasedVisibility>();
  655. if (distanceVis != null)
  656. {
  657. DestroyImmediate(distanceVis);
  658. }
  659. // Remove LookAtCamera component
  660. LookAtCamera lookAt = nameObj.GetComponent<LookAtCamera>();
  661. if (lookAt != null)
  662. {
  663. DestroyImmediate(lookAt);
  664. }
  665. // Apply fixed rotation
  666. nameObj.transform.rotation = Quaternion.Euler(fixedRotation);
  667. // Fix TextMeshPro settings
  668. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  669. if (tmp != null)
  670. {
  671. // Ensure renderer is enabled
  672. if (tmp.renderer != null)
  673. {
  674. tmp.renderer.enabled = true;
  675. }
  676. // Set proper font size and disable auto-sizing
  677. tmp.fontSize = textSize; // Use the large size (72f)
  678. tmp.autoSizeTextContainer = false;
  679. tmp.rectTransform.sizeDelta = new Vector2(200, 50);
  680. // Force mesh update
  681. tmp.ForceMeshUpdate();
  682. fixedCount++;
  683. }
  684. // Fix Unity TextMesh if used instead
  685. TextMesh textMesh = nameObj.GetComponent<TextMesh>();
  686. if (textMesh != null)
  687. {
  688. MeshRenderer meshRenderer = nameObj.GetComponent<MeshRenderer>();
  689. if (meshRenderer != null)
  690. {
  691. meshRenderer.enabled = true;
  692. }
  693. textMesh.fontSize = (int)textSize;
  694. fixedCount++;
  695. }
  696. }
  697. }
  698. // Disable face camera and distance culling for future objects
  699. faceCamera = false;
  700. maxViewDistance = 0f;
  701. Debug.Log($"Fixed visibility settings for {fixedCount} text objects");
  702. Debug.Log($"- Removed distance-based visibility components");
  703. Debug.Log($"- Removed LookAtCamera components");
  704. Debug.Log($"- Enabled mesh renderers");
  705. Debug.Log($"- Set font size to {textSize}");
  706. Debug.Log($"- Applied fixed rotation: {fixedRotation}");
  707. }
  708. // Automatically apply visibility fixes at startup (no manual interaction needed)
  709. private void ApplyVisibilityFixesAtStartup()
  710. {
  711. if (debugMode)
  712. {
  713. Debug.Log("WorldSpaceLocationNames: Applying automatic visibility fixes at startup...");
  714. }
  715. int fixedCount = 0;
  716. foreach (var nameObj in nameObjects)
  717. {
  718. if (nameObj != null)
  719. {
  720. // Remove distance-based visibility component
  721. DistanceBasedVisibility distanceVis = nameObj.GetComponent<DistanceBasedVisibility>();
  722. if (distanceVis != null)
  723. {
  724. DestroyImmediate(distanceVis);
  725. }
  726. // Remove LookAtCamera component
  727. LookAtCamera lookAt = nameObj.GetComponent<LookAtCamera>();
  728. if (lookAt != null)
  729. {
  730. DestroyImmediate(lookAt);
  731. }
  732. // Apply fixed rotation
  733. nameObj.transform.rotation = Quaternion.Euler(fixedRotation);
  734. // Fix TextMeshPro settings
  735. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  736. if (tmp != null)
  737. {
  738. // Ensure renderer is enabled
  739. if (tmp.renderer != null)
  740. {
  741. tmp.renderer.enabled = true;
  742. // Set sorting layer to render above roads
  743. tmp.renderer.sortingLayerName = sortingLayerName;
  744. tmp.renderer.sortingOrder = sortingOrder;
  745. }
  746. // FORCE the font size to be large - this was the main issue
  747. tmp.fontSize = Mathf.Max(textSize, 72f); // Ensure minimum 72pt size
  748. tmp.autoSizeTextContainer = false;
  749. tmp.rectTransform.sizeDelta = new Vector2(200, 50);
  750. // Force mesh update
  751. tmp.ForceMeshUpdate();
  752. if (debugMode)
  753. {
  754. Debug.Log($" - Fixed text '{tmp.text}': fontSize={tmp.fontSize}, renderer enabled={tmp.renderer.enabled}");
  755. }
  756. fixedCount++;
  757. }
  758. // Fix Unity TextMesh if used instead
  759. TextMesh textMesh = nameObj.GetComponent<TextMesh>();
  760. if (textMesh != null)
  761. {
  762. MeshRenderer meshRenderer = nameObj.GetComponent<MeshRenderer>();
  763. if (meshRenderer != null)
  764. {
  765. meshRenderer.enabled = true;
  766. // Set sorting layer to render above roads
  767. meshRenderer.sortingLayerName = sortingLayerName;
  768. meshRenderer.sortingOrder = sortingOrder;
  769. }
  770. textMesh.fontSize = (int)Mathf.Max(textSize, 72f);
  771. fixedCount++;
  772. }
  773. }
  774. }
  775. // Ensure settings are correct for future objects
  776. faceCamera = false;
  777. maxViewDistance = 0f;
  778. if (debugMode)
  779. {
  780. Debug.Log($"WorldSpaceLocationNames: Automatically fixed {fixedCount} text objects at startup");
  781. }
  782. }
  783. // Fix sorting order for existing text to render above roads
  784. [ContextMenu("Fix Text Sorting Order")]
  785. public void FixTextSortingOrder()
  786. {
  787. int fixedCount = 0;
  788. foreach (var nameObj in nameObjects)
  789. {
  790. if (nameObj != null)
  791. {
  792. // Fix TextMeshPro sorting
  793. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  794. if (tmp != null && tmp.renderer != null)
  795. {
  796. tmp.renderer.sortingLayerName = sortingLayerName;
  797. tmp.renderer.sortingOrder = sortingOrder;
  798. fixedCount++;
  799. }
  800. // Fix Unity TextMesh sorting
  801. TextMesh textMesh = nameObj.GetComponent<TextMesh>();
  802. if (textMesh != null)
  803. {
  804. MeshRenderer meshRenderer = nameObj.GetComponent<MeshRenderer>();
  805. if (meshRenderer != null)
  806. {
  807. meshRenderer.sortingLayerName = sortingLayerName;
  808. meshRenderer.sortingOrder = sortingOrder;
  809. fixedCount++;
  810. }
  811. }
  812. }
  813. }
  814. Debug.Log($"Fixed sorting order for {fixedCount} text objects");
  815. Debug.Log($"- Sorting Layer: {sortingLayerName}");
  816. Debug.Log($"- Sorting Order: {sortingOrder} (roads are 1, so text should be 2+)");
  817. }
  818. }
  819. // Helper component to make text face the camera
  820. public class LookAtCamera : MonoBehaviour
  821. {
  822. public Camera targetCamera;
  823. private void Update()
  824. {
  825. if (targetCamera != null)
  826. {
  827. // Make the text face the camera without flipping
  828. Vector3 directionToCamera = targetCamera.transform.position - transform.position;
  829. directionToCamera.y = 0; // Keep text upright by ignoring Y rotation
  830. if (directionToCamera != Vector3.zero)
  831. {
  832. transform.rotation = Quaternion.LookRotation(-directionToCamera, Vector3.up);
  833. }
  834. }
  835. }
  836. }
  837. // Helper component for distance-based visibility
  838. public class DistanceBasedVisibility : MonoBehaviour
  839. {
  840. public Camera targetCamera;
  841. public float maxDistance = 50f;
  842. private Renderer textRenderer;
  843. private void Start()
  844. {
  845. textRenderer = GetComponent<Renderer>();
  846. }
  847. private void Update()
  848. {
  849. if (targetCamera != null && textRenderer != null)
  850. {
  851. float distance = Vector3.Distance(transform.position, targetCamera.transform.position);
  852. textRenderer.enabled = distance <= maxDistance;
  853. }
  854. }
  855. }