278 lines
9.8 KiB
C#
278 lines
9.8 KiB
C#
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<UnitTeam>();
|
|
if (unitTeam == null)
|
|
{
|
|
unitTeam = gameObject.AddComponent<UnitTeam>();
|
|
}
|
|
unitTeam.teamId = 2;
|
|
}
|
|
else
|
|
{
|
|
unitTeam = GetComponent<UnitTeam>();
|
|
}
|
|
|
|
// if (glowOrb == null)
|
|
// {
|
|
// glowOrb = GetComponentInChildren<AdjustableGlowOrb>(true);
|
|
// }
|
|
|
|
// if (riftVisual == null)
|
|
// {
|
|
// riftVisual = GetComponentInChildren<RiftPortalVisual>(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<Renderer>();
|
|
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<EnemyCastleAI>();
|
|
enemyAI?.AddMana(manaAmount);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ReceiveCaptureDamage(float damage, GameObject attacker)
|
|
{
|
|
if (damage <= 0f || attacker == null) return false;
|
|
|
|
UnitTeam attackerTeam = attacker.GetComponentInParent<UnitTeam>();
|
|
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>();
|
|
canvas.renderMode = RenderMode.WorldSpace;
|
|
canvas.sortingOrder = 110;
|
|
root.GetComponent<CanvasScaler>().dynamicPixelsPerUnit = 10f;
|
|
barRect = root.GetComponent<RectTransform>();
|
|
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<RectTransform>();
|
|
marker.sizeDelta = new Vector2(2f, 14f);
|
|
marker.anchorMin = new Vector2(0.5f, 0f);
|
|
marker.anchorMax = new Vector2(0.5f, 1f);
|
|
marker.GetComponent<Image>().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<RectTransform>();
|
|
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>();
|
|
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;
|
|
}
|
|
}
|