feat(WorldHealthBar): 添加技能能量系统并重构血条UI布局

- 新增技能能量相关属性(maxEnergy、currentEnergy)
- 添加能量条UI组件和布局管理
- 实现AddEnergy和ResetEnergy方法支持能量管理
- 调整血条整体布局以容纳能量条
- 移除已废弃的EnemyBaseMove脚本及其元文件
```
This commit is contained in:
huadexuan committed 2026-08-26 18:15:03 +08:00
1 parent 98176721d0
commit e532a6c8ce
24 files changed
+7525 -6

No files matched your search

+116 -5
View File
@@ -9,6 +9,13 @@ public class WorldHealthBar : MonoBehaviour
// 当前生命值,范围会自动限制在 0 到 maxHealth 之间。
[Min(0f)] public float currentHealth = 100f;
// 技能能量上限,所有单位默认使用100点。
[Header("技能能量")]
[Min(1f)] public float maxEnergy = 100f;
// 当前技能能量,范围会自动限制在 0 到 maxEnergy 之间。
[Min(0f)] public float currentEnergy = 0f;
// 血条相对于单位头顶的额外高度。
[Min(0f)] public float heightOffset = 0.25f;
@@ -28,6 +35,12 @@ public class WorldHealthBar : MonoBehaviour
private const int PixelWidth = 100;
private const int PixelHeight = 14;
// 能量条紧贴血条下方,宽度更窄,高度更小。
private const int EnergyPixelWidth = 80;
private const int EnergyPixelHeight = 8;
private const int BarGap = 2;
private const int TotalPixelHeight = PixelHeight + BarGap + EnergyPixelHeight;
// 世界空间 Canvas,用来承载血条 UI。
private Canvas canvas;
@@ -37,6 +50,9 @@ public class WorldHealthBar : MonoBehaviour
// 绿色血量填充 Image。
private Image fillImage;
// 金色技能能量填充 Image。
private Image energyFillImage;
// 血条相对于单位的本地位置。
private Vector3 localBarPosition;
@@ -54,16 +70,19 @@ public class WorldHealthBar : MonoBehaviour
{
maxHealth = Mathf.Max(1f, maxHealth);
currentHealth = Mathf.Clamp(currentHealth, 0f, maxHealth);
maxEnergy = Mathf.Max(1f, maxEnergy);
currentEnergy = Mathf.Clamp(currentEnergy, 0f, maxEnergy);
CalculateLayout();
CreateBar();
UpdateFill();
UpdateEnergyFill();
}
// 每帧让血条保持在单位头顶,并朝向主摄像机。
private void LateUpdate()
{
if (barRect == null) return;
barRect.localPosition = localBarPosition;
barRect.localPosition = GetBarRootPosition();
// 优先使用场景中名称完全为 Main Camera 的摄像机。
Camera camera = FindMainCamera();
@@ -121,6 +140,32 @@ public class WorldHealthBar : MonoBehaviour
// 让单位恢复生命值。
public void Heal(float amount) => SetHealth(currentHealth + amount);
// 增加技能能量,并返回本次增加后是否达到满能量。
public bool AddEnergy(float amount)
{
if (isDead || amount <= 0f)
{
return false;
}
float previousEnergy = currentEnergy;
currentEnergy = Mathf.Clamp(currentEnergy + amount, 0f, maxEnergy);
UpdateEnergyFill();
return previousEnergy < maxEnergy && currentEnergy >= maxEnergy;
}
// 清空技能能量并刷新能量条。
public void ResetEnergy()
{
if (isDead)
{
return;
}
currentEnergy = 0f;
UpdateEnergyFill();
}
// 处理单位死亡:隐藏模型和血条,播放蓝色向上爆散特效。
private void Die()
{
@@ -284,14 +329,15 @@ public class WorldHealthBar : MonoBehaviour
canvas.sortingOrder = 100;
barObject.GetComponent<CanvasScaler>().dynamicPixelsPerUnit = 10f;
barRect = barObject.GetComponent<RectTransform>();
barRect.sizeDelta = new Vector2(PixelWidth, PixelHeight);
barRect.sizeDelta = new Vector2(PixelWidth, TotalPixelHeight);
barRect.localScale = Vector3.one * (worldWidth / PixelWidth);
barRect.localPosition = localBarPosition;
barRect.localPosition = GetBarRootPosition();
Image background = CreateImage("Border", barObject.transform, RoundedSprite(), borderColor);
RectTransform healthRow = CreateRow(barObject.transform, "HealthBar", PixelWidth, PixelHeight, 0);
Image background = CreateImage("Border", healthRow, RoundedSprite(), borderColor);
SetFullRect(background.rectTransform);
Image inner = CreateImage("Fill", barObject.transform, RoundedSprite(), GetHealthFillColor());
Image inner = CreateImage("Fill", healthRow, RoundedSprite(), GetHealthFillColor());
inner.rectTransform.anchorMin = Vector2.zero;
inner.rectTransform.anchorMax = Vector2.one;
inner.rectTransform.offsetMin = new Vector2(2f, 2f);
@@ -300,6 +346,57 @@ public class WorldHealthBar : MonoBehaviour
inner.fillMethod = Image.FillMethod.Horizontal;
inner.fillOrigin = 0;
fillImage = inner;
RectTransform energyRow = CreateRow(
barObject.transform,
"EnergyBar",
EnergyPixelWidth,
EnergyPixelHeight,
PixelHeight + BarGap);
Image energyBackground = CreateImage("EnergyBorder", energyRow, RoundedSprite(), borderColor);
SetFullRect(energyBackground.rectTransform);
Image energyInner = CreateImage(
"EnergyFill",
energyRow,
RoundedSprite(),
GetEnergyFillColor());
energyInner.rectTransform.anchorMin = Vector2.zero;
energyInner.rectTransform.anchorMax = Vector2.one;
energyInner.rectTransform.offsetMin = new Vector2(2f, 1f);
energyInner.rectTransform.offsetMax = new Vector2(-2f, -1f);
energyInner.type = Image.Type.Filled;
energyInner.fillMethod = Image.FillMethod.Horizontal;
energyInner.fillOrigin = 0;
energyFillImage = energyInner;
}
// 返回整体 Canvas 的位置,使血条中心保持原来的高度。
private Vector3 GetBarRootPosition()
{
float pixelToWorld = worldWidth / PixelWidth;
float energyOffset = (TotalPixelHeight - PixelHeight) * 0.5f * pixelToWorld;
return localBarPosition - Vector3.up * energyOffset;
}
// 创建一行固定像素高度的世界空间 UI。
private static RectTransform CreateRow(
Transform parent,
string name,
int width,
int height,
int topOffset)
{
GameObject rowObject = new GameObject(name, typeof(RectTransform));
rowObject.transform.SetParent(parent, false);
RectTransform rowRect = rowObject.GetComponent<RectTransform>();
rowRect.anchorMin = new Vector2(0.5f, 1f);
rowRect.anchorMax = new Vector2(0.5f, 1f);
rowRect.pivot = new Vector2(0.5f, 1f);
rowRect.sizeDelta = new Vector2(width, height);
rowRect.anchoredPosition = new Vector2(0f, -topOffset);
return rowRect;
}
private Color GetHealthFillColor()
@@ -309,6 +406,11 @@ public class WorldHealthBar : MonoBehaviour
: new Color(0.1f, 0.8f, 0.18f, 1f);
}
private Color GetEnergyFillColor()
{
return new Color(1f, 0.72f, 0.08f, 1f);
}
// 创建一个用于边框或填充的 UI Image。
private static Image CreateImage(string name, Transform parent, Sprite sprite, Color color)
{
@@ -338,6 +440,15 @@ public class WorldHealthBar : MonoBehaviour
}
}
// 根据当前技能能量更新金色填充比例。
private void UpdateEnergyFill()
{
if (energyFillImage != null)
{
energyFillImage.fillAmount = Mathf.Clamp01(currentEnergy / maxEnergy);
}
}
// 创建并缓存一个带圆角的白色 Sprite,供边框和填充复用。
private static Sprite RoundedSprite()
{