M2 回合制调度:速度/阵营/队伍顺序规则、先制第一轮、4v4 调试入口与调度测试
- BattleRuntime 调度替换为完整规则:速度高优先、同速玩家优先、同阵营同速 PartyOrder 小优先;先制第一轮玩家全先行,第二轮恢复普通排序;RemainingTurnOrder 同步按调度序输出 - TurnBattleComponent 开局行动者为敌人时自动推进,协程等待 UI 监听注册避免先手事件丢失 - 调试入口支持 1v1 / 2v2 / 4v4 与先制开关;新增敌人队伍预设 4005(四普通敌人) - UI:修复 UIForm 复用导致第二次开战按钮监听丢失;EnemySlots/TurnSlots 改为按数量复用 item,图标同一单位不重复加载 - EditMode 新增 10 个调度用例(共 35 个):排序规则、先制两轮、重复敌人分别击杀、4v4 创建、死亡移出候选、固定随机序列复现
This commit is contained in:
@@ -18,16 +18,12 @@ namespace SepCore.UI
|
||||
private int _displayedRound;
|
||||
private readonly List<BattleTurnSlotItem> _turnSlots = new List<BattleTurnSlotItem>();
|
||||
private readonly List<int> _turnSlotUnitIds = new List<int>();
|
||||
private readonly List<BattleEnemySlotItem> _enemySlots = new List<BattleEnemySlotItem>();
|
||||
|
||||
protected override void OnInit(object userData)
|
||||
{
|
||||
base.OnInit(userData);
|
||||
|
||||
View.attackButton.onClick.AddListener(OnAttackButtonClick);
|
||||
View.skillButton.onClick.AddListener(OnSkillButtonClick);
|
||||
View.itemButton.onClick.AddListener(OnItemButtonClick);
|
||||
View.escapeButton.onClick.AddListener(OnEscapeButtonClick);
|
||||
|
||||
// M1 只开放攻击;道具、技能、逃跑在后续里程碑接入
|
||||
View.itemButton.interactable = false;
|
||||
View.skillButton.interactable = false;
|
||||
@@ -38,6 +34,12 @@ namespace SepCore.UI
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
|
||||
// UIForm 实例复用:每次打开都重新注册按钮监听(OnClose 会移除)
|
||||
View.attackButton.onClick.AddListener(OnAttackButtonClick);
|
||||
View.skillButton.onClick.AddListener(OnSkillButtonClick);
|
||||
View.itemButton.onClick.AddListener(OnItemButtonClick);
|
||||
View.escapeButton.onClick.AddListener(OnEscapeButtonClick);
|
||||
|
||||
if (GameEntry.TurnBattle != null)
|
||||
{
|
||||
GameEntry.TurnBattle.SetStepListener(ApplyStep);
|
||||
@@ -47,6 +49,7 @@ namespace SepCore.UI
|
||||
_displayedRound = 0;
|
||||
_turnSlots.Clear();
|
||||
_turnSlotUnitIds.Clear();
|
||||
_enemySlots.Clear();
|
||||
Refresh(GameEntry.TurnBattle != null ? GameEntry.TurnBattle.GetViewState() : null);
|
||||
}
|
||||
|
||||
@@ -164,19 +167,32 @@ namespace SepCore.UI
|
||||
return;
|
||||
}
|
||||
|
||||
template.gameObject.SetActive(false);
|
||||
ClearSlots(View.enemySlotsRoot, template.transform);
|
||||
|
||||
List<BattleUnitView> enemies = new List<BattleUnitView>();
|
||||
foreach (BattleUnitView unit in view.Units)
|
||||
{
|
||||
if (unit.Faction != BattleFaction.Enemy)
|
||||
if (unit.Faction == BattleFaction.Enemy)
|
||||
{
|
||||
continue;
|
||||
enemies.Add(unit);
|
||||
}
|
||||
}
|
||||
|
||||
BattleEnemySlotItem slot = Instantiate(template, View.enemySlotsRoot);
|
||||
slot.gameObject.SetActive(true);
|
||||
slot.SetEnemy(unit, unit.BattleUnitId == view.CurrentActorUnitId);
|
||||
// 敌人数量在一场战斗内固定:数量变化(新开战斗/规模不同)时才重建,之后原地复用
|
||||
if (_enemySlots.Count != enemies.Count)
|
||||
{
|
||||
template.gameObject.SetActive(false);
|
||||
ClearSlots(View.enemySlotsRoot, template.transform);
|
||||
_enemySlots.Clear();
|
||||
foreach (BattleUnitView unit in enemies)
|
||||
{
|
||||
BattleEnemySlotItem slot = Instantiate(template, View.enemySlotsRoot);
|
||||
slot.gameObject.SetActive(true);
|
||||
_enemySlots.Add(slot);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _enemySlots.Count; i++)
|
||||
{
|
||||
_enemySlots[i].SetEnemy(enemies[i], enemies[i].BattleUnitId == view.CurrentActorUnitId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +205,8 @@ namespace SepCore.UI
|
||||
return;
|
||||
}
|
||||
|
||||
if (view.RoundNumber != _displayedRound)
|
||||
if (view.RoundNumber != _displayedRound ||
|
||||
(view.CurrentActorUnitId != 0 && !_turnSlotUnitIds.Contains(view.CurrentActorUnitId)))
|
||||
{
|
||||
RebuildTurnSlots(view, template);
|
||||
_displayedRound = view.RoundNumber;
|
||||
@@ -204,28 +221,41 @@ namespace SepCore.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新一轮开始时重建本轮完整顺序:全部活跃单位按显示顺序排列,
|
||||
/// 新一轮开始时刷新本轮顺序:从当前行动者开始的剩余单位按调度优先级排列,
|
||||
/// 当前行动者高亮;本轮内已行动单位保持可见。
|
||||
/// 数量与上一轮相同时复用槽对象,只重建单位映射与内容;
|
||||
/// 先制第一轮只有玩家候选,敌人进入行动阶段时(当前行动者不在列表)自动重建为剩余敌人。
|
||||
/// </summary>
|
||||
private void RebuildTurnSlots(BattleViewState view, BattleTurnSlotItem template)
|
||||
{
|
||||
template.gameObject.SetActive(false);
|
||||
ClearSlots(View.turnSlotsRoot, template.transform);
|
||||
_turnSlots.Clear();
|
||||
_turnSlotUnitIds.Clear();
|
||||
|
||||
foreach (BattleUnitView unit in view.Units)
|
||||
List<BattleUnitView> units = new List<BattleUnitView>();
|
||||
foreach (int unitId in view.RemainingTurnOrder)
|
||||
{
|
||||
if (unit.IsDefeated || unit.IsEscaped)
|
||||
BattleUnitView unit = FindUnit(view, unitId);
|
||||
if (unit != null)
|
||||
{
|
||||
continue;
|
||||
units.Add(unit);
|
||||
}
|
||||
}
|
||||
|
||||
BattleTurnSlotItem slot = Instantiate(template, View.turnSlotsRoot);
|
||||
slot.gameObject.SetActive(true);
|
||||
slot.SetTurnSlot(unit, unit.BattleUnitId == view.CurrentActorUnitId);
|
||||
_turnSlots.Add(slot);
|
||||
_turnSlotUnitIds.Add(unit.BattleUnitId);
|
||||
if (_turnSlots.Count != units.Count)
|
||||
{
|
||||
template.gameObject.SetActive(false);
|
||||
ClearSlots(View.turnSlotsRoot, template.transform);
|
||||
_turnSlots.Clear();
|
||||
foreach (BattleUnitView unit in units)
|
||||
{
|
||||
BattleTurnSlotItem slot = Instantiate(template, View.turnSlotsRoot);
|
||||
slot.gameObject.SetActive(true);
|
||||
_turnSlots.Add(slot);
|
||||
}
|
||||
}
|
||||
|
||||
_turnSlotUnitIds.Clear();
|
||||
for (int i = 0; i < _turnSlots.Count; i++)
|
||||
{
|
||||
_turnSlotUnitIds.Add(units[i].BattleUnitId);
|
||||
_turnSlots[i].SetTurnSlot(units[i], units[i].BattleUnitId == view.CurrentActorUnitId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +319,7 @@ namespace SepCore.UI
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static BattleUnitView FindUnit(BattleViewState view, int unitId)
|
||||
private static BattleUnitView FindUnit(BattleViewState view, int unitId)
|
||||
{
|
||||
foreach (BattleUnitView unit in view.Units)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user