refactor 7:

- 拆分 CombatScheduler,将公用方法与字段拆分为:
    - FlowCoordinator
    - RuntimeContext
- 地图侧现在收口为“MapData 初始快照 + CombatCoinChangedEventArgs 同步 + 独立命令桥接”
This commit is contained in:
2026-03-07 19:42:17 +08:00
parent eb818e6295
commit b38088c3ea
25 changed files with 1031 additions and 786 deletions
@@ -15,9 +15,6 @@ namespace GeometryTD.Entity.EntityData
[SerializeField] private BackpackInventoryData _inventorySnapshot;
[SerializeField] private TowerItemData[] _participantTowerSnapshot = Array.Empty<TowerItemData>();
[NonSerialized] private Func<int, bool> _tryConsumeCoin;
[NonSerialized] private Action<int> _addCoin;
public MapData(int entityId, int levelId, Vector3 position) : this(entityId, 0, levelId, position)
{
}
@@ -36,9 +33,7 @@ namespace GeometryTD.Entity.EntityData
int initialCoin,
IReadOnlyList<TowerStatsData> buildTowerStatsSnapshot,
BackpackInventoryData inventorySnapshot,
IReadOnlyList<TowerItemData> participantTowerSnapshot,
Func<int, bool> tryConsumeCoin,
Action<int> addCoin) : base(entityId, typeId)
IReadOnlyList<TowerItemData> participantTowerSnapshot) : base(entityId, typeId)
{
_levelId = levelId;
Position = position;
@@ -48,8 +43,6 @@ namespace GeometryTD.Entity.EntityData
? InventoryCloneUtility.CloneInventory(inventorySnapshot)
: null;
SetParticipantTowerSnapshot(participantTowerSnapshot);
_tryConsumeCoin = tryConsumeCoin;
_addCoin = addCoin;
}
public int LevelId
@@ -70,34 +63,6 @@ namespace GeometryTD.Entity.EntityData
: null;
public IReadOnlyList<TowerItemData> ParticipantTowerSnapshot => _participantTowerSnapshot;
public void BindCombatCallbacks(Func<int, bool> tryConsumeCoin, Action<int> addCoin)
{
_tryConsumeCoin = tryConsumeCoin;
_addCoin = addCoin;
}
public bool TryConsumeCoin(int coin)
{
int requiredCoin = Mathf.Max(0, coin);
if (requiredCoin <= 0)
{
return true;
}
return _tryConsumeCoin != null && _tryConsumeCoin.Invoke(requiredCoin);
}
public void AddCoin(int coin)
{
int amount = Mathf.Max(0, coin);
if (amount <= 0)
{
return;
}
_addCoin?.Invoke(amount);
}
public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats)
{
stats = null;
@@ -141,9 +106,7 @@ namespace GeometryTD.Entity.EntityData
_initialCoin,
_buildTowerStatsSnapshot,
_inventorySnapshot,
_participantTowerSnapshot,
_tryConsumeCoin,
_addCoin);
_participantTowerSnapshot);
}
public void SetParticipantTowerSnapshot(IReadOnlyList<TowerItemData> participantTowerSnapshot)
@@ -0,0 +1,18 @@
using System;
namespace GeometryTD.Entity.EntityData
{
public sealed class MapEntityLoadContext
{
public MapEntityLoadContext(MapData initialMapData, Func<int, bool> tryConsumeCoin, Action<int> addCoin)
{
InitialMapData = initialMapData;
TryConsumeCoin = tryConsumeCoin;
AddCoin = addCoin;
}
public MapData InitialMapData { get; }
public Func<int, bool> TryConsumeCoin { get; }
public Action<int> AddCoin { get; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ba39a6e9c0948b386b8b0189e3541da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -56,6 +56,17 @@ namespace GeometryTD
public static void ShowMap(this EntityComponent entityComponent, MapData data, string mapAssetName)
{
ShowMap(entityComponent, new MapEntityLoadContext(data, null, null), mapAssetName);
}
public static void ShowMap(this EntityComponent entityComponent, MapEntityLoadContext loadContext)
{
ShowMap(entityComponent, loadContext, null);
}
public static void ShowMap(this EntityComponent entityComponent, MapEntityLoadContext loadContext, string mapAssetName)
{
MapData data = loadContext?.InitialMapData;
if (data == null)
{
Log.Warning("Map data is invalid.");
@@ -64,7 +75,7 @@ namespace GeometryTD
string resolvedMapAssetName = string.IsNullOrEmpty(mapAssetName) ? data.LevelId.ToString() : mapAssetName;
string mapAssetPath = AssetUtility.GetLevelMapAsset(resolvedMapAssetName);
entityComponent.ShowEntity(data.Id, typeof(MapEntity), mapAssetPath, "Map", Constant.AssetPriority.MapAsset, data);
entityComponent.ShowEntity(data.Id, typeof(MapEntity), mapAssetPath, "Map", Constant.AssetPriority.MapAsset, loadContext);
}
private static void ShowEntity(this EntityComponent entityComponent, Type logicType, string entityGroup,
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using GeometryTD.CustomComponent;
using GeometryTD.CustomEvent;
using GameFramework.Event;
using GeometryTD.Map;
using GeometryTD.Definition;
using GeometryTD.Entity.EntityData;
@@ -37,10 +35,8 @@ namespace GeometryTD.Entity
private CombatSelectInputService _combatSelectInputService;
private TowerPlacementService _towerPlacementService;
private TowerSelectionPresenter _towerSelectionPresenter;
private CombatSelectUseCaseConfigurator _combatSelectUseCaseConfigurator;
private int _currentCoin;
private bool _isCoinEventSubscribed;
private MapCombatRuntimeBridge _combatRuntimeBridge;
public IReadOnlyList<Vector3Int> PathCells => _mapTopologyService != null
? _mapTopologyService.PathCells
@@ -110,22 +106,21 @@ namespace GeometryTD.Entity
InitializeMapTopologyService();
InitializeTowerPlacementService();
InitializeTowerSelectionPresenter();
InitializeCombatRuntimeBridge();
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
_mapData = userData as MapData;
MapEntityLoadContext loadContext = ResolveLoadContext(userData);
_mapData = loadContext?.InitialMapData;
if (_mapData == null)
{
Log.Warning("MapData is invalid for map entity '{0}'.", Id);
}
else
{
_currentCoin = Mathf.Max(0, _mapData.InitialCoin);
SubscribeCombatEvents();
}
base.OnShow(_mapData);
_combatRuntimeBridge?.Initialize(loadContext, name);
RefreshTiles();
ConfigureCombatSelectUseCase();
@@ -134,7 +129,7 @@ namespace GeometryTD.Entity
protected override void OnHide(bool isShutdown, object userData)
{
UnsubscribeCombatEvents();
_combatRuntimeBridge?.Reset();
HideCombatSelectForm();
_towerPlacementService?.HideAndClearAllPlacedTowers();
ClearSelectionState();
@@ -240,6 +235,14 @@ namespace GeometryTD.Entity
}
}
private void InitializeCombatRuntimeBridge()
{
if (_combatRuntimeBridge == null)
{
_combatRuntimeBridge = new MapCombatRuntimeBridge();
}
}
private void ConfigureCombatSelectUseCase()
{
_combatSelectFormUseCase.SetCoinProvider(GetCurrentCoin);
@@ -257,6 +260,21 @@ namespace GeometryTD.Entity
_mapData != null ? _mapData.ParticipantTowerSnapshot : null);
}
private MapEntityLoadContext ResolveLoadContext(object userData)
{
if (userData is MapEntityLoadContext loadContext)
{
return loadContext;
}
if (userData is MapData legacyMapData)
{
return new MapEntityLoadContext(legacyMapData, null, null);
}
return null;
}
private void HandleCombatSelectInput()
{
if (!_enableCombatSelectInput || !Input.GetMouseButtonDown(0))
@@ -379,38 +397,6 @@ namespace GeometryTD.Entity
GameEntry.UIRouter.CloseUI(UIFormType.CombatSelectForm);
}
private void SubscribeCombatEvents()
{
if (_isCoinEventSubscribed)
{
return;
}
GameEntry.Event.Subscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
_isCoinEventSubscribed = true;
}
private void UnsubscribeCombatEvents()
{
if (!_isCoinEventSubscribed)
{
return;
}
GameEntry.Event.Unsubscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
_isCoinEventSubscribed = false;
}
private void OnCombatCoinChanged(object sender, GameEventArgs e)
{
if (e is not CombatCoinChangedEventArgs args)
{
return;
}
_currentCoin = Mathf.Max(0, args.CurrentCoin);
}
private int GetCurrentBuildTowerCount()
{
if (_mapData == null)
@@ -434,12 +420,12 @@ namespace GeometryTD.Entity
return false;
}
return _mapData.TryConsumeCoin(requiredCoin);
return _combatRuntimeBridge != null && _combatRuntimeBridge.TryConsumeCoin(requiredCoin);
}
private int GetCurrentCoin()
{
return Mathf.Max(0, _currentCoin);
return Mathf.Max(0, _combatRuntimeBridge != null ? _combatRuntimeBridge.CurrentCoin : 0);
}
private void AddCoin(int coin)
@@ -450,7 +436,7 @@ namespace GeometryTD.Entity
return;
}
_mapData?.AddCoin(amount);
_combatRuntimeBridge?.AddCoin(amount);
}
}
}