WorldSpaceLocationNames.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  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 = false;
  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 if (debugMode)
  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. if (debugMode)
  609. {
  610. Debug.Log($"Created test text at {testPosition}");
  611. Debug.Log($"Camera is at {mainCamera.transform.position}");
  612. Debug.Log($"Distance: {Vector3.Distance(mainCamera.transform.position, testPosition):F2}");
  613. }
  614. }
  615. else if (debugMode)
  616. {
  617. Debug.LogError("No camera found for test text creation");
  618. }
  619. }
  620. // Refresh all location names from context menu
  621. [ContextMenu("Refresh Location Names")]
  622. public void ForceRefreshLocationNames()
  623. {
  624. RefreshLocationNames();
  625. }
  626. // Remove all LookAtCamera components to allow manual rotation control
  627. [ContextMenu("Remove Camera Facing")]
  628. public void RemoveCameraFacing()
  629. {
  630. foreach (var nameObj in nameObjects)
  631. {
  632. if (nameObj != null)
  633. {
  634. LookAtCamera lookAt = nameObj.GetComponent<LookAtCamera>();
  635. if (lookAt != null)
  636. {
  637. DestroyImmediate(lookAt);
  638. // Apply fixed rotation
  639. nameObj.transform.rotation = Quaternion.Euler(fixedRotation);
  640. }
  641. }
  642. }
  643. // Also disable face camera for future objects
  644. faceCamera = false;
  645. if (debugMode)
  646. {
  647. Debug.Log($"Removed LookAtCamera components from {nameObjects.Count} objects and applied fixed rotation: {fixedRotation}");
  648. }
  649. }
  650. // Apply all the manual fixes you discovered automatically
  651. [ContextMenu("Fix Text Visibility")]
  652. public void FixTextVisibility()
  653. {
  654. int fixedCount = 0;
  655. foreach (var nameObj in nameObjects)
  656. {
  657. if (nameObj != null)
  658. {
  659. // Remove distance-based visibility component
  660. DistanceBasedVisibility distanceVis = nameObj.GetComponent<DistanceBasedVisibility>();
  661. if (distanceVis != null)
  662. {
  663. DestroyImmediate(distanceVis);
  664. }
  665. // Remove LookAtCamera component
  666. LookAtCamera lookAt = nameObj.GetComponent<LookAtCamera>();
  667. if (lookAt != null)
  668. {
  669. DestroyImmediate(lookAt);
  670. }
  671. // Apply fixed rotation
  672. nameObj.transform.rotation = Quaternion.Euler(fixedRotation);
  673. // Fix TextMeshPro settings
  674. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  675. if (tmp != null)
  676. {
  677. // Ensure renderer is enabled
  678. if (tmp.renderer != null)
  679. {
  680. tmp.renderer.enabled = true;
  681. }
  682. // Set proper font size and disable auto-sizing
  683. tmp.fontSize = textSize; // Use the large size (72f)
  684. tmp.autoSizeTextContainer = false;
  685. tmp.rectTransform.sizeDelta = new Vector2(200, 50);
  686. // Force mesh update
  687. tmp.ForceMeshUpdate();
  688. fixedCount++;
  689. }
  690. // Fix Unity TextMesh if used instead
  691. TextMesh textMesh = nameObj.GetComponent<TextMesh>();
  692. if (textMesh != null)
  693. {
  694. MeshRenderer meshRenderer = nameObj.GetComponent<MeshRenderer>();
  695. if (meshRenderer != null)
  696. {
  697. meshRenderer.enabled = true;
  698. }
  699. textMesh.fontSize = (int)textSize;
  700. fixedCount++;
  701. }
  702. }
  703. }
  704. // Disable face camera and distance culling for future objects
  705. faceCamera = false;
  706. maxViewDistance = 0f;
  707. if (debugMode)
  708. {
  709. Debug.Log($"Fixed visibility settings for {fixedCount} text objects");
  710. Debug.Log($"- Removed distance-based visibility components");
  711. Debug.Log($"- Removed LookAtCamera components");
  712. Debug.Log($"- Enabled mesh renderers");
  713. Debug.Log($"- Set font size to {textSize}");
  714. Debug.Log($"- Applied fixed rotation: {fixedRotation}");
  715. }
  716. }
  717. // Automatically apply visibility fixes at startup (no manual interaction needed)
  718. private void ApplyVisibilityFixesAtStartup()
  719. {
  720. if (debugMode)
  721. {
  722. Debug.Log("WorldSpaceLocationNames: Applying automatic visibility fixes at startup...");
  723. }
  724. int fixedCount = 0;
  725. foreach (var nameObj in nameObjects)
  726. {
  727. if (nameObj != null)
  728. {
  729. // Remove distance-based visibility component
  730. DistanceBasedVisibility distanceVis = nameObj.GetComponent<DistanceBasedVisibility>();
  731. if (distanceVis != null)
  732. {
  733. DestroyImmediate(distanceVis);
  734. }
  735. // Remove LookAtCamera component
  736. LookAtCamera lookAt = nameObj.GetComponent<LookAtCamera>();
  737. if (lookAt != null)
  738. {
  739. DestroyImmediate(lookAt);
  740. }
  741. // Apply fixed rotation
  742. nameObj.transform.rotation = Quaternion.Euler(fixedRotation);
  743. // Fix TextMeshPro settings
  744. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  745. if (tmp != null)
  746. {
  747. // Ensure renderer is enabled
  748. if (tmp.renderer != null)
  749. {
  750. tmp.renderer.enabled = true;
  751. // Set sorting layer to render above roads
  752. tmp.renderer.sortingLayerName = sortingLayerName;
  753. tmp.renderer.sortingOrder = sortingOrder;
  754. }
  755. // FORCE the font size to be large - this was the main issue
  756. tmp.fontSize = Mathf.Max(textSize, 72f); // Ensure minimum 72pt size
  757. tmp.autoSizeTextContainer = false;
  758. tmp.rectTransform.sizeDelta = new Vector2(200, 50);
  759. // Force mesh update
  760. tmp.ForceMeshUpdate();
  761. if (debugMode)
  762. {
  763. Debug.Log($" - Fixed text '{tmp.text}': fontSize={tmp.fontSize}, renderer enabled={tmp.renderer.enabled}");
  764. }
  765. fixedCount++;
  766. }
  767. // Fix Unity TextMesh if used instead
  768. TextMesh textMesh = nameObj.GetComponent<TextMesh>();
  769. if (textMesh != null)
  770. {
  771. MeshRenderer meshRenderer = nameObj.GetComponent<MeshRenderer>();
  772. if (meshRenderer != null)
  773. {
  774. meshRenderer.enabled = true;
  775. // Set sorting layer to render above roads
  776. meshRenderer.sortingLayerName = sortingLayerName;
  777. meshRenderer.sortingOrder = sortingOrder;
  778. }
  779. textMesh.fontSize = (int)Mathf.Max(textSize, 72f);
  780. fixedCount++;
  781. }
  782. }
  783. }
  784. // Ensure settings are correct for future objects
  785. faceCamera = false;
  786. maxViewDistance = 0f;
  787. if (debugMode)
  788. {
  789. Debug.Log($"WorldSpaceLocationNames: Automatically fixed {fixedCount} text objects at startup");
  790. }
  791. }
  792. // Fix sorting order for existing text to render above roads
  793. [ContextMenu("Fix Text Sorting Order")]
  794. public void FixTextSortingOrder()
  795. {
  796. int fixedCount = 0;
  797. foreach (var nameObj in nameObjects)
  798. {
  799. if (nameObj != null)
  800. {
  801. // Fix TextMeshPro sorting
  802. TextMeshPro tmp = nameObj.GetComponent<TextMeshPro>();
  803. if (tmp != null && tmp.renderer != null)
  804. {
  805. tmp.renderer.sortingLayerName = sortingLayerName;
  806. tmp.renderer.sortingOrder = sortingOrder;
  807. fixedCount++;
  808. }
  809. // Fix Unity TextMesh sorting
  810. TextMesh textMesh = nameObj.GetComponent<TextMesh>();
  811. if (textMesh != null)
  812. {
  813. MeshRenderer meshRenderer = nameObj.GetComponent<MeshRenderer>();
  814. if (meshRenderer != null)
  815. {
  816. meshRenderer.sortingLayerName = sortingLayerName;
  817. meshRenderer.sortingOrder = sortingOrder;
  818. fixedCount++;
  819. }
  820. }
  821. }
  822. }
  823. if (debugMode)
  824. {
  825. Debug.Log($"Fixed sorting order for {fixedCount} text objects");
  826. Debug.Log($"- Sorting Layer: {sortingLayerName}");
  827. Debug.Log($"- Sorting Order: {sortingOrder} (roads are 1, so text should be 2+)");
  828. }
  829. }
  830. }
  831. // Helper component to make text face the camera
  832. public class LookAtCamera : MonoBehaviour
  833. {
  834. public Camera targetCamera;
  835. private void Update()
  836. {
  837. if (targetCamera != null)
  838. {
  839. // Make the text face the camera without flipping
  840. Vector3 directionToCamera = targetCamera.transform.position - transform.position;
  841. directionToCamera.y = 0; // Keep text upright by ignoring Y rotation
  842. if (directionToCamera != Vector3.zero)
  843. {
  844. transform.rotation = Quaternion.LookRotation(-directionToCamera, Vector3.up);
  845. }
  846. }
  847. }
  848. }
  849. // Helper component for distance-based visibility
  850. public class DistanceBasedVisibility : MonoBehaviour
  851. {
  852. public Camera targetCamera;
  853. public float maxDistance = 50f;
  854. private Renderer textRenderer;
  855. private void Start()
  856. {
  857. textRenderer = GetComponent<Renderer>();
  858. }
  859. private void Update()
  860. {
  861. if (targetCamera != null && textRenderer != null)
  862. {
  863. float distance = Vector3.Distance(transform.position, targetCamera.transform.position);
  864. textRenderer.enabled = distance <= maxDistance;
  865. }
  866. }
  867. }