using UnityEngine; using TMPro; using System.Collections.Generic; using System.Collections; public class WorldSpaceLocationNames : MonoBehaviour { [Header("Display Settings")] public bool showSettlementNames = true; public bool showForestNames = true; public bool showLakeNames = true; public bool showPlainNames = true; public bool showMountainNames = true; public bool showRiverNames = true; [Header("Visual Settings")] public Color settlementNameColor = Color.white; public Color forestNameColor = Color.green; public Color lakeNameColor = Color.cyan; public Color plainNameColor = Color.yellow; public Color mountainNameColor = Color.gray; public Color riverNameColor = Color.blue; [Header("3D Text Settings")] public GameObject textPrefab; // TextMeshPro 3D prefab public float textSize = 72f; // Large size for visibility (was 10f) public float heightOffset = 0.5f; // Small offset above terrain (0.5 = just above ground) public bool faceCamera = false; // Disabled by default to allow manual rotation control public Vector3 fixedRotation = new Vector3(90, 0, 0); // Fixed rotation for text (X=90 for flat text) public float maxViewDistance = 0f; // Disabled by default (was 100f) - distance culling causes visibility issues [Header("Rendering Order")] public string sortingLayerName = "Default"; // Sorting layer for text rendering public int sortingOrder = 2; // Order in layer (higher = renders on top). Roads are 1, so use 2+ public bool useBuiltInTextMesh = false; // Fallback to Unity's TextMesh if TextMeshPro fails [Header("Debug")] public bool debugMode = true; private List nameObjects = new List(); private GeographicFeatureManager featureManager; private MapData mapData; private MapMaker2 mapMaker; private Camera mainCamera; private Transform nameContainer; private void Start() { StartCoroutine(InitializeDelayed()); } private IEnumerator InitializeDelayed() { // Wait for map generation to complete yield return new WaitForSeconds(1f); InitializeReferences(); CreateNameContainer(); if (textPrefab == null) { CreateDefaultTextPrefab(); // If TextMeshPro setup failed, try built-in TextMesh as fallback if (textPrefab == null) { useBuiltInTextMesh = true; CreateFallbackTextPrefab(); } } yield return new WaitForSeconds(0.5f); RefreshLocationNames(); // Automatically apply visibility fixes at startup yield return new WaitForSeconds(0.1f); // Small delay to ensure objects are created ApplyVisibilityFixesAtStartup(); } private void InitializeReferences() { featureManager = FindFirstObjectByType(); // If GeographicFeatureManager isn't found, try to find it by name if (featureManager == null) { GameObject gfmObject = GameObject.Find("GeographicFeatureManager"); if (gfmObject != null) { featureManager = gfmObject.GetComponent(); } } mapMaker = FindFirstObjectByType(); mainCamera = Camera.main ?? FindFirstObjectByType(); if (mapMaker != null) { mapData = mapMaker.GetMapData(); } if (debugMode) { Debug.Log($"WorldSpaceLocationNames: Initialized"); Debug.Log($" - FeatureManager: {(featureManager != null ? $"Found ({featureManager.GeographicFeatures.Count} features)" : "NULL")}"); Debug.Log($" - MapData: {(mapData != null ? $"Found ({mapData.Width}x{mapData.Height})" : "NULL")}"); Debug.Log($" - Camera: {(mainCamera != null ? "Found" : "NULL")}"); } } private void CreateNameContainer() { GameObject containerObj = new GameObject("LocationNameContainer"); containerObj.transform.parent = transform; containerObj.transform.localPosition = Vector3.zero; containerObj.transform.localRotation = Quaternion.identity; containerObj.transform.localScale = Vector3.one; nameContainer = containerObj.transform; } private void CreateDefaultTextPrefab() { // Create a simple TextMeshPro 3D prefab if none is assigned GameObject prefab = new GameObject("DefaultLocationNameText"); TextMeshPro tmp = prefab.AddComponent(); tmp.text = "Sample Text"; tmp.fontSize = textSize; // Use the large font size (72f) tmp.color = Color.white; tmp.alignment = TextAlignmentOptions.Center; tmp.autoSizeTextContainer = false; // Disable auto-sizing based on user findings // Make sure the text has proper bounds - use larger size based on user findings tmp.rectTransform.sizeDelta = new Vector2(40, 20); // Try to assign a font asset - this is crucial for visibility TMP_FontAsset fontAsset = null; // First try to get the default font from TMP Settings if (TMP_Settings.defaultFontAsset != null) { fontAsset = TMP_Settings.defaultFontAsset; } else { // Try to load the default TMP font from Resources fontAsset = Resources.Load("LiberationSans SDF"); if (fontAsset == null) { // Try alternative resource paths fontAsset = Resources.Load("Fonts & Materials/LiberationSans SDF"); } } if (fontAsset != null) { tmp.font = fontAsset; } else { // If no TMP font is found, we'll rely on the default behavior Debug.LogWarning("WorldSpaceLocationNames: No TMP_FontAsset found, using default font"); } // Set up the material properly MeshRenderer renderer = prefab.GetComponent(); if (renderer != null) { // Ensure renderer is enabled renderer.enabled = true; renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; renderer.receiveShadows = false; // Try to use the font's material if available if (tmp.font != null && tmp.font.material != null) { renderer.material = tmp.font.material; } else { // Create a basic material for TextMeshPro with fallbacks Material tmpMaterial = null; if (Shader.Find("TextMeshPro/Distance Field") != null) { tmpMaterial = new Material(Shader.Find("TextMeshPro/Distance Field")); tmpMaterial.color = Color.white; } else if (Shader.Find("TextMeshPro/Mobile/Distance Field") != null) { tmpMaterial = new Material(Shader.Find("TextMeshPro/Mobile/Distance Field")); tmpMaterial.color = Color.white; } else if (Shader.Find("GUI/Text Shader") != null) { tmpMaterial = new Material(Shader.Find("GUI/Text Shader")); tmpMaterial.color = Color.white; } else { // Last resort - simple unlit color tmpMaterial = new Material(Shader.Find("Unlit/Color")); tmpMaterial.color = Color.white; } if (tmpMaterial != null) { renderer.material = tmpMaterial; } } } textPrefab = prefab; if (debugMode) { Debug.Log($"WorldSpaceLocationNames: Created default TextMeshPro prefab"); Debug.Log($" - Font: {(tmp.font != null ? tmp.font.name : "NULL")}"); Debug.Log($" - Material: {(renderer.material != null ? renderer.material.name : "NULL")}"); Debug.Log($" - Shader: {(renderer.material != null && renderer.material.shader != null ? renderer.material.shader.name : "NULL")}"); } } private void CreateFallbackTextPrefab() { // Create a simple Unity TextMesh 3D prefab as fallback GameObject prefab = new GameObject("FallbackLocationNameText"); TextMesh textMesh = prefab.AddComponent(); textMesh.text = "Sample Text"; textMesh.fontSize = (int)textSize; textMesh.color = Color.white; textMesh.anchor = TextAnchor.MiddleCenter; textMesh.alignment = TextAlignment.Center; // Set up the mesh renderer with a simple material MeshRenderer renderer = prefab.GetComponent(); if (renderer != null) { // Use Unity's default font material Material fontMaterial = Resources.GetBuiltinResource("Default-Material"); if (fontMaterial != null) { renderer.material = fontMaterial; } else { // Create a simple unlit material Material simpleMaterial = new Material(Shader.Find("Unlit/Color")); simpleMaterial.color = Color.white; renderer.material = simpleMaterial; } } textPrefab = prefab; if (debugMode) Debug.Log("WorldSpaceLocationNames: Created fallback Unity TextMesh prefab"); } public void RefreshLocationNames() { ClearAllNames(); if (mapData == null || textPrefab == null) { if (debugMode) Debug.LogWarning($"WorldSpaceLocationNames: Cannot refresh - mapData: {(mapData != null ? "OK" : "NULL")}, textPrefab: {(textPrefab != null ? "OK" : "NULL")}"); return; } if (debugMode) Debug.Log("WorldSpaceLocationNames: Refreshing location names..."); // Display settlement names if (showSettlementNames) { DisplaySettlementNames(); } // Display geographic feature names if (featureManager != null && featureManager.GeographicFeatures != null) { foreach (var feature in featureManager.GeographicFeatures) { if (ShouldDisplayFeature(feature)) { CreateFeatureName(feature); } } } if (debugMode) { Debug.Log($"WorldSpaceLocationNames: Created {nameObjects.Count} name objects"); // Debug camera information if (mainCamera != null) { Debug.Log($" - Camera position: {mainCamera.transform.position}"); Debug.Log($" - Camera rotation: {mainCamera.transform.rotation.eulerAngles}"); Debug.Log($" - Camera far clip: {mainCamera.farClipPlane}"); } // Debug first few name objects for (int i = 0; i < Mathf.Min(3, nameObjects.Count); i++) { if (nameObjects[i] != null) { Debug.Log($" - Name object {i}: '{nameObjects[i].name}' at {nameObjects[i].transform.position}"); // Check if it has TextMeshPro TextMeshPro tmp = nameObjects[i].GetComponent(); if (tmp != null) { Debug.Log($" - TMP text: '{tmp.text}', size: {tmp.fontSize}, bounds: {tmp.bounds}"); Debug.Log($" - TMP renderer enabled: {tmp.renderer.enabled}"); } // Check distance to camera if (mainCamera != null) { float distance = Vector3.Distance(mainCamera.transform.position, nameObjects[i].transform.position); Debug.Log($" - Distance to camera: {distance:F2}"); } } } } } private void DisplaySettlementNames() { var settlements = mapData.GetAllSettlements(); if (debugMode) Debug.Log($"WorldSpaceLocationNames: Creating {settlements.Count} settlement names"); foreach (var settlement in settlements) { CreateSettlementName(settlement); } } private bool ShouldDisplayFeature(GeographicFeature feature) { switch (feature.type) { case GeographicFeatureType.Forest: return showForestNames; case GeographicFeatureType.Lake: return showLakeNames; case GeographicFeatureType.Plain: return showPlainNames; case GeographicFeatureType.Mountain: return showMountainNames; case GeographicFeatureType.River: return showRiverNames; default: return false; } } private void CreateSettlementName(Settlement settlement) { // Position text directly at the settlement location with a small height offset Vector3 worldPosition = new Vector3(settlement.position.x, heightOffset, settlement.position.y); Color color = settlementNameColor; float fontSize = settlement.Type == SettlementType.Town ? textSize * 1.2f : textSize; GameObject nameObj = CreateNameObject(settlement.name, worldPosition, color, fontSize); if (debugMode) { Debug.Log($"WorldSpaceLocationNames: Created settlement name '{settlement.name}' at {worldPosition}"); Debug.Log($" - Settlement position: {settlement.position}"); Debug.Log($" - GameObject position: {nameObj.transform.position}"); Debug.Log($" - Container position: {nameContainer.position}"); Debug.Log($" - Text height: {heightOffset} (directly on terrain)"); } } private void CreateFeatureName(GeographicFeature feature) { Vector2Int featureCenter = feature.GetCenterPosition(); // Position text directly at the feature center with a small height offset Vector3 worldPosition = new Vector3(featureCenter.x, heightOffset, featureCenter.y); Color color = GetFeatureColor(feature.type); float fontSize = GetFeatureFontSize(feature.type); GameObject nameObj = CreateNameObject(feature.name, worldPosition, color, fontSize); // Add opacity for undiscovered features if (!feature.isDiscovered) { TextMeshPro tmp = nameObj.GetComponent(); if (tmp != null) { Color c = tmp.color; c.a = 0.6f; tmp.color = c; } } if (debugMode) { Debug.Log($"WorldSpaceLocationNames: Created feature name '{feature.name}' at {worldPosition}"); } } private GameObject CreateNameObject(string text, Vector3 position, Color color, float fontSize) { GameObject nameObj = Instantiate(textPrefab, nameContainer); nameObj.name = $"LocationName_{text}"; // Set position after instantiation to ensure it's not reset nameObj.transform.position = position; nameObj.transform.rotation = Quaternion.identity; // Make sure the object is on a visible layer nameObj.layer = 0; // Default layer // Try TextMeshPro first TextMeshPro tmp = nameObj.GetComponent(); if (tmp != null) { tmp.text = text; tmp.color = color; tmp.fontSize = Mathf.Max(fontSize, 72f); // Force minimum 72pt size for visibility tmp.alignment = TextAlignmentOptions.Center; // Based on user findings: disable auto-sizing and set manual size tmp.autoSizeTextContainer = false; // Disable auto-sizing tmp.rectTransform.sizeDelta = new Vector2(200, 50); // Much larger text area // Force the text to update and generate mesh tmp.ForceMeshUpdate(); // Ensure proper material setup and renderer activation if (tmp.renderer != null) { tmp.renderer.enabled = true; // Explicitly enable renderer tmp.renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; tmp.renderer.receiveShadows = false; // Set sorting layer to render above roads and map tiles tmp.renderer.sortingLayerName = sortingLayerName; tmp.renderer.sortingOrder = sortingOrder; // Force material assignment if missing if (tmp.renderer.material == null || tmp.renderer.material.shader == null) { Material tmpMaterial = null; // Try to find a working TextMeshPro shader if (Shader.Find("TextMeshPro/Distance Field") != null) { tmpMaterial = new Material(Shader.Find("TextMeshPro/Distance Field")); } else if (Shader.Find("TextMeshPro/Mobile/Distance Field") != null) { tmpMaterial = new Material(Shader.Find("TextMeshPro/Mobile/Distance Field")); } else if (Shader.Find("GUI/Text Shader") != null) { tmpMaterial = new Material(Shader.Find("GUI/Text Shader")); } else { // Last resort - unlit color tmpMaterial = new Material(Shader.Find("Unlit/Color")); tmpMaterial.color = color; } if (tmpMaterial != null) { tmp.renderer.material = tmpMaterial; } } } // Force another mesh update after material assignment tmp.ForceMeshUpdate(); if (debugMode) { Debug.Log($"WorldSpaceLocationNames: TextMeshPro setup - Text: '{text}', Font Size: {tmp.fontSize}, Color: {color}"); Debug.Log($" - Position: {nameObj.transform.position}"); Debug.Log($" - Renderer enabled: {tmp.renderer.enabled}"); Debug.Log($" - Material: {(tmp.renderer.material != null ? tmp.renderer.material.name : "NULL")}"); Debug.Log($" - Shader: {(tmp.renderer.material != null && tmp.renderer.material.shader != null ? tmp.renderer.material.shader.name : "NULL")}"); Debug.Log($" - Bounds: {tmp.bounds}"); Debug.Log($" - Auto-size enabled: {tmp.autoSizeTextContainer}"); } } else { // Fallback to Unity TextMesh TextMesh textMesh = nameObj.GetComponent(); if (textMesh != null) { textMesh.text = text; textMesh.color = color; textMesh.fontSize = (int)Mathf.Max(fontSize, 72f); // Force minimum 72pt size for visibility textMesh.anchor = TextAnchor.MiddleCenter; textMesh.alignment = TextAlignment.Center; // Ensure renderer is enabled MeshRenderer meshRenderer = nameObj.GetComponent(); if (meshRenderer != null) { meshRenderer.enabled = true; // Explicitly enable renderer meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; meshRenderer.receiveShadows = false; // Set sorting layer to render above roads and map tiles meshRenderer.sortingLayerName = sortingLayerName; meshRenderer.sortingOrder = sortingOrder; } if (debugMode) { Debug.Log($"WorldSpaceLocationNames: Unity TextMesh setup - Text: '{text}', Font Size: {textMesh.fontSize}, Color: {color}"); Debug.Log($" - Position: {nameObj.transform.position}"); Debug.Log($" - Renderer enabled: {(meshRenderer != null ? meshRenderer.enabled : false)}"); } } } // Use a more visible scale nameObj.transform.localScale = Vector3.one; // Set rotation based on settings if (faceCamera) { // Add camera-facing behavior if enabled LookAtCamera lookAt = nameObj.GetComponent(); if (lookAt == null) { lookAt = nameObj.AddComponent(); } lookAt.targetCamera = mainCamera; } else { // Use fixed rotation for better control nameObj.transform.rotation = Quaternion.Euler(fixedRotation); if (debugMode) { Debug.Log($" - Applied fixed rotation: {fixedRotation}"); } } // Distance-based visibility disabled by default (causes visibility issues) // Only add if maxViewDistance is explicitly set > 0 if (maxViewDistance > 0) { DistanceBasedVisibility visibility = nameObj.GetComponent(); if (visibility == null) { visibility = nameObj.AddComponent(); } visibility.maxDistance = maxViewDistance; visibility.targetCamera = mainCamera; if (debugMode) { Debug.Log($" - Added distance visibility with max distance: {maxViewDistance}"); } } else if (debugMode) { Debug.Log($" - Distance-based visibility disabled (maxViewDistance = {maxViewDistance})"); } nameObjects.Add(nameObj); return nameObj; } private Color GetFeatureColor(GeographicFeatureType type) { switch (type) { case GeographicFeatureType.Forest: return forestNameColor; case GeographicFeatureType.Lake: return lakeNameColor; case GeographicFeatureType.Plain: return plainNameColor; case GeographicFeatureType.Mountain: return mountainNameColor; case GeographicFeatureType.River: return riverNameColor; default: return Color.white; } } private float GetFeatureFontSize(GeographicFeatureType type) { float baseSize = textSize; switch (type) { case GeographicFeatureType.Mountain: return baseSize * 1.1f; case GeographicFeatureType.Forest: case GeographicFeatureType.Lake: return baseSize; case GeographicFeatureType.Plain: case GeographicFeatureType.River: return baseSize * 0.9f; default: return baseSize; } } private void ClearAllNames() { if (debugMode && nameObjects.Count > 0) { Debug.Log($"WorldSpaceLocationNames: Clearing {nameObjects.Count} existing name objects"); } foreach (var nameObj in nameObjects) { if (nameObj != null) { DestroyImmediate(nameObj); } } nameObjects.Clear(); } // Public methods for toggling different name types public void ToggleSettlementNames(bool show) { showSettlementNames = show; RefreshLocationNames(); } public void ToggleForestNames(bool show) { showForestNames = show; RefreshLocationNames(); } public void ToggleLakeNames(bool show) { showLakeNames = show; RefreshLocationNames(); } public void TogglePlainNames(bool show) { showPlainNames = show; RefreshLocationNames(); } public void ToggleMountainNames(bool show) { showMountainNames = show; RefreshLocationNames(); } public void ToggleRiverNames(bool show) { showRiverNames = show; RefreshLocationNames(); } [ContextMenu("Refresh Names")] public void RefreshNamesManual() { RefreshLocationNames(); } private void OnDestroy() { ClearAllNames(); } // Test method to create a simple text object at camera position for debugging [ContextMenu("Create Test Text")] public void CreateTestText() { if (mainCamera == null) { mainCamera = Camera.main ?? FindFirstObjectByType(); } if (textPrefab == null) { CreateDefaultTextPrefab(); } if (nameContainer == null) { CreateNameContainer(); } if (mainCamera != null) { // Create test text in front of camera at terrain level Vector3 testPosition = mainCamera.transform.position + mainCamera.transform.forward * 10f; testPosition.y = heightOffset; // Same height as location names (on terrain) GameObject testObj = CreateNameObject("TEST TEXT", testPosition, Color.yellow, textSize); Debug.Log($"Created test text at {testPosition}"); Debug.Log($"Camera is at {mainCamera.transform.position}"); Debug.Log($"Distance: {Vector3.Distance(mainCamera.transform.position, testPosition):F2}"); } else { Debug.LogError("No camera found for test text creation"); } } // Refresh all location names from context menu [ContextMenu("Refresh Location Names")] public void ForceRefreshLocationNames() { RefreshLocationNames(); } // Remove all LookAtCamera components to allow manual rotation control [ContextMenu("Remove Camera Facing")] public void RemoveCameraFacing() { foreach (var nameObj in nameObjects) { if (nameObj != null) { LookAtCamera lookAt = nameObj.GetComponent(); if (lookAt != null) { DestroyImmediate(lookAt); // Apply fixed rotation nameObj.transform.rotation = Quaternion.Euler(fixedRotation); } } } // Also disable face camera for future objects faceCamera = false; Debug.Log($"Removed LookAtCamera components from {nameObjects.Count} objects and applied fixed rotation: {fixedRotation}"); } // Apply all the manual fixes you discovered automatically [ContextMenu("Fix Text Visibility")] public void FixTextVisibility() { int fixedCount = 0; foreach (var nameObj in nameObjects) { if (nameObj != null) { // Remove distance-based visibility component DistanceBasedVisibility distanceVis = nameObj.GetComponent(); if (distanceVis != null) { DestroyImmediate(distanceVis); } // Remove LookAtCamera component LookAtCamera lookAt = nameObj.GetComponent(); if (lookAt != null) { DestroyImmediate(lookAt); } // Apply fixed rotation nameObj.transform.rotation = Quaternion.Euler(fixedRotation); // Fix TextMeshPro settings TextMeshPro tmp = nameObj.GetComponent(); if (tmp != null) { // Ensure renderer is enabled if (tmp.renderer != null) { tmp.renderer.enabled = true; } // Set proper font size and disable auto-sizing tmp.fontSize = textSize; // Use the large size (72f) tmp.autoSizeTextContainer = false; tmp.rectTransform.sizeDelta = new Vector2(200, 50); // Force mesh update tmp.ForceMeshUpdate(); fixedCount++; } // Fix Unity TextMesh if used instead TextMesh textMesh = nameObj.GetComponent(); if (textMesh != null) { MeshRenderer meshRenderer = nameObj.GetComponent(); if (meshRenderer != null) { meshRenderer.enabled = true; } textMesh.fontSize = (int)textSize; fixedCount++; } } } // Disable face camera and distance culling for future objects faceCamera = false; maxViewDistance = 0f; Debug.Log($"Fixed visibility settings for {fixedCount} text objects"); Debug.Log($"- Removed distance-based visibility components"); Debug.Log($"- Removed LookAtCamera components"); Debug.Log($"- Enabled mesh renderers"); Debug.Log($"- Set font size to {textSize}"); Debug.Log($"- Applied fixed rotation: {fixedRotation}"); } // Automatically apply visibility fixes at startup (no manual interaction needed) private void ApplyVisibilityFixesAtStartup() { if (debugMode) { Debug.Log("WorldSpaceLocationNames: Applying automatic visibility fixes at startup..."); } int fixedCount = 0; foreach (var nameObj in nameObjects) { if (nameObj != null) { // Remove distance-based visibility component DistanceBasedVisibility distanceVis = nameObj.GetComponent(); if (distanceVis != null) { DestroyImmediate(distanceVis); } // Remove LookAtCamera component LookAtCamera lookAt = nameObj.GetComponent(); if (lookAt != null) { DestroyImmediate(lookAt); } // Apply fixed rotation nameObj.transform.rotation = Quaternion.Euler(fixedRotation); // Fix TextMeshPro settings TextMeshPro tmp = nameObj.GetComponent(); if (tmp != null) { // Ensure renderer is enabled if (tmp.renderer != null) { tmp.renderer.enabled = true; // Set sorting layer to render above roads tmp.renderer.sortingLayerName = sortingLayerName; tmp.renderer.sortingOrder = sortingOrder; } // FORCE the font size to be large - this was the main issue tmp.fontSize = Mathf.Max(textSize, 72f); // Ensure minimum 72pt size tmp.autoSizeTextContainer = false; tmp.rectTransform.sizeDelta = new Vector2(200, 50); // Force mesh update tmp.ForceMeshUpdate(); if (debugMode) { Debug.Log($" - Fixed text '{tmp.text}': fontSize={tmp.fontSize}, renderer enabled={tmp.renderer.enabled}"); } fixedCount++; } // Fix Unity TextMesh if used instead TextMesh textMesh = nameObj.GetComponent(); if (textMesh != null) { MeshRenderer meshRenderer = nameObj.GetComponent(); if (meshRenderer != null) { meshRenderer.enabled = true; // Set sorting layer to render above roads meshRenderer.sortingLayerName = sortingLayerName; meshRenderer.sortingOrder = sortingOrder; } textMesh.fontSize = (int)Mathf.Max(textSize, 72f); fixedCount++; } } } // Ensure settings are correct for future objects faceCamera = false; maxViewDistance = 0f; if (debugMode) { Debug.Log($"WorldSpaceLocationNames: Automatically fixed {fixedCount} text objects at startup"); } } // Fix sorting order for existing text to render above roads [ContextMenu("Fix Text Sorting Order")] public void FixTextSortingOrder() { int fixedCount = 0; foreach (var nameObj in nameObjects) { if (nameObj != null) { // Fix TextMeshPro sorting TextMeshPro tmp = nameObj.GetComponent(); if (tmp != null && tmp.renderer != null) { tmp.renderer.sortingLayerName = sortingLayerName; tmp.renderer.sortingOrder = sortingOrder; fixedCount++; } // Fix Unity TextMesh sorting TextMesh textMesh = nameObj.GetComponent(); if (textMesh != null) { MeshRenderer meshRenderer = nameObj.GetComponent(); if (meshRenderer != null) { meshRenderer.sortingLayerName = sortingLayerName; meshRenderer.sortingOrder = sortingOrder; fixedCount++; } } } } Debug.Log($"Fixed sorting order for {fixedCount} text objects"); Debug.Log($"- Sorting Layer: {sortingLayerName}"); Debug.Log($"- Sorting Order: {sortingOrder} (roads are 1, so text should be 2+)"); } } // Helper component to make text face the camera public class LookAtCamera : MonoBehaviour { public Camera targetCamera; private void Update() { if (targetCamera != null) { // Make the text face the camera without flipping Vector3 directionToCamera = targetCamera.transform.position - transform.position; directionToCamera.y = 0; // Keep text upright by ignoring Y rotation if (directionToCamera != Vector3.zero) { transform.rotation = Quaternion.LookRotation(-directionToCamera, Vector3.up); } } } } // Helper component for distance-based visibility public class DistanceBasedVisibility : MonoBehaviour { public Camera targetCamera; public float maxDistance = 50f; private Renderer textRenderer; private void Start() { textRenderer = GetComponent(); } private void Update() { if (targetCamera != null && textRenderer != null) { float distance = Vector3.Distance(transform.position, targetCamera.transform.position); textRenderer.enabled = distance <= maxDistance; } } }