feat(gameplay): 实现护盾系统和强化增益选择机制

- 新增护盾功能,包括护盾数值显示和护盾条UI
- 重构盾卫技能,实现坚固壁垒技能效果
- 添加暴击率、吸血率等战斗属性
- 优化增益选择界面,新增传奇品质和刷新机制
- 调整材质纹理和文字着色器效果
- 修复金币显示格式问题
```
This commit is contained in:
huadexuan committed 2026-10-09 18:13:18 +08:00
1 parent 336ec9980d
commit 48395ab414
27 files changed
+10343 -128

No files matched your search

@@ -45,9 +45,10 @@ public class BuffSelectionController : MonoBehaviour
public enum BuffQuality
{
Common, // 初级 (白色 55%)
Rare, // 中级 (紫色 35%)
Epic // 高级 (金色 10%)
Common, // 初级 (白色 85%)
Rare, // 中级 (紫色 10%)
Epic, // 高级 (金色 5%)
Legendary // 顶级 (红色/酒红 预留)
}
public class BuffOption
@@ -68,18 +69,24 @@ public class BuffSelectionController : MonoBehaviour
[Header("UI 节点引用")]
public GameObject buffSelectionWindow;
public Transform cardsContainer;
public Button rollButton; // 刷新按钮
public int rollCost = 2; // 每次刷新消耗金币
[Header("品质边框 Sprites")]
public Sprite commonBorderSprite; // 铜 (初级)
public Sprite rareBorderSprite; // 银 (中级)
public Sprite epicBorderSprite; // 金 (高级)
public Sprite legendaryBorderSprite; // 红 (最高级)
public Sprite legendaryBorderSprite; // 红 (顶级)
[Header("头像 Sprites")]
public Sprite allIconSprite; // 全体/全局头像 (all.png)
private bool isWindowShowing = false;
// 3 个卡槽各自独立追踪的品质与递进未升阶计数
private BuffQuality[] slotQualities = new BuffQuality[3];
private int[] slotNonUpgradeCounts = new int[3];
private void Awake()
{
if (_instance == null)
@@ -102,15 +109,111 @@ public class BuffSelectionController : MonoBehaviour
return;
}
// 生成 3 个随机卡片
List<BuffOption> options = GenerateThreeOptions();
// 初始弹窗时重置 3 个卡槽的累积计数,并按初始基础概率独立抽品质 (初级 85% / 中级 10% / 高级 5%)
for (int i = 0; i < 3; i++)
{
slotNonUpgradeCounts[i] = 0;
float roll = Random.value;
if (roll < 0.85f)
{
slotQualities[i] = BuffQuality.Common;
}
else if (roll < 0.95f)
{
slotQualities[i] = BuffQuality.Rare;
}
else
{
slotQualities[i] = BuffQuality.Epic;
}
}
// 生成 3 个卡槽的增益选项
List<BuffOption> options = GenerateOptionsForSlots();
RenderOptionsUI(options);
// 绑定刷新按钮事件
if (rollButton != null)
{
rollButton.onClick.RemoveAllListeners();
rollButton.onClick.AddListener(OnRollButtonClicked);
}
buffSelectionWindow.SetActive(true);
isWindowShowing = true;
Time.timeScale = 0f; // 暂停游戏让玩家做选择
}
/// <summary>
/// 点击刷新按钮 (耗费 2 金币),3 个卡槽独立计算升阶概率并重新生成选项
/// </summary>
private void OnRollButtonClicked()
{
if (GoldManager.Instance != null && !GoldManager.Instance.TrySpendGold(rollCost))
{
Debug.LogWarning($"金币不足 {rollCost},无法刷新!");
return;
}
// 3 个卡槽独立判定升级逻辑
for (int i = 0; i < 3; i++)
{
BuffQuality currentQ = slotQualities[i];
bool upgraded = false;
BuffQuality targetQ = currentQ;
if (currentQ == BuffQuality.Common)
{
// 初级 升 中级 10% 每次 +10% (第2次20%、第3次30%...)
float chance = 0.10f + slotNonUpgradeCounts[i] * 0.10f;
if (Random.value < chance)
{
targetQ = BuffQuality.Rare;
upgraded = true;
}
}
else if (currentQ == BuffQuality.Rare)
{
// 中级 升 高级 3% 每次 +5% (第2次8%、第3次13%...)
float chance = 0.03f + slotNonUpgradeCounts[i] * 0.05f;
if (Random.value < chance)
{
targetQ = BuffQuality.Epic;
upgraded = true;
}
}
else if (currentQ == BuffQuality.Epic)
{
// 高级 升 顶级 1% (预留) 每次 +2%
float chance = 0.01f + slotNonUpgradeCounts[i] * 0.02f;
if (Random.value < chance)
{
targetQ = BuffQuality.Legendary;
upgraded = true;
}
}
if (upgraded)
{
slotQualities[i] = targetQ;
slotNonUpgradeCounts[i] = 0; // 刷出升阶,该槽位的追加概率重置
}
else
{
if (currentQ != BuffQuality.Legendary)
{
slotNonUpgradeCounts[i]++; // 未升阶,追加概率增加
}
}
}
Debug.Log($"<color=yellow>玩家消耗 {rollCost} 金币刷新卡牌!</color> 当前槽位品质: [{slotQualities[0]}], [{slotQualities[1]}], [{slotQualities[2]}]");
// 重新渲染 3 个卡槽
List<BuffOption> newOptions = GenerateOptionsForSlots();
RenderOptionsUI(newOptions);
}
private void RenderOptionsUI(List<BuffOption> options)
{
int cardCount = cardsContainer.childCount;
@@ -120,7 +223,7 @@ public class BuffSelectionController : MonoBehaviour
if (i < options.Count)
{
card.gameObject.SetActive(true);
SetupCardUI(card, options[i]);
SetupCardUI(card, options[i], i);
}
else
{
@@ -129,10 +232,32 @@ public class BuffSelectionController : MonoBehaviour
}
}
private void SetupCardUI(Transform cardTransform, BuffOption option)
private float GetNextUpgradeChance(int slotIndex)
{
if (slotIndex < 0 || slotIndex >= 3) return 0f;
BuffQuality currentQ = slotQualities[slotIndex];
int count = slotNonUpgradeCounts[slotIndex];
if (currentQ == BuffQuality.Common)
{
return 0.10f + count * 0.10f; // 10% + n * 10%
}
else if (currentQ == BuffQuality.Rare)
{
return 0.03f + count * 0.05f; // 3% + n * 5%
}
else if (currentQ == BuffQuality.Epic)
{
return 0.01f + count * 0.02f; // 1% + n * 2%
}
return 0f; // 顶级无升级
}
private void SetupCardUI(Transform cardTransform, BuffOption option, int slotIndex)
{
Text buffNameText = cardTransform.Find("BuffName")?.GetComponent<Text>();
Text descText = cardTransform.Find("Description")?.GetComponent<Text>();
Text upgradeRateText = cardTransform.Find("UpgradeRateText")?.GetComponent<Text>();
if (buffNameText != null)
{
@@ -145,6 +270,19 @@ public class BuffSelectionController : MonoBehaviour
descText.text = option.descText;
}
if (upgradeRateText != null)
{
if (option.quality == BuffQuality.Legendary)
{
upgradeRateText.text = "已达最高品质";
}
else
{
float chance = GetNextUpgradeChance(slotIndex);
upgradeRateText.text = $"升阶概率: {Mathf.Min(100f, chance * 100f):F0}%";
}
}
// 设置目标英雄 / 全体 Icon 头像
Image iconImage = cardTransform.Find("Icon")?.GetComponent<Image>();
if (iconImage != null && option.iconSprite != null)
@@ -191,6 +329,8 @@ public class BuffSelectionController : MonoBehaviour
return rareBorderSprite != null ? rareBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/银");
case BuffQuality.Epic:
return epicBorderSprite != null ? epicBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/金");
case BuffQuality.Legendary:
return legendaryBorderSprite != null ? legendaryBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/红");
default:
return commonBorderSprite;
}
@@ -335,7 +475,7 @@ public class BuffSelectionController : MonoBehaviour
}
}
private List<BuffOption> GenerateThreeOptions()
private List<BuffOption> GenerateOptionsForSlots()
{
List<BuffOption> list = new List<BuffOption>();
HashSet<BuffType> usedTypes = new HashSet<BuffType>();
@@ -346,7 +486,7 @@ public class BuffSelectionController : MonoBehaviour
int maxAttempts = 10;
do
{
opt = CreateRandomOption(i);
opt = CreateOptionForSlot(i, slotQualities[i]);
maxAttempts--;
} while (usedTypes.Contains(opt.type) && maxAttempts > 0);
@@ -370,42 +510,42 @@ public class BuffSelectionController : MonoBehaviour
BuffType.EmergencyHeal // 14. 紧急救急
};
private BuffOption CreateRandomOption(int seedOffset)
private BuffOption CreateOptionForSlot(int slotIndex, BuffQuality quality)
{
BuffOption opt = new BuffOption();
// 从当前开放的词条池中随机选择
opt.type = AvailableBuffTypes[Random.Range(0, AvailableBuffTypes.Length)];
opt.quality = quality;
// 随机品质 (初级55%, 中级35%, 高级10%)
float rollQuality = Random.value;
if (rollQuality < 0.55f)
switch (quality)
{
opt.quality = BuffQuality.Common;
opt.qualityName = "初级";
opt.qualityColor = new Color(0.85f, 0.85f, 0.85f); // 白色
}
else if (rollQuality < 0.90f)
{
opt.quality = BuffQuality.Rare;
opt.qualityName = "中级";
opt.qualityColor = new Color(0.7f, 0.35f, 0.95f); // 紫色
}
else
{
opt.quality = BuffQuality.Epic;
opt.qualityName = "高级";
opt.qualityColor = new Color(1f, 0.8f, 0.15f); // 金色
case BuffQuality.Common:
opt.qualityName = "初级";
opt.qualityColor = new Color(0.85f, 0.85f, 0.85f); // 白色
break;
case BuffQuality.Rare:
opt.qualityName = "中级";
opt.qualityColor = new Color(0.7f, 0.35f, 0.95f); // 紫色
break;
case BuffQuality.Epic:
opt.qualityName = "高级";
opt.qualityColor = new Color(1f, 0.8f, 0.15f); // 金色
break;
case BuffQuality.Legendary:
opt.qualityName = "顶级";
opt.qualityColor = new Color(1f, 0.25f, 0.25f); // 红色/酒红
break;
}
// 判断是否为全局属性
// 判断是否为全局属性 (20% 全体,80% 指定单体)
bool isGlobalOnly = (opt.type == BuffType.SummonerCD ||
opt.type == BuffType.SummonerPower ||
opt.type == BuffType.KillGold ||
opt.type == BuffType.GoldBonus);
List<UnitBase> activeHeroes = GetLocalPlayerHeroes();
bool isAll = isGlobalOnly || (Random.value < 0.5f) || (activeHeroes.Count == 0);
bool isAll = isGlobalOnly || (Random.value < 0.20f) || (activeHeroes.Count == 0);
string heroDisplayName = "";
if (isAll)
@@ -530,33 +670,33 @@ public class BuffSelectionController : MonoBehaviour
switch (type)
{
case BuffType.AttackPower:
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : 0.20f);
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : (quality == BuffQuality.Epic ? 0.20f : 0.32f));
case BuffType.MaxHealth:
return quality == BuffQuality.Common ? 0.10f : (quality == BuffQuality.Rare ? 0.18f : 0.30f);
return quality == BuffQuality.Common ? 0.10f : (quality == BuffQuality.Rare ? 0.18f : (quality == BuffQuality.Epic ? 0.30f : 0.45f));
case BuffType.AttackSpeed:
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.16f);
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : (quality == BuffQuality.Epic ? 0.16f : 0.25f));
case BuffType.CritRate:
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : 0.20f);
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : (quality == BuffQuality.Epic ? 0.20f : 0.30f));
case BuffType.Lifesteal:
return quality == BuffQuality.Common ? 0.04f : (quality == BuffQuality.Rare ? 0.08f : 0.14f);
return quality == BuffQuality.Common ? 0.04f : (quality == BuffQuality.Rare ? 0.08f : (quality == BuffQuality.Epic ? 0.14f : 0.20f));
case BuffType.SkillCharge:
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : 0.25f);
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : (quality == BuffQuality.Epic ? 0.25f : 0.40f));
case BuffType.SummonerCD:
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.18f);
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : (quality == BuffQuality.Epic ? 0.18f : 0.28f));
case BuffType.SummonerPower:
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : 0.25f);
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : (quality == BuffQuality.Epic ? 0.25f : 0.40f));
case BuffType.AttackRange:
return quality == BuffQuality.Common ? 0.5f : (quality == BuffQuality.Rare ? 1.0f : 1.8f);
return quality == BuffQuality.Common ? 0.5f : (quality == BuffQuality.Rare ? 1.0f : (quality == BuffQuality.Epic ? 1.8f : 2.5f));
case BuffType.DamageReduction:
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.16f);
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : (quality == BuffQuality.Epic ? 0.16f : 0.25f));
case BuffType.Shield:
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.25f : 0.40f);
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.25f : (quality == BuffQuality.Epic ? 0.40f : 0.60f));
case BuffType.KillGold:
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.30f : 0.50f);
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.30f : (quality == BuffQuality.Epic ? 0.50f : 0.80f));
case BuffType.GoldBonus:
return quality == BuffQuality.Common ? 30f : (quality == BuffQuality.Rare ? 60f : 100f);
return quality == BuffQuality.Common ? 30f : (quality == BuffQuality.Rare ? 60f : (quality == BuffQuality.Epic ? 100f : 160f));
case BuffType.EmergencyHeal:
return quality == BuffQuality.Common ? 0.25f : (quality == BuffQuality.Rare ? 0.40f : 0.60f);
return quality == BuffQuality.Common ? 0.25f : (quality == BuffQuality.Rare ? 0.40f : (quality == BuffQuality.Epic ? 0.60f : 0.80f));
default:
return 0.10f;
}
@@ -595,10 +735,19 @@ public class BuffSelectionController : MonoBehaviour
if (winTrans != null) buffSelectionWindow = winTrans.gameObject;
}
if (buffSelectionWindow != null && cardsContainer == null)
if (buffSelectionWindow != null)
{
var containerTrans = buffSelectionWindow.transform.Find("BuffPanel/BuffCardsContainer");
if (containerTrans != null) cardsContainer = containerTrans;
if (cardsContainer == null)
{
var containerTrans = buffSelectionWindow.transform.Find("BuffPanel/BuffCardsContainer");
if (containerTrans != null) cardsContainer = containerTrans;
}
if (rollButton == null)
{
var rollTrans = buffSelectionWindow.transform.Find("BuffPanel/RollButton");
if (rollTrans != null) rollButton = rollTrans.GetComponent<Button>();
}
}
}
}