using UnityEngine; using UnityEngine.UI; public class CrystalMineCapture : MonoBehaviour { [Header("争夺进度")] [Range(0f, 100f)] public float blueProgress = 50f; [Min(0f)] public float capturePerDamage = 1f; [Tooltip("是否自动添加中立阵营(teamId=2)。拆家版关闭以避免单位自动锁敌水晶矿")] public bool autoSetNeutralTeam = true; [Header("资源产出")] [Tooltip("占领后是否按固定间隔给所属阵营增加能量")] public bool producesMana = true; [Min(1f)] public float manaInterval = 8f; [Min(1)] public int manaAmount = 1; // [SerializeField] private AdjustableGlowOrb glowOrb; // 已删除,视觉效果脚本未使用 // [Tooltip("混沌裂缝传送门(随机出兵点),随争夺进度同步变色,可留空")] // [SerializeField] private RiftPortalVisual riftVisual; // 已删除,视觉效果脚本未使用 [SerializeField] private Color neutralColor = Color.white; [SerializeField] private Color blueColor = new Color(0.1f, 0.65f, 1f, 1f); [SerializeField] private Color redColor = new Color(1f, 0.12f, 0.08f, 1f); [Header("世界血条")] [Tooltip("血条相对模型渲染包围盒顶部的额外高度")] [Min(0f)] public float heightOffset = 1.1f; [Min(0.1f)] public float barWidth = 5f; private float? barHeightCache; private Canvas canvas; private RectTransform barRect; private Image blueFill; private Image redFill; private RectTransform marker; private UnitTeam unitTeam; private float manaTimer; private static Sprite roundedSprite; private static readonly int BaseColorId = Shader.PropertyToID("_Color"); private void Awake() { if (autoSetNeutralTeam) { unitTeam = GetComponent(); if (unitTeam == null) { unitTeam = gameObject.AddComponent(); } unitTeam.teamId = 2; } else { unitTeam = GetComponent(); } // if (glowOrb == null) // { // glowOrb = GetComponentInChildren(true); // } // if (riftVisual == null) // { // riftVisual = GetComponentInChildren(true); // } CreateCaptureBar(); RefreshVisuals(); } private void Start() { // 强制重置光球颜色为初始状态 // if (glowOrb != null) // { // glowOrb.SetGlowColor(neutralColor); // } RefreshVisuals(); } private void LateUpdate() { UpdateManaProduction(); if (barRect == null) return; barRect.position = transform.position + Vector3.up * GetBarHeight(); barRect.localScale = Vector3.one * (barWidth / 100f); Camera camera = Camera.main; if (camera != null) { barRect.rotation = Quaternion.LookRotation(camera.transform.position - barRect.position, camera.transform.up); } } // 血条高度 = 所有子 Renderer 包围盒顶部 + heightOffset。 // 水晶矿模型根节点是 100 倍缩放,高度必须按世界包围盒算,否则血条会埋进模型内部被遮挡。 private float GetBarHeight() { if (barHeightCache.HasValue) return barHeightCache.Value; float top = 0f; Renderer[] renderers = GetComponentsInChildren(); if (renderers.Length > 0) { Bounds bounds = renderers[0].bounds; for (int i = 1; i < renderers.Length; i++) { bounds.Encapsulate(renderers[i].bounds); } top = bounds.max.y; } barHeightCache = top - transform.position.y + heightOffset; return barHeightCache.Value; } private void UpdateManaProduction() { if (!producesMana || unitTeam == null || (unitTeam.teamId != 0 && unitTeam.teamId != 1)) { manaTimer = 0f; return; } manaTimer += Time.deltaTime; while (manaTimer >= manaInterval) { manaTimer -= manaInterval; if (unitTeam.teamId == 0) { ManaSystem.Instance?.AddMana(manaAmount); } else { EnemyCastleAI enemyAI = FindObjectOfType(); enemyAI?.AddMana(manaAmount); } } } public bool ReceiveCaptureDamage(float damage, GameObject attacker) { if (damage <= 0f || attacker == null) return false; UnitTeam attackerTeam = attacker.GetComponentInParent(); if (attackerTeam == null) return false; float direction = attackerTeam.teamId == 0 ? 1f : -1f; blueProgress = Mathf.Clamp(blueProgress + direction * damage * capturePerDamage, 0f, 100f); RefreshVisuals(); return true; } public void SetCaptureProgress(float value) { blueProgress = Mathf.Clamp(value, 0f, 100f); RefreshVisuals(); } private void RefreshVisuals() { if (blueFill == null || redFill == null) return; float blue = blueProgress / 100f; blueFill.fillAmount = blue; redFill.fillAmount = 1f - blue; marker.anchorMin = new Vector2(blue, 0f); marker.anchorMax = new Vector2(blue, 1f); marker.anchoredPosition = Vector2.zero; marker.gameObject.SetActive(blueProgress > 0f && blueProgress < 100f); if (unitTeam != null) { unitTeam.teamId = blueProgress >= 100f ? 0 : blueProgress <= 0f ? 1 : 2; } // if (glowOrb != null) // { // Color color = blueProgress >= 100f ? blueColor : blueProgress <= 0f ? redColor : neutralColor; // glowOrb.SetGlowColor(color); // } // if (riftVisual != null) // { // Color riftColor = blueProgress >= 100f ? blueColor : blueProgress <= 0f ? redColor : neutralColor; // riftVisual.SetRiftColor(riftColor); // } } private void CreateCaptureBar() { GameObject root = new GameObject("CrystalCaptureBar", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler)); // 血条 Canvas 不要作为水晶矿的子对象,直接放在场景根目录避免被 100 倍缩放影响 root.transform.parent = null; canvas = root.GetComponent(); canvas.renderMode = RenderMode.WorldSpace; canvas.sortingOrder = 110; root.GetComponent().dynamicPixelsPerUnit = 10f; barRect = root.GetComponent(); barRect.sizeDelta = new Vector2(100f, 16f); barRect.localScale = Vector3.one * (barWidth / 100f); RectTransform row = CreateRow(root.transform, "CaptureBar", 100f, 14f); Image background = CreateImage(row, "Background", new Color(0.03f, 0.03f, 0.03f, 0.95f)); SetFullRect(background.rectTransform); blueFill = CreateImage(row, "BlueFill", blueColor); blueFill.type = Image.Type.Filled; blueFill.fillMethod = Image.FillMethod.Horizontal; blueFill.fillOrigin = 0; blueFill.rectTransform.anchorMin = new Vector2(0f, 0f); blueFill.rectTransform.anchorMax = new Vector2(1f, 1f); blueFill.rectTransform.offsetMin = new Vector2(2f, 2f); blueFill.rectTransform.offsetMax = new Vector2(-2f, -2f); redFill = CreateImage(row, "RedFill", redColor); redFill.type = Image.Type.Filled; redFill.fillMethod = Image.FillMethod.Horizontal; redFill.fillOrigin = 1; redFill.rectTransform.anchorMin = new Vector2(0f, 0f); redFill.rectTransform.anchorMax = new Vector2(1f, 1f); redFill.rectTransform.offsetMin = new Vector2(2f, 2f); redFill.rectTransform.offsetMax = new Vector2(-2f, -2f); GameObject markerObject = new GameObject("CenterMarker", typeof(RectTransform), typeof(Image)); markerObject.transform.SetParent(row, false); marker = markerObject.GetComponent(); marker.sizeDelta = new Vector2(2f, 14f); marker.anchorMin = new Vector2(0.5f, 0f); marker.anchorMax = new Vector2(0.5f, 1f); marker.GetComponent().color = Color.white; } private RectTransform CreateRow(Transform parent, string name, float width, float height) { GameObject rowObject = new GameObject(name, typeof(RectTransform)); rowObject.transform.SetParent(parent, false); RectTransform rect = rowObject.GetComponent(); rect.anchorMin = new Vector2(0.5f, 0.5f); rect.anchorMax = new Vector2(0.5f, 0.5f); rect.pivot = new Vector2(0.5f, 0.5f); rect.sizeDelta = new Vector2(width, height); return rect; } private Image CreateImage(Transform parent, string name, Color color) { GameObject imageObject = new GameObject(name, typeof(RectTransform), typeof(Image)); imageObject.transform.SetParent(parent, false); Image image = imageObject.GetComponent(); image.sprite = RoundedSprite(); image.color = color; return image; } private void SetFullRect(RectTransform rect) { rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; } private static Sprite RoundedSprite() { if (roundedSprite != null) return roundedSprite; Texture2D texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); texture.SetPixel(0, 0, Color.white); texture.Apply(); roundedSprite = Sprite.Create(texture, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f)); roundedSprite.name = "CrystalCaptureBarSprite"; return roundedSprite; } }