From 3c504151115b9e1a07382efc63e5dd51671cdb37 Mon Sep 17 00:00:00 2001 From: Konstantin Dyachenko Date: Sun, 1 Mar 2026 16:14:44 +0700 Subject: [PATCH] Extend VContainer DI: eliminate manual composition and duplicate references - Register InventoryModel, ShopModel as container-managed singletons - Register GameController, ShopController, InventoryController via RegisterComponent - Replace [SerializeField] with [Inject] for service dependencies in controllers - Move maxActiveModifierSlots config to GameLifetimeScope (composition root) - Remove manual model creation and Initialize() calls from GameController - Add ToggleVisibility() to ShopController/InventoryController, removing GetComponentInChildren - Move event subscriptions from Awake to Start for safe VContainer injection order - Transfer game startup orchestration to GameController.Start() Co-Authored-By: Claude Opus 4.6 --- Assets/Scripts/DI/GameLifetimeScope.cs | 19 ++- Assets/Scripts/Game/GameManager.cs | 14 +- .../Scripts/Inventory/InventoryController.cs | 29 ++++- Assets/Scripts/Shop/ShopController.cs | 32 +++-- Assets/Scripts/UI/GameController.cs | 120 +++++++----------- 5 files changed, 116 insertions(+), 98 deletions(-) diff --git a/Assets/Scripts/DI/GameLifetimeScope.cs b/Assets/Scripts/DI/GameLifetimeScope.cs index af9acf0..8986e00 100644 --- a/Assets/Scripts/DI/GameLifetimeScope.cs +++ b/Assets/Scripts/DI/GameLifetimeScope.cs @@ -5,10 +5,13 @@ using YachtDice.Categories; using YachtDice.Economy; using YachtDice.Events; using YachtDice.Game; +using YachtDice.Inventory; using YachtDice.Modifiers.Definition; using YachtDice.Modifiers.Pipeline; using YachtDice.Modifiers.Runtime; using YachtDice.Scoring; +using YachtDice.Shop; +using YachtDice.UI; namespace YachtDice.DI { @@ -22,6 +25,12 @@ namespace YachtDice.DI [SerializeField] private CurrencyBank currencyBank; [SerializeField] private GameManager gameManager; [SerializeField] private DiceManager diceManager; + [SerializeField] private GameController gameController; + [SerializeField] private ShopController shopController; + [SerializeField] private InventoryController inventoryController; + + [Header("Settings")] + [SerializeField] private int maxActiveModifierSlots = 5; protected override void Configure(IContainerBuilder builder) { @@ -30,15 +39,23 @@ namespace YachtDice.DI builder.RegisterInstance(categoryCatalog); // Core modifier services - builder.Register(Lifetime.Singleton); + builder.Register(Lifetime.Singleton) + .WithParameter(maxActiveModifierSlots); builder.Register(Lifetime.Singleton); builder.Register(Lifetime.Singleton); + // Domain models + builder.Register(Lifetime.Singleton); + builder.Register(Lifetime.Singleton); + // Scene MonoBehaviour components builder.RegisterComponent(scoringSystem); builder.RegisterComponent(currencyBank); builder.RegisterComponent(gameManager); builder.RegisterComponent(diceManager); + builder.RegisterComponent(gameController); + builder.RegisterComponent(shopController); + builder.RegisterComponent(inventoryController); } } } diff --git a/Assets/Scripts/Game/GameManager.cs b/Assets/Scripts/Game/GameManager.cs index c4f2364..fc48f0e 100644 --- a/Assets/Scripts/Game/GameManager.cs +++ b/Assets/Scripts/Game/GameManager.cs @@ -1,5 +1,6 @@ using System; using UnityEngine; +using VContainer; using YachtDice.Categories; using YachtDice.Scoring; @@ -7,13 +8,12 @@ namespace YachtDice.Game { public class GameManager : MonoBehaviour { - [Header("References")] - [SerializeField] private DiceManager diceManager; - [SerializeField] private ScoringSystem scoringSystem; - [Header("Settings")] [SerializeField] private int maxRollsPerTurn = 3; + private DiceManager diceManager; + private ScoringSystem scoringSystem; + public int CurrentRoll { get; private set; } public int CurrentTurn { get; private set; } @@ -26,9 +26,11 @@ namespace YachtDice.Game public event Action OnScored; public event Action OnGameOver; - private void Start() + [Inject] + public void Construct(DiceManager diceManager, ScoringSystem scoringSystem) { - StartNewGame(); + this.diceManager = diceManager; + this.scoringSystem = scoringSystem; } public void StartNewGame() diff --git a/Assets/Scripts/Inventory/InventoryController.cs b/Assets/Scripts/Inventory/InventoryController.cs index 7c8a644..8ac2f17 100644 --- a/Assets/Scripts/Inventory/InventoryController.cs +++ b/Assets/Scripts/Inventory/InventoryController.cs @@ -1,4 +1,5 @@ using UnityEngine; +using VContainer; using YachtDice.Categories; using YachtDice.Economy; using YachtDice.Modifiers.Runtime; @@ -9,25 +10,29 @@ namespace YachtDice.Inventory public class InventoryController : MonoBehaviour { [SerializeField] private InventoryView inventoryView; - [SerializeField] private ScoringSystem scoringSystem; - [SerializeField] private CurrencyBank currencyBank; private InventoryModel model; + private ScoringSystem scoringSystem; + private CurrencyBank currencyBank; public InventoryModel Model => model; - public void Initialize(InventoryModel inventoryModel) + [Inject] + public void Construct(InventoryModel model, ScoringSystem scoringSystem, CurrencyBank currencyBank) { - model = inventoryModel; + this.model = model; + this.scoringSystem = scoringSystem; + this.currencyBank = currencyBank; + } + private void Start() + { inventoryView.OnActivateClicked += HandleActivate; inventoryView.OnDeactivateClicked += HandleDeactivate; inventoryView.OnSellClicked += HandleSell; model.OnInventoryChanged += HandleInventoryChanged; - - if (scoringSystem != null) - scoringSystem.OnCategoryConfirmed += HandleCategoryConfirmed; + scoringSystem.OnCategoryConfirmed += HandleCategoryConfirmed; RefreshView(); } @@ -48,6 +53,16 @@ namespace YachtDice.Inventory scoringSystem.OnCategoryConfirmed -= HandleCategoryConfirmed; } + public void ToggleVisibility() + { + if (inventoryView == null) return; + + if (inventoryView.IsVisible) + inventoryView.Hide(); + else + inventoryView.Show(); + } + private void HandleActivate(ModifierInstance instance) { model.TryActivate(instance); diff --git a/Assets/Scripts/Shop/ShopController.cs b/Assets/Scripts/Shop/ShopController.cs index 52174d3..8aecc4f 100644 --- a/Assets/Scripts/Shop/ShopController.cs +++ b/Assets/Scripts/Shop/ShopController.cs @@ -1,4 +1,5 @@ using UnityEngine; +using VContainer; using YachtDice.Economy; using YachtDice.Modifiers.Definition; @@ -6,27 +7,30 @@ namespace YachtDice.Shop { public class ShopController : MonoBehaviour { - [SerializeField] private ModifierCatalogSO catalog; [SerializeField] private ShopView shopView; - [SerializeField] private CurrencyBank currencyBank; + private ModifierCatalogSO catalog; + private CurrencyBank currencyBank; private ShopModel model; public ModifierCatalogSO Catalog => catalog; - public void Initialize(ShopModel shopModel) + [Inject] + public void Construct(ModifierCatalogSO catalog, CurrencyBank currencyBank, ShopModel model) { - model = shopModel; + this.catalog = catalog; + this.currencyBank = currencyBank; + this.model = model; + } + private void Start() + { shopView.OnBuyClicked += HandleBuyClicked; - - if (currencyBank != null) - currencyBank.OnBalanceChanged += HandleCurrencyChanged; - + currencyBank.OnBalanceChanged += HandleCurrencyChanged; model.OnItemPurchased += HandleItemPurchased; shopView.Populate(catalog.All, model); - shopView.UpdateCurrencyDisplay(currencyBank != null ? currencyBank.Balance : 0); + shopView.UpdateCurrencyDisplay(currencyBank.Balance); } private void OnDestroy() @@ -41,6 +45,16 @@ namespace YachtDice.Shop model.OnItemPurchased -= HandleItemPurchased; } + public void ToggleVisibility() + { + if (shopView == null) return; + + if (shopView.IsVisible) + shopView.Hide(); + else + shopView.Show(); + } + private void HandleBuyClicked(ModifierDefinitionSO def) { model.TryPurchase(def); diff --git a/Assets/Scripts/UI/GameController.cs b/Assets/Scripts/UI/GameController.cs index 6fa13ce..2086b11 100644 --- a/Assets/Scripts/UI/GameController.cs +++ b/Assets/Scripts/UI/GameController.cs @@ -16,43 +16,56 @@ namespace YachtDice.UI { public class GameController : MonoBehaviour { - [Header("Model")] - [SerializeField] private GameManager gameManager; - [SerializeField] private ScoringSystem scoringSystem; - [SerializeField] private DiceManager diceManager; - [Header("Views")] [SerializeField] private ScoreCardView scoreCardView; [SerializeField] private DicePanelView dicePanelView; [SerializeField] private GameInfoView gameInfoView; - [Header("Economy & Modifiers")] - [SerializeField] private CurrencyBank currencyBank; - [SerializeField] private ShopController shopController; - [SerializeField] private InventoryController inventoryController; - [Header("Settings")] [SerializeField] private int maxRollsPerTurn = 3; - [SerializeField] private int maxActiveModifierSlots = 5; private const int UpperBonusThreshold = 63; private const int UpperBonusValue = 35; + private GameManager gameManager; + private ScoringSystem scoringSystem; + private DiceManager diceManager; + private CurrencyBank currencyBank; + private ShopController shopController; + private InventoryController inventoryController; private ModifierRegistry modifierRegistry; private CategoryCatalog categoryCatalog; - private InventoryModel inventoryModel; + private ModifierCatalogSO modifierCatalog; private ShopModel shopModel; [Inject] - public void Construct(ModifierRegistry modifierRegistry, CategoryCatalog categoryCatalog) + public void Construct( + GameManager gameManager, + ScoringSystem scoringSystem, + DiceManager diceManager, + CurrencyBank currencyBank, + ShopController shopController, + InventoryController inventoryController, + ModifierRegistry modifierRegistry, + CategoryCatalog categoryCatalog, + ModifierCatalogSO modifierCatalog, + ShopModel shopModel) { + this.gameManager = gameManager; + this.scoringSystem = scoringSystem; + this.diceManager = diceManager; + this.currencyBank = currencyBank; + this.shopController = shopController; + this.inventoryController = inventoryController; this.modifierRegistry = modifierRegistry; this.categoryCatalog = categoryCatalog; + this.modifierCatalog = modifierCatalog; + this.shopModel = shopModel; } // ── Lifecycle ────────────────────────────────────────────── - private void Awake() + private void Start() { // Model → Controller gameManager.OnTurnStarted += HandleTurnStarted; @@ -69,17 +82,17 @@ namespace YachtDice.UI gameInfoView.OnShopClicked += HandleShopClicked; gameInfoView.OnInventoryClicked += HandleInventoryClicked; - // Currency - if (currencyBank != null) - currencyBank.OnBalanceChanged += HandleCurrencyChanged; - } + // Currency & Modifiers + currencyBank.OnBalanceChanged += HandleCurrencyChanged; + modifierRegistry.OnChanged += HandleInventoryChangedForSave; - private void Start() - { - // Инициализируем скоркарту из каталога категорий + // Initialize scoreCardView.Initialize(categoryCatalog); + LoadSaveData(); + gameInfoView.SetCurrencyText(currencyBank.Balance); - InitializeModifierSystems(); + // Start the game after all subscriptions are in place + gameManager.StartNewGame(); } private void OnDestroy() @@ -97,49 +110,22 @@ namespace YachtDice.UI gameInfoView.OnShopClicked -= HandleShopClicked; gameInfoView.OnInventoryClicked -= HandleInventoryClicked; - if (currencyBank != null) - currencyBank.OnBalanceChanged -= HandleCurrencyChanged; + currencyBank.OnBalanceChanged -= HandleCurrencyChanged; if (modifierRegistry != null) modifierRegistry.OnChanged -= HandleInventoryChangedForSave; } - // ── Modifier System Init ───────────────────────────────── + // ── Save / Load ───────────────────────────────────────── - private void InitializeModifierSystems() - { - if (modifierRegistry == null) - modifierRegistry = new ModifierRegistry(maxActiveModifierSlots); - else - modifierRegistry.SetMaxActiveSlots(maxActiveModifierSlots); - - modifierRegistry.OnChanged += HandleInventoryChangedForSave; - - inventoryModel = new InventoryModel(modifierRegistry); - - shopModel = new ShopModel(currencyBank, inventoryModel); - - ModifierCatalogSO catalog = shopController != null ? shopController.Catalog : null; - LoadSaveData(catalog); - - if (inventoryController != null) - inventoryController.Initialize(inventoryModel); - - if (shopController != null) - shopController.Initialize(shopModel); - - if (currencyBank != null) - gameInfoView.SetCurrencyText(currencyBank.Balance); - } - - private void LoadSaveData(ModifierCatalogSO catalog) + private void LoadSaveData() { SaveData save = SaveSystem.Load(); - if (currencyBank != null && save.Currency > 0) + if (save.Currency > 0) currencyBank.SetBalance(save.Currency); - if (catalog != null && save.OwnedModifiers.Count > 0) + if (modifierCatalog != null && save.OwnedModifiers.Count > 0) { var entries = new List(); var permanentIds = new HashSet(); @@ -147,7 +133,7 @@ namespace YachtDice.UI for (int i = 0; i < save.OwnedModifiers.Count; i++) { var oldEntry = save.OwnedModifiers[i]; - var def = catalog.FindById(oldEntry.ModifierId); + var def = modifierCatalog.FindById(oldEntry.ModifierId); if (def == null) { @@ -168,7 +154,7 @@ namespace YachtDice.UI permanentIds.Add(def.Id); } - modifierRegistry.LoadSaveData(entries, catalog); + modifierRegistry.LoadSaveData(entries, modifierCatalog); shopModel.LoadPurchasedPermanentIds(permanentIds); } } @@ -177,7 +163,7 @@ namespace YachtDice.UI { var save = new SaveData { - Currency = currencyBank != null ? currencyBank.Balance : 0 + Currency = currencyBank.Balance }; var entries = modifierRegistry.GetSaveData(); @@ -277,28 +263,12 @@ namespace YachtDice.UI private void HandleShopClicked() { - if (shopController != null) - { - var shopView = shopController.GetComponentInChildren(true); - if (shopView != null) - { - if (shopView.IsVisible) shopView.Hide(); - else shopView.Show(); - } - } + shopController.ToggleVisibility(); } private void HandleInventoryClicked() { - if (inventoryController != null) - { - var inventoryView = inventoryController.GetComponentInChildren(true); - if (inventoryView != null) - { - if (inventoryView.IsVisible) inventoryView.Hide(); - else inventoryView.Show(); - } - } + inventoryController.ToggleVisibility(); } private void HandleCurrencyChanged(int newBalance)