AgentGroupVisual.cs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. using UnityEngine;
  2. /// <summary>
  3. /// Attached to the group leader's GameObject.
  4. /// Scales the leader's sphere to reflect group size and renders a floating
  5. /// count label above it using a World-Space Canvas + TextMesh.
  6. /// Follower agents are hidden (renderer disabled) while in the group.
  7. /// </summary>
  8. [RequireComponent(typeof(MeshRenderer))]
  9. public class AgentGroupVisual : MonoBehaviour
  10. {
  11. private AgentGroup group;
  12. private MeshRenderer meshRenderer;
  13. private GameObject labelObject;
  14. private TextMesh countLabel;
  15. // Base scale of a solo agent is 0.5 (set in CreateDefaultAgentPrefab)
  16. private const float BASE_SCALE = 0.5f;
  17. // Each additional member adds 10% to radius (so group of 9 is ~1.4× base)
  18. private const float SCALE_GROWTH = 0.1f;
  19. private static readonly Color SOLO_COLOR = Color.cyan;
  20. private static readonly Color GROUP_COLOR = new Color(1f, 0.55f, 0f); // orange
  21. public void Init(AgentGroup g)
  22. {
  23. group = g;
  24. meshRenderer = GetComponent<MeshRenderer>();
  25. CreateLabel();
  26. Refresh();
  27. }
  28. public void Refresh()
  29. {
  30. if (group == null) return;
  31. int size = group.Size;
  32. // Scale sphere
  33. float scale = BASE_SCALE + BASE_SCALE * SCALE_GROWTH * (size - 1);
  34. transform.localScale = Vector3.one * scale;
  35. // Color: solo = cyan, grouped = orange, larger group = brighter orange
  36. meshRenderer.material.color = size > 1 ? GROUP_COLOR : SOLO_COLOR;
  37. // Count label – only show for groups of 2+
  38. if (labelObject != null)
  39. {
  40. labelObject.SetActive(size > 1);
  41. if (countLabel != null) countLabel.text = size.ToString();
  42. }
  43. // Hide/show follower renderers
  44. foreach (var member in group.Members)
  45. {
  46. if (member == group.Leader) continue;
  47. var mr = member.GetComponent<MeshRenderer>();
  48. if (mr != null) mr.enabled = false;
  49. var lr = member.GetComponent<LineRenderer>();
  50. if (lr != null) lr.enabled = false;
  51. }
  52. }
  53. private void CreateLabel()
  54. {
  55. labelObject = new GameObject("GroupCountLabel");
  56. labelObject.transform.SetParent(transform, false);
  57. // Float above the sphere
  58. labelObject.transform.localPosition = new Vector3(0f, 0.8f, 0f);
  59. countLabel = labelObject.AddComponent<TextMesh>();
  60. countLabel.text = "1";
  61. countLabel.fontSize = 48;
  62. countLabel.alignment = TextAlignment.Center;
  63. countLabel.anchor = TextAnchor.MiddleCenter;
  64. countLabel.color = Color.white;
  65. countLabel.characterSize = 0.05f;
  66. // Always face camera
  67. labelObject.AddComponent<BillboardLabel>();
  68. }
  69. void OnDestroy()
  70. {
  71. if (labelObject != null) Destroy(labelObject);
  72. // Restore solo scale/color if visual is removed (member left group)
  73. transform.localScale = Vector3.one * BASE_SCALE;
  74. if (meshRenderer != null) meshRenderer.material.color = SOLO_COLOR;
  75. }
  76. }
  77. /// <summary>Keeps the TextMesh facing the main camera.</summary>
  78. public class BillboardLabel : MonoBehaviour
  79. {
  80. void LateUpdate()
  81. {
  82. if (Camera.main == null) return;
  83. transform.forward = Camera.main.transform.forward;
  84. }
  85. }