```
feat(gameplay): 提升单位检测范围并添加法师属性 - 将所有单位的检测范围从默认值提升至100 - 为法师预制体添加护盾、等级等新属性 - 为法师添加暴击、吸血、伤害减免等战斗属性 - 更换法师弹丸预设并调整相关参数 - 添加锚点交战Z线控制功能 - 更新玩家数据中配备的召唤师技能 - 移除多余的纹理配置文件 ```
This commit is contained in:
1 parent
6089308f66
commit
23c559d9f7
243 files changed
+150612
-1459
No files matched your search
@@ -181,7 +181,8 @@ public class BattleFormationSpawner : MonoBehaviour
|
||||
unitBase.InitializeFromPlayerData(
|
||||
characterData.GetMaxHealth(level),
|
||||
characterData.GetAttackPower(level),
|
||||
characterData.GetMoveSpeed(level));
|
||||
characterData.GetMoveSpeed(level),
|
||||
level);
|
||||
}
|
||||
|
||||
// 设置友方英雄可复活
|
||||
|
||||
@@ -18,6 +18,7 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
public DefenseWaveLevelController waveLevelController;
|
||||
public Button startBattleButton;
|
||||
public GameObject deploymentUI; // 布阵 UI(英雄卡片面板)
|
||||
public Text deployHintText; // 布阵提示文本
|
||||
|
||||
[Header("英雄配置")]
|
||||
public HeroRoster roster;
|
||||
@@ -77,6 +78,7 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
SetupDeployGrid();
|
||||
DeployHighlight.Ensure();
|
||||
InitializeCardDrag();
|
||||
EnsureDeployHintText();
|
||||
|
||||
Debug.Log("DefenseGameFlowController: 布阵阶段,把英雄卡拖到网格上。");
|
||||
}
|
||||
@@ -138,7 +140,7 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
}
|
||||
|
||||
AlignGridCells(deployGridRoot.transform);
|
||||
deployGridRoot.SetActive(true);
|
||||
deployGridRoot.SetActive(false); // 隐藏蓝框视觉,仅保留拖拽时的防守格位高亮
|
||||
}
|
||||
|
||||
/// <summary>把网格下的格位按 DeployGrid 的坐标摆好,保证视觉与吸附完全一致。</summary>
|
||||
@@ -227,6 +229,58 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
Debug.Log($"DefenseGameFlowController: 已按出战阵容开启 {cardDrags.Count} 张卡片的拖动。");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保布阵提示文本存在。如果 Inspector 未绑定,则在 TowerDefenseCanvas 下自动创建或寻找。
|
||||
/// </summary>
|
||||
private void EnsureDeployHintText()
|
||||
{
|
||||
if (deployHintText != null)
|
||||
{
|
||||
deployHintText.gameObject.SetActive(true);
|
||||
deployHintText.text = "拖拽下方卡牌部署英雄到格位";
|
||||
return;
|
||||
}
|
||||
|
||||
var canvasObj = GameObject.Find("TowerDefenseCanvas");
|
||||
if (canvasObj == null) return;
|
||||
|
||||
var existingHint = canvasObj.transform.Find("DeployHintText");
|
||||
if (existingHint != null)
|
||||
{
|
||||
deployHintText = existingHint.GetComponent<Text>();
|
||||
if (deployHintText != null)
|
||||
{
|
||||
deployHintText.gameObject.SetActive(true);
|
||||
deployHintText.text = "拖拽下方卡牌部署英雄到格位";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 自动创建 DeployHintText UI
|
||||
var hintObj = new GameObject("DeployHintText", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
||||
hintObj.transform.SetParent(canvasObj.transform, false);
|
||||
|
||||
var rect = hintObj.GetComponent<RectTransform>();
|
||||
rect.anchorMin = new Vector2(0.5f, 1f);
|
||||
rect.anchorMax = new Vector2(0.5f, 1f);
|
||||
rect.pivot = new Vector2(0.5f, 1f);
|
||||
rect.anchoredPosition = new Vector2(0f, -310f); // 放在顶部标题与开始按钮下方
|
||||
rect.sizeDelta = new Vector2(600f, 60f);
|
||||
|
||||
deployHintText = hintObj.GetComponent<Text>();
|
||||
deployHintText.text = "拖拽下方卡牌部署英雄到格位";
|
||||
deployHintText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||||
deployHintText.fontSize = 28;
|
||||
deployHintText.fontStyle = FontStyle.Bold;
|
||||
deployHintText.alignment = TextAnchor.MiddleCenter;
|
||||
deployHintText.color = new Color(1f, 0.92f, 0.5f, 1f); // 亮暖黄色,突出提示
|
||||
|
||||
// 加上外发光/阴影组件增强可视度
|
||||
var shadow = hintObj.AddComponent<Shadow>();
|
||||
shadow.effectColor = new Color(0f, 0f, 0f, 0.8f);
|
||||
shadow.effectDistance = new Vector2(2f, -2f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取玩家出战阵容(PlayerData.json 的 currentDeck),按槽位返回角色数据。
|
||||
/// PlayerData 不可用时回退到场景里配置的 HeroRoster。
|
||||
@@ -360,6 +414,7 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
|
||||
if (deploymentUI != null) deploymentUI.SetActive(false);
|
||||
if (startBattleButton != null) startBattleButton.gameObject.SetActive(false);
|
||||
if (deployHintText != null) deployHintText.gameObject.SetActive(false);
|
||||
if (deployGridRoot != null) deployGridRoot.SetActive(false);
|
||||
if (DeployHighlight.Instance != null) DeployHighlight.Instance.Hide();
|
||||
|
||||
@@ -432,7 +487,8 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
unitBase.InitializeFromPlayerData(
|
||||
characterData.GetMaxHealth(level),
|
||||
characterData.GetAttackPower(level),
|
||||
characterData.GetMoveSpeed(level));
|
||||
characterData.GetMoveSpeed(level),
|
||||
level);
|
||||
}
|
||||
|
||||
var healthBar = unit.GetComponent<WorldHealthBar>();
|
||||
@@ -536,12 +592,21 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
var unitBase = hero.GetComponent<UnitBase>();
|
||||
var navAgent = hero.GetComponent<UnityEngine.AI.NavMeshAgent>();
|
||||
|
||||
bool canWalk = UnityEngine.AI.NavMesh.SamplePosition(
|
||||
hero.transform.position, out _, 2f, UnityEngine.AI.NavMesh.AllAreas);
|
||||
// 如果处于攻击或技能状态,navAgent 可能会被临时禁用,需重新启用 NavMeshAgent
|
||||
if (unitBase != null)
|
||||
{
|
||||
unitBase.target = null; // 清空目标退出攻击/追踪状态
|
||||
}
|
||||
if (navAgent != null && !navAgent.enabled)
|
||||
{
|
||||
navAgent.enabled = true;
|
||||
}
|
||||
|
||||
if (navAgent != null && navAgent.enabled && canWalk)
|
||||
bool canWalk = navAgent != null && navAgent.enabled && navAgent.isOnNavMesh
|
||||
&& UnityEngine.AI.NavMesh.SamplePosition(hero.transform.position, out _, 2f, UnityEngine.AI.NavMesh.AllAreas);
|
||||
|
||||
if (canWalk)
|
||||
{
|
||||
if (unitBase != null) unitBase.target = null; // 放开目标,进入回位
|
||||
navAgent.isStopped = false;
|
||||
navAgent.ResetPath();
|
||||
continue;
|
||||
@@ -552,7 +617,11 @@ public class DefenseGameFlowController : MonoBehaviour
|
||||
{
|
||||
Debug.LogWarning($"DefenseGameFlowController: {hero.name} 寻路不可用,瞬移回格位");
|
||||
hero.transform.position = initialPos;
|
||||
if (navAgent != null) navAgent.nextPosition = initialPos;
|
||||
if (navAgent != null)
|
||||
{
|
||||
if (!navAgent.enabled) navAgent.enabled = true;
|
||||
navAgent.nextPosition = initialPos;
|
||||
}
|
||||
if (unitBase != null) unitBase.ResetAnchorPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,12 @@ public class DefenseWaveLevelController : MonoBehaviour
|
||||
public float spawnY = 0.16f;
|
||||
public float waveCompleteDelay = 3f;
|
||||
|
||||
[Header("UI 绑定")]
|
||||
public Text waveTextUI; // 绑定 TowerDefenseCanvas/TopBar/WaveText
|
||||
public Text killCountTextUI; // 绑定 TowerDefenseCanvas/TopBar/KillCountText
|
||||
|
||||
private LevelWaveFile level;
|
||||
private readonly List<GameObject> activeEnemies = new List<GameObject>();
|
||||
private Slider progressSlider;
|
||||
private Text progressText;
|
||||
private int currentWaveIndex = -1;
|
||||
private int totalEnemies;
|
||||
private int defeatedEnemies;
|
||||
@@ -55,7 +57,8 @@ public class DefenseWaveLevelController : MonoBehaviour
|
||||
private void Start()
|
||||
{
|
||||
if (!LoadLevel()) return;
|
||||
CreateProgressBar();
|
||||
EnsureUIReferences();
|
||||
UpdateProgress(0, level.waves.Length, "准备中");
|
||||
if (autoStart) StartCoroutine(RunLevel());
|
||||
}
|
||||
|
||||
@@ -105,6 +108,12 @@ public class DefenseWaveLevelController : MonoBehaviour
|
||||
// 每波结束后,让英雄回到初始位置
|
||||
ResetHeroPositionsAfterWave();
|
||||
|
||||
// 每 2 波结束(第 2、4、6、8...波)触发 3 选 1 战术增益弹窗
|
||||
if ((currentWaveIndex + 1) % 2 == 0)
|
||||
{
|
||||
BuffSelectionController.Instance.ShowBuffSelection();
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(Mathf.Max(0f, waveCompleteDelay));
|
||||
}
|
||||
|
||||
@@ -177,6 +186,16 @@ public class DefenseWaveLevelController : MonoBehaviour
|
||||
{
|
||||
activeEnemies.RemoveAt(i);
|
||||
defeatedEnemies++;
|
||||
|
||||
// 击杀金币奖励 (基础 15 金币 * 战术增益倍率)
|
||||
if (GoldManager.Instance != null)
|
||||
{
|
||||
float killMult = BuffSelectionController.KillGoldMultiplier;
|
||||
int goldAmount = Mathf.RoundToInt(15f * killMult);
|
||||
GoldManager.Instance.AddGold(goldAmount);
|
||||
}
|
||||
|
||||
UpdateProgress(currentWaveIndex + 1, level.waves.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +226,9 @@ public class DefenseWaveLevelController : MonoBehaviour
|
||||
}
|
||||
if (unit == null) return;
|
||||
|
||||
unit.InitializeFromPlayerData(data.GetMaxHealth(1), data.GetAttackPower(1), data.GetMoveSpeed(1));
|
||||
// 小怪等级根据波次索引动态计算(前几波 Lv.1,随着波次推进适度成长,如 1~3 波 Lv.1,4~6 波 Lv.2,7~10 波 Lv.3)
|
||||
int enemyLevel = Mathf.Clamp((currentWaveIndex / 3) + 1, 1, 10);
|
||||
unit.InitializeFromPlayerData(data.GetMaxHealth(enemyLevel), data.GetAttackPower(enemyLevel), data.GetMoveSpeed(enemyLevel), enemyLevel);
|
||||
}
|
||||
|
||||
private int TotalCount(WaveEntry wave)
|
||||
@@ -228,60 +249,42 @@ public class DefenseWaveLevelController : MonoBehaviour
|
||||
return entry.prefab;
|
||||
}
|
||||
|
||||
private void CreateProgressBar()
|
||||
private void EnsureUIReferences()
|
||||
{
|
||||
var canvasObject = new GameObject("WaveProgressHUD", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||||
var canvas = canvasObject.GetComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = 500;
|
||||
if (waveTextUI != null && killCountTextUI != null) return;
|
||||
|
||||
var root = canvasObject.GetComponent<RectTransform>();
|
||||
var panel = new GameObject("WaveProgressPanel", typeof(RectTransform), typeof(Image));
|
||||
panel.transform.SetParent(root, false);
|
||||
var panelRect = panel.GetComponent<RectTransform>();
|
||||
panelRect.anchorMin = new Vector2(0.5f, 1f);
|
||||
panelRect.anchorMax = new Vector2(0.5f, 1f);
|
||||
panelRect.pivot = new Vector2(0.5f, 1f);
|
||||
panelRect.anchoredPosition = new Vector2(0f, -20f);
|
||||
panelRect.sizeDelta = new Vector2(420f, 64f);
|
||||
panel.GetComponent<Image>().color = new Color(0f, 0f, 0f, 0.45f);
|
||||
var canvasObject = GameObject.Find("TowerDefenseCanvas");
|
||||
if (canvasObject == null) return;
|
||||
|
||||
var sliderObject = new GameObject("WaveProgress", typeof(RectTransform), typeof(Slider));
|
||||
sliderObject.transform.SetParent(panel.transform, false);
|
||||
var sliderRect = sliderObject.GetComponent<RectTransform>();
|
||||
sliderRect.anchorMin = new Vector2(0.5f, 0f);
|
||||
sliderRect.anchorMax = new Vector2(0.5f, 0f);
|
||||
sliderRect.pivot = new Vector2(0.5f, 0f);
|
||||
sliderRect.anchoredPosition = new Vector2(0f, 10f);
|
||||
sliderRect.sizeDelta = new Vector2(360f, 16f);
|
||||
progressSlider = sliderObject.GetComponent<Slider>();
|
||||
progressSlider.minValue = 0f;
|
||||
progressSlider.maxValue = 1f;
|
||||
progressSlider.value = 0f;
|
||||
progressSlider.interactable = false;
|
||||
var topBar = canvasObject.transform.Find("TopBar");
|
||||
if (topBar == null) return;
|
||||
|
||||
var textObject = new GameObject("WaveProgressText", typeof(RectTransform), typeof(Text));
|
||||
textObject.transform.SetParent(panel.transform, false);
|
||||
var textRect = textObject.GetComponent<RectTransform>();
|
||||
textRect.anchorMin = new Vector2(0.5f, 1f);
|
||||
textRect.anchorMax = new Vector2(0.5f, 1f);
|
||||
textRect.pivot = new Vector2(0.5f, 1f);
|
||||
textRect.anchoredPosition = new Vector2(0f, -8f);
|
||||
textRect.sizeDelta = new Vector2(390f, 28f);
|
||||
progressText = textObject.GetComponent<Text>();
|
||||
progressText.alignment = TextAnchor.MiddleCenter;
|
||||
progressText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||||
progressText.fontSize = 18;
|
||||
progressText.color = Color.white;
|
||||
if (waveTextUI == null)
|
||||
{
|
||||
var waveObj = topBar.Find("WaveText");
|
||||
if (waveObj != null) waveTextUI = waveObj.GetComponent<Text>();
|
||||
}
|
||||
|
||||
if (killCountTextUI == null)
|
||||
{
|
||||
var killObj = topBar.Find("KillCountText");
|
||||
if (killObj != null) killCountTextUI = killObj.GetComponent<Text>();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateProgress(int waveNumber, int waveCount, string text)
|
||||
private void UpdateProgress(int waveNumber, int waveCount, string statusHint = "")
|
||||
{
|
||||
if (progressSlider != null)
|
||||
EnsureUIReferences();
|
||||
|
||||
int currentDisplayWave = Mathf.Clamp(waveNumber > 0 ? waveNumber : 1, 1, waveCount);
|
||||
if (waveTextUI != null)
|
||||
{
|
||||
float defeatedRatio = totalEnemies > 0 ? (float)defeatedEnemies / totalEnemies : 0f;
|
||||
progressSlider.value = Mathf.Clamp01((waveNumber - 1 + defeatedRatio) / Mathf.Max(1, waveCount));
|
||||
waveTextUI.text = $"波次 {currentDisplayWave}/{waveCount}";
|
||||
}
|
||||
|
||||
if (killCountTextUI != null)
|
||||
{
|
||||
killCountTextUI.text = $"击杀 {defeatedEnemies}/{totalEnemies}";
|
||||
}
|
||||
if (progressText != null) progressText.text = text;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user