Files
daluandou/Assets/Scripts/系统脚本/BuffSelectionController.cs
T
huadexuan 23c559d9f7 ```
feat(gameplay): 提升单位检测范围并添加法师属性

- 将所有单位的检测范围从默认值提升至100
- 为法师预制体添加护盾、等级等新属性
- 为法师添加暴击、吸血、伤害减免等战斗属性
- 更换法师弹丸预设并调整相关参数
- 添加锚点交战Z线控制功能
- 更新玩家数据中配备的召唤师技能
- 移除多余的纹理配置文件
```
2026-10-07 23:59:17 +08:00

605 lines
23 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BuffSelectionController : MonoBehaviour
{
private static BuffSelectionController _instance;
public static BuffSelectionController Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<BuffSelectionController>();
if (_instance == null)
{
var canvas = GameObject.Find("TowerDefenseCanvas");
var targetObj = canvas != null ? canvas : new GameObject("BuffSelectionControllerManager");
_instance = targetObj.AddComponent<BuffSelectionController>();
}
}
return _instance;
}
}
public static float KillGoldMultiplier = 1.0f; // 全局击杀金币收益倍率
public enum BuffType
{
AttackPower, // 1. 攻击力 (+6% / +12% / +20%)
MaxHealth, // 2. 最大生命值 (+10% / +18% / +30%)
AttackSpeed, // 3. 攻击速度 (+5% / +10% / +16%)
CritRate, // 4. 暴击率 (+6% / +12% / +20%)
Lifesteal, // 5. 通用吸血 (+4% / +8% / +14%)
SkillCharge, // 6. 技能充能 (+8% / +15% / +25%)
SummonerCD, // 7. 召唤师技能CD (-5% / -10% / -18%)
SummonerPower, // 8. 召唤师技能威力 (+8% / +15% / +25%)
AttackRange, // 9. 攻击射程 (+0.5米 / +1.0米 / +1.8米)
DamageReduction, // 10. 伤害减免 (+5% / +10% / +16%)
Shield, // 11. 护盾生成 (15% / 25% / 40% MaxHP)
KillGold, // 12. 击杀金币 (+15% / +30% / +50%)
GoldBonus, // 特殊 1: 招财进宝 (+30 / +60 / +100 金币)
EmergencyHeal // 特殊 2: 紧急救急 (恢复 25% / 40% / 60% HP)
}
public enum BuffQuality
{
Common, // 初级 (白色 55%)
Rare, // 中级 (紫色 35%)
Epic // 高级 (金色 10%)
}
public class BuffOption
{
public BuffType type;
public BuffQuality quality;
public int targetHeroIndex; // -1 表示【全体英雄】或【全局】,0~4 表示指定单体英雄
public string targetName;
public float rawValue; // 单体原始加成数值 (如 0.06f)
public float finalValue; // 计算 40% 折扣后的最终加成数值
public string titleText;
public string descText;
public Color qualityColor;
public string qualityName;
public Sprite iconSprite; // 英雄头像 / 全体头像
}
[Header("UI 节点引用")]
public GameObject buffSelectionWindow;
public Transform cardsContainer;
[Header("品质边框 Sprites")]
public Sprite commonBorderSprite; // 铜 (初级)
public Sprite rareBorderSprite; // 银 (中级)
public Sprite epicBorderSprite; // 金 (高级)
public Sprite legendaryBorderSprite; // 红 (最高级)
[Header("头像 Sprites")]
public Sprite allIconSprite; // 全体/全局头像 (all.png)
private bool isWindowShowing = false;
private void Awake()
{
if (_instance == null)
{
_instance = this;
}
}
/// <summary>
/// 触发 3 选 1 增益弹窗(自动暂停游戏)
/// </summary>
public void ShowBuffSelection()
{
if (isWindowShowing) return;
EnsureUIReferences();
if (buffSelectionWindow == null || cardsContainer == null)
{
Debug.LogWarning("BuffSelectionController: UI 节点缺失,无法弹出 3 选 1。");
return;
}
// 生成 3 个随机卡片
List<BuffOption> options = GenerateThreeOptions();
RenderOptionsUI(options);
buffSelectionWindow.SetActive(true);
isWindowShowing = true;
Time.timeScale = 0f; // 暂停游戏让玩家做选择
}
private void RenderOptionsUI(List<BuffOption> options)
{
int cardCount = cardsContainer.childCount;
for (int i = 0; i < cardCount; i++)
{
var card = cardsContainer.GetChild(i);
if (i < options.Count)
{
card.gameObject.SetActive(true);
SetupCardUI(card, options[i]);
}
else
{
card.gameObject.SetActive(false);
}
}
}
private void SetupCardUI(Transform cardTransform, BuffOption option)
{
Text buffNameText = cardTransform.Find("BuffName")?.GetComponent<Text>();
Text descText = cardTransform.Find("Description")?.GetComponent<Text>();
if (buffNameText != null)
{
buffNameText.text = $"[{option.qualityName}] {option.titleText}";
buffNameText.color = option.qualityColor;
}
if (descText != null)
{
descText.text = option.descText;
}
// 设置目标英雄 / 全体 Icon 头像
Image iconImage = cardTransform.Find("Icon")?.GetComponent<Image>();
if (iconImage != null && option.iconSprite != null)
{
iconImage.sprite = option.iconSprite;
iconImage.enabled = true;
}
// 设置品质边框 (仅更换 buffImage 节点的源图像,不修改缩放和位置)
Image borderImage = cardTransform.Find("buffImage")?.GetComponent<Image>();
if (borderImage != null)
{
Sprite borderSprite = GetQualityBorderSprite(option.quality);
if (borderSprite != null)
{
borderImage.sprite = borderSprite;
}
}
// 卡牌整体点击选定
Button selectBtn = cardTransform.GetComponent<Button>();
if (selectBtn == null)
{
selectBtn = cardTransform.gameObject.AddComponent<Button>();
}
selectBtn.onClick.RemoveAllListeners();
selectBtn.onClick.AddListener(() => OnOptionSelected(option));
}
private Sprite GetQualityBorderSprite(BuffQuality quality)
{
#if UNITY_EDITOR
if (commonBorderSprite == null) commonBorderSprite = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Picture/UI/增益边框/铜.png");
if (rareBorderSprite == null) rareBorderSprite = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Picture/UI/增益边框/银.png");
if (epicBorderSprite == null) epicBorderSprite = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Picture/UI/增益边框/金.png");
if (legendaryBorderSprite == null) legendaryBorderSprite = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Picture/UI/增益边框/红.png");
#endif
switch (quality)
{
case BuffQuality.Common:
return commonBorderSprite != null ? commonBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/铜");
case BuffQuality.Rare:
return rareBorderSprite != null ? rareBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/银");
case BuffQuality.Epic:
return epicBorderSprite != null ? epicBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/金");
default:
return commonBorderSprite;
}
}
private void OnOptionSelected(BuffOption option)
{
ApplyBuffToUnits(option);
if (buffSelectionWindow != null)
{
buffSelectionWindow.SetActive(false);
}
isWindowShowing = false;
Time.timeScale = 1f; // 恢复游戏正常播放
Debug.Log($"玩家选择了增益: [{option.qualityName}] {option.titleText} ({option.descText})");
}
/// <summary>
/// 将选中的增益应用到对应单位
/// </summary>
private void ApplyBuffToUnits(BuffOption option)
{
// 1. 全局与玩家技能词条处理
if (option.type == BuffType.SummonerCD)
{
if (PlayerSkillSystem.Instance != null)
{
PlayerSkillSystem.Instance.cooldownMultiplier *= (1f - option.finalValue);
Debug.Log($"召唤师技能 CD 缩减加成: -{option.finalValue * 100f:F1}%,当前 CD 倍率: {PlayerSkillSystem.Instance.cooldownMultiplier:F2}");
}
return;
}
if (option.type == BuffType.SummonerPower)
{
if (PlayerSkillSystem.Instance != null)
{
PlayerSkillSystem.Instance.powerMultiplier *= (1f + option.finalValue);
Debug.Log($"召唤师技能威力加成: +{option.finalValue * 100f:F1}%,当前威力倍率: {PlayerSkillSystem.Instance.powerMultiplier:F2}");
}
return;
}
if (option.type == BuffType.KillGold)
{
KillGoldMultiplier *= (1f + option.finalValue);
Debug.Log($"击杀金币加成: +{option.finalValue * 100f:F1}%,当前击杀金币倍率: {KillGoldMultiplier:F2}");
return;
}
if (option.type == BuffType.GoldBonus)
{
int goldAdd = Mathf.RoundToInt(option.finalValue);
if (GoldManager.Instance != null)
{
GoldManager.Instance.AddGold(goldAdd);
}
return;
}
// 2. 单位目标词条处理
var heroes = FindObjectsOfType<UnitBase>();
List<UnitBase> targetUnits = new List<UnitBase>();
foreach (var hero in heroes)
{
if (hero == null || !hero.gameObject.activeInHierarchy) continue;
UnitTeam team = hero.GetComponent<UnitTeam>();
if (team != null && !team.IsLocalPlayerTeam()) continue; // 只给己方英雄加
targetUnits.Add(hero);
}
if (option.targetHeroIndex >= 0 && option.targetHeroIndex < targetUnits.Count)
{
// 给单体英雄加成
ApplyBuffToOneUnit(targetUnits[option.targetHeroIndex], option);
}
else
{
// 给全体英雄加成 (已按 40% 折扣计算)
foreach (var unit in targetUnits)
{
ApplyBuffToOneUnit(unit, option);
}
}
}
private void ApplyBuffToOneUnit(UnitBase unit, BuffOption option)
{
if (unit == null) return;
switch (option.type)
{
case BuffType.AttackPower:
unit.attackPower *= (1f + option.finalValue);
break;
case BuffType.MaxHealth:
WorldHealthBar bar = unit.GetComponent<WorldHealthBar>();
if (bar != null)
{
float healthAdd = bar.maxHealth * option.finalValue;
bar.maxHealth += healthAdd;
bar.currentHealth += healthAdd; // 提升上限同时补血
}
break;
case BuffType.AttackSpeed:
unit.MultiplyAttackSpeed(1f + option.finalValue, 3f);
break;
case BuffType.CritRate:
unit.critRate += option.finalValue;
break;
case BuffType.Lifesteal:
unit.lifestealRate += option.finalValue;
break;
case BuffType.SkillCharge:
unit.skillChargeMultiplier *= (1f + option.finalValue);
break;
case BuffType.AttackRange:
unit.attackRange += option.finalValue;
break;
case BuffType.DamageReduction:
unit.damageReductionRate += option.finalValue;
break;
case BuffType.Shield:
WorldHealthBar shieldBar = unit.GetComponent<WorldHealthBar>();
if (shieldBar != null)
{
float shieldAdd = shieldBar.maxHealth * option.finalValue;
shieldBar.AddShield(shieldAdd);
}
break;
case BuffType.EmergencyHeal:
WorldHealthBar healBar = unit.GetComponent<WorldHealthBar>();
if (healBar != null)
{
healBar.Heal(healBar.maxHealth * option.finalValue);
}
break;
}
}
private List<BuffOption> GenerateThreeOptions()
{
List<BuffOption> list = new List<BuffOption>();
HashSet<BuffType> usedTypes = new HashSet<BuffType>();
for (int i = 0; i < 3; i++)
{
BuffOption opt;
int maxAttempts = 10;
do
{
opt = CreateRandomOption(i);
maxAttempts--;
} while (usedTypes.Contains(opt.type) && maxAttempts > 0);
usedTypes.Add(opt.type);
list.Add(opt);
}
return list;
}
// 开放抽取的词条池 (暂时屏蔽 7:SummonerCD, 8:SummonerPower, 9:AttackRange, 12:KillGold, 13:GoldBonus)
private static readonly BuffType[] AvailableBuffTypes = new BuffType[]
{
BuffType.AttackPower, // 1. 攻击力
BuffType.MaxHealth, // 2. 最大生命值
BuffType.AttackSpeed, // 3. 攻击速度
BuffType.CritRate, // 4. 暴击率
BuffType.Lifesteal, // 5. 通用吸血
BuffType.SkillCharge, // 6. 技能充能
BuffType.DamageReduction, // 10. 伤害减免
BuffType.Shield, // 11. 护盾生成
BuffType.EmergencyHeal // 14. 紧急救急
};
private BuffOption CreateRandomOption(int seedOffset)
{
BuffOption opt = new BuffOption();
// 从当前开放的词条池中随机选择
opt.type = AvailableBuffTypes[Random.Range(0, AvailableBuffTypes.Length)];
// 随机品质 (初级55%, 中级35%, 高级10%)
float rollQuality = Random.value;
if (rollQuality < 0.55f)
{
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); // 金色
}
// 判断是否为全局属性
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);
string heroDisplayName = "";
if (isAll)
{
opt.targetHeroIndex = -1;
opt.iconSprite = GetAllIconSprite();
}
else
{
opt.targetHeroIndex = Random.Range(0, activeHeroes.Count);
UnitBase hero = activeHeroes[opt.targetHeroIndex];
opt.iconSprite = GetHeroIconSprite(hero.characterDataName);
if (!string.IsNullOrEmpty(hero.characterDataName))
{
var data = Resources.Load<CharacterData>($"Data/{hero.characterDataName}");
heroDisplayName = data != null && !string.IsNullOrEmpty(data.characterName) ? data.characterName : hero.characterDataName.Replace("Data", "");
}
else
{
heroDisplayName = $"己方英雄 #{opt.targetHeroIndex + 1}";
}
}
// 计算数值
opt.rawValue = GetBaseValue(opt.type, opt.quality);
if (isAll && !isGlobalOnly)
{
opt.finalValue = opt.rawValue * 0.4f; // 40% 全体折扣法则
}
else
{
opt.finalValue = opt.rawValue;
}
// 生成属性名称与描述
string attrName = GetTypeName(opt.type);
string targetStr = isGlobalOnly ? "玩家/全局" : (isAll ? "全体英雄" : heroDisplayName);
opt.titleText = $"{targetStr} · {attrName}";
opt.descText = FormatValueDesc(opt.type, opt.finalValue);
return opt;
}
private List<UnitBase> GetLocalPlayerHeroes()
{
List<UnitBase> list = new List<UnitBase>();
var heroes = FindObjectsOfType<UnitBase>();
foreach (var hero in heroes)
{
if (hero == null || !hero.gameObject.activeInHierarchy) continue;
UnitTeam team = hero.GetComponent<UnitTeam>();
if (team != null && !team.IsLocalPlayerTeam()) continue;
list.Add(hero);
}
return list;
}
private Sprite GetAllIconSprite()
{
if (allIconSprite != null) return allIconSprite;
#if UNITY_EDITOR
allIconSprite = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Picture/角色头像/all.png");
if (allIconSprite != null) return allIconSprite;
#endif
return Resources.Load<Sprite>("Picture/角色头像/all");
}
private Sprite GetHeroIconSprite(string characterDataName)
{
if (!string.IsNullOrEmpty(characterDataName))
{
var data = Resources.Load<CharacterData>($"Data/{characterDataName}");
if (data != null && data.cardIcon != null)
{
return data.cardIcon;
}
string cleanName = characterDataName.Replace("Data", "").Trim();
if (cleanName == "狂暴狼人") cleanName = "荒野狼人";
if (cleanName == "盾卫") cleanName = "盾卫头像";
#if UNITY_EDITOR
var sprite = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>($"Assets/Picture/角色头像/{cleanName}.png");
if (sprite != null) return sprite;
#endif
}
return GetAllIconSprite();
}
private string FormatValueDesc(BuffType type, float value)
{
switch (type)
{
case BuffType.AttackPower:
case BuffType.MaxHealth:
case BuffType.AttackSpeed:
case BuffType.CritRate:
case BuffType.Lifesteal:
case BuffType.SkillCharge:
case BuffType.SummonerPower:
case BuffType.DamageReduction:
case BuffType.KillGold:
return $"{GetTypeName(type)} +{value * 100f:F1}%";
case BuffType.SummonerCD:
return $"{GetTypeName(type)} -{value * 100f:F1}%";
case BuffType.AttackRange:
return $"{GetTypeName(type)} +{value:F1} 米";
case BuffType.Shield:
return $"获得 {value * 100f:F0}% 最大生命值的护盾";
case BuffType.GoldBonus:
return $"立即获得 +{value:F0} 金币";
case BuffType.EmergencyHeal:
return $"恢复 {value * 100f:F0}% 最大生命值";
default:
return $"{GetTypeName(type)} +{value}";
}
}
private float GetBaseValue(BuffType type, BuffQuality quality)
{
switch (type)
{
case BuffType.AttackPower:
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : 0.20f);
case BuffType.MaxHealth:
return quality == BuffQuality.Common ? 0.10f : (quality == BuffQuality.Rare ? 0.18f : 0.30f);
case BuffType.AttackSpeed:
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.16f);
case BuffType.CritRate:
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : 0.20f);
case BuffType.Lifesteal:
return quality == BuffQuality.Common ? 0.04f : (quality == BuffQuality.Rare ? 0.08f : 0.14f);
case BuffType.SkillCharge:
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : 0.25f);
case BuffType.SummonerCD:
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.18f);
case BuffType.SummonerPower:
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : 0.25f);
case BuffType.AttackRange:
return quality == BuffQuality.Common ? 0.5f : (quality == BuffQuality.Rare ? 1.0f : 1.8f);
case BuffType.DamageReduction:
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.16f);
case BuffType.Shield:
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.25f : 0.40f);
case BuffType.KillGold:
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.30f : 0.50f);
case BuffType.GoldBonus:
return quality == BuffQuality.Common ? 30f : (quality == BuffQuality.Rare ? 60f : 100f);
case BuffType.EmergencyHeal:
return quality == BuffQuality.Common ? 0.25f : (quality == BuffQuality.Rare ? 0.40f : 0.60f);
default:
return 0.10f;
}
}
private string GetTypeName(BuffType type)
{
switch (type)
{
case BuffType.AttackPower: return "攻击力";
case BuffType.MaxHealth: return "最大生命值";
case BuffType.AttackSpeed: return "攻击速度";
case BuffType.CritRate: return "暴击率";
case BuffType.Lifesteal: return "通用吸血";
case BuffType.SkillCharge: return "技能充能";
case BuffType.SummonerCD: return "召唤师技能CD";
case BuffType.SummonerPower: return "召唤师技能威力";
case BuffType.AttackRange: return "攻击射程";
case BuffType.DamageReduction: return "伤害减免";
case BuffType.Shield: return "护盾生成";
case BuffType.KillGold: return "击杀金币加成";
case BuffType.GoldBonus: return "招财进宝";
case BuffType.EmergencyHeal: return "紧急救急";
default: return "属性";
}
}
private void EnsureUIReferences()
{
var canvas = GameObject.Find("TowerDefenseCanvas");
if (canvas == null) return;
if (buffSelectionWindow == null)
{
var winTrans = canvas.transform.Find("BuffSelectionWindow");
if (winTrans != null) buffSelectionWindow = winTrans.gameObject;
}
if (buffSelectionWindow != null && cardsContainer == null)
{
var containerTrans = buffSelectionWindow.transform.Find("BuffPanel/BuffCardsContainer");
if (containerTrans != null) cardsContainer = containerTrans;
}
}
}