docs added
This commit is contained in:
8
Assets/Darkmatter/Code/Features.meta
Normal file
8
Assets/Darkmatter/Code/Features.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43ea0ae4e7f8a4f108088032adaaccd6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
543
Readme.md
543
Readme.md
@@ -1047,3 +1047,546 @@ Toddler-mode error UI:
|
||||
| `ColorBookLifetimeScope`, `AppBoot` | App | `Darkmatter.App` |
|
||||
|
||||
If a class's natural home doesn't match its asmdef, the architecture is bent — fix the placement, don't add a reference.
|
||||
|
||||
---
|
||||
|
||||
## 32. Class Reference (Detailed)
|
||||
|
||||
Canonical breakdown of every concrete class and interface. For each: **purpose**, **public surface** (signatures), **injected dependencies**, and **collaborators** (signals or interfaces it talks to).
|
||||
|
||||
> Convention used below
|
||||
> - `// fields:` = constructor-injected dependencies
|
||||
> - `// pub:` = events / signals fired
|
||||
> - `// sub:` = events / signals consumed
|
||||
> - All async returns are `UniTask` unless noted.
|
||||
|
||||
---
|
||||
|
||||
### 32.1 Core Contracts
|
||||
|
||||
Pure interfaces and DTOs. Zero logic.
|
||||
|
||||
#### `IDrawingTemplate` *(Core/Drawing)*
|
||||
Immutable view of a single drawing's authored data.
|
||||
```csharp
|
||||
public interface IDrawingTemplate {
|
||||
string Id { get; } // e.g. "animals/elephant"
|
||||
string DisplayName { get; } // user-facing
|
||||
Sprite Thumbnail { get; } // 256×256 for catalog grid
|
||||
Sprite PaperBackground { get; } // composited under artwork
|
||||
IReadOnlyList<ShapePieceDTO> Pieces { get; } // for ShapeBuilder
|
||||
IReadOnlyList<ColorRegionDTO> Regions { get; } // for Coloring
|
||||
}
|
||||
```
|
||||
Implemented by `DrawingTemplateSO` (ScriptableObject) loaded via Addressables.
|
||||
|
||||
#### `IDrawingTemplateCatalog` *(Core/Drawing)*
|
||||
Authority on which drawings exist, completion state, and "next" selection.
|
||||
```csharp
|
||||
public interface IDrawingTemplateCatalog {
|
||||
UniTask InitializeAsync(); // pulls manifest from Addressables
|
||||
IReadOnlyList<string> AllTemplateIds { get; }
|
||||
UniTask<Sprite> GetThumbnailAsync(string id); // cheap; for grid
|
||||
UniTask<IDrawingTemplate> LoadAsync(string id); // expensive; full template
|
||||
void Release(string id); // Addressables ref count down
|
||||
string NextUnseen(string currentId); // progression hint
|
||||
}
|
||||
```
|
||||
|
||||
#### `IColorPalette` *(Core/Coloring)*
|
||||
Set of colors offered to the child. Authored as `ColorPaletteSO`.
|
||||
```csharp
|
||||
public interface IColorPalette {
|
||||
string Id { get; }
|
||||
IReadOnlyList<Color> Colors { get; } // 6–10 entries typical
|
||||
}
|
||||
```
|
||||
|
||||
#### `ICommand` & `IUndoStack` *(Core/History)*
|
||||
Already shown in section 8. Each undoable user action is one `ICommand`; the stack is bounded.
|
||||
|
||||
#### `IGalleryService` *(Core/Gallery)*
|
||||
Persistent store of saved artwork PNGs.
|
||||
```csharp
|
||||
public interface IGalleryService {
|
||||
UniTask<SavedArtworkDTO> SaveAsync(byte[] png, string templateId);
|
||||
UniTask<IReadOnlyList<SavedArtworkDTO>> ListAsync(); // sorted newest first
|
||||
UniTask<Texture2D> LoadFullAsync(string artworkId); // for fullscreen view
|
||||
UniTask<Texture2D> LoadThumbnailAsync(string artworkId);
|
||||
UniTask DeleteAsync(string artworkId);
|
||||
}
|
||||
```
|
||||
|
||||
#### `ICaptureService` *(Core/Capture)*
|
||||
Snapshots the artwork camera to a PNG blob.
|
||||
```csharp
|
||||
public interface ICaptureService {
|
||||
UniTask<byte[]> CaptureAsync(
|
||||
Camera artCamera,
|
||||
Sprite paperBackground,
|
||||
int width = 2048,
|
||||
int height = 2048);
|
||||
}
|
||||
```
|
||||
|
||||
#### `IProgressionService` *(Core/Progression)*
|
||||
Tracks which templates the child has completed and what they last opened.
|
||||
```csharp
|
||||
public interface IProgressionService {
|
||||
UniTask LoadAsync();
|
||||
UniTask SaveAsync();
|
||||
IReadOnlyCollection<string> CompletedTemplateIds { get; }
|
||||
string LastOpenedTemplateId { get; }
|
||||
void MarkCompleted(string templateId);
|
||||
void SetLastOpened(string templateId);
|
||||
}
|
||||
```
|
||||
|
||||
#### `IAssetProviderService` *(Core/Assets)*
|
||||
Addressables wrapper. Hides handle bookkeeping from features.
|
||||
```csharp
|
||||
public interface IAssetProviderService {
|
||||
UniTask InitializeAsync();
|
||||
UniTask<T> LoadAsync<T>(string address) where T : UnityEngine.Object;
|
||||
UniTask<IReadOnlyList<T>> LoadByLabelAsync<T>(string label) where T : UnityEngine.Object;
|
||||
void Release(string address);
|
||||
void ReleaseAll();
|
||||
}
|
||||
```
|
||||
|
||||
#### `IEventBus` *(Libs/EventBus, also referenced from Core)*
|
||||
```csharp
|
||||
public interface IEventBus {
|
||||
void Publish<T>(T signal) where T : struct;
|
||||
IDisposable Subscribe<T>(Action<T> handler) where T : struct;
|
||||
}
|
||||
```
|
||||
Signals are structs to avoid GC. Disposable subscription so presenters can unsubscribe in `Dispose()`.
|
||||
|
||||
---
|
||||
|
||||
### 32.2 Services Layer
|
||||
|
||||
Concrete infrastructure. One implementation each. All singletons in `RootLifetimeScope`.
|
||||
|
||||
#### `AddressableAssetProviderService` *(Services/Assets)*
|
||||
Implements `IAssetProviderService`.
|
||||
- **Responsibility:** Wrap `Addressables.LoadAssetAsync<T>` and ref-count handles by address.
|
||||
- **State:** `Dictionary<string, AsyncOperationHandle>` keyed by address.
|
||||
- **Notes:** `Release(address)` decrements; `ReleaseAll()` for scene teardown. Initialization must complete before any other service may load.
|
||||
|
||||
#### `FileGalleryService` *(Services/Gallery)*
|
||||
Implements `IGalleryService`.
|
||||
```csharp
|
||||
// fields:
|
||||
// IPathProvider _paths (wraps Application.persistentDataPath for tests)
|
||||
// IThumbnailGenerator _thumb (downscale + encode)
|
||||
// IEventBus _bus
|
||||
// pub: ArtworkSavedSignal, ArtworkDeletedSignal
|
||||
```
|
||||
- **Save flow:** write `{guid}.png.tmp` → fsync → rename; generate thumbnail on a worker; write sidecar JSON last (so partial saves are detectable by absence of JSON).
|
||||
- **List flow:** enumerate `*.json` in `Gallery/`, deserialize, sort by `CreatedUtc desc`.
|
||||
- **Delete flow:** delete png + thumb + json; missing files ignored (idempotent).
|
||||
|
||||
#### `RenderTextureCaptureService` *(Services/Capture)*
|
||||
Implements `ICaptureService`.
|
||||
- **Steps:** allocate `RenderTexture(width, height, 0, ARGB32)` → bind to `artCamera.targetTexture` → `artCamera.Render()` → `ReadPixels` into `Texture2D` → composite `paperBackground` underneath (single shader blit) → `EncodeToPNG` → release RT + textures.
|
||||
- **Threading:** PNG encode happens on a `UniTask.RunOnThreadPool` to avoid hitching the main thread on tablets.
|
||||
- **Sizing:** default 2048², overridable. Capped at device max texture size.
|
||||
|
||||
#### `JsonPersistenceService` *(Services/Persistence)*
|
||||
Implements `IPersistenceService` (small JSON blob; not the gallery).
|
||||
```csharp
|
||||
public interface IPersistenceService {
|
||||
UniTask<T> LoadAsync<T>(string key) where T : class, new();
|
||||
UniTask SaveAsync<T>(string key, T value);
|
||||
}
|
||||
```
|
||||
- **Path:** `Application.persistentDataPath/save.json`.
|
||||
- **Format:** single JSON object keyed by `key` so multiple services can share one file.
|
||||
- **Atomicity:** write to `save.json.tmp` → rename.
|
||||
|
||||
#### `SceneService` *(Services/Scenes)*
|
||||
Implements `ISceneService`. Wraps `SceneManager.LoadSceneAsync` with `UniTask` plus a fade-curtain.
|
||||
```csharp
|
||||
public interface ISceneService {
|
||||
UniTask LoadAsync(string sceneName, LoadSceneMode mode = LoadSceneMode.Single);
|
||||
UniTask UnloadAsync(string sceneName);
|
||||
}
|
||||
```
|
||||
|
||||
#### `AudioService` *(Services/Audio)*
|
||||
Implements `IAudioService`. Plays SFX clips loaded by address, mixes via Unity AudioMixer groups.
|
||||
```csharp
|
||||
public interface IAudioService {
|
||||
UniTask PreloadAsync(string label); // e.g. "sfx,ui"
|
||||
void PlayOneShot(string clipId, float volume = 1f);
|
||||
void SetCategoryVolume(AudioCategory cat, float v01);
|
||||
}
|
||||
```
|
||||
Holds an internal `Dictionary<string, AudioClip>` populated at preload.
|
||||
|
||||
#### `InputReaderSO` *(Services/Inputs)*
|
||||
ScriptableObject wrapping the new Input System; exposes events.
|
||||
```csharp
|
||||
public interface IInputReader {
|
||||
event Action<Vector2> PointerDown; // world pos
|
||||
event Action<Vector2> PointerDrag;
|
||||
event Action<Vector2> PointerUp;
|
||||
}
|
||||
```
|
||||
- **Why an SO:** assignable in inspector and survives scene loads, but still resolvable via DI (`builder.RegisterInstance(_inputReader).As<IInputReader>()`).
|
||||
|
||||
---
|
||||
|
||||
### 32.3 Libs
|
||||
|
||||
Generic, project-agnostic utilities.
|
||||
|
||||
#### `BoundedUndoStack` *(Libs/CommandStack)*
|
||||
Implements `IUndoStack`. Source already in section 24.
|
||||
- **Capacity:** default 20.
|
||||
- **Invariant:** `_redo` cleared on any new `Push`.
|
||||
- **Edge cases:** `Undo`/`Redo` on empty stack is a no-op (never throws).
|
||||
|
||||
#### `EventBus` *(Libs/EventBus)*
|
||||
Implements `IEventBus` with a `Dictionary<Type, Delegate>` of `Action<T>` per signal type.
|
||||
- **Subscribe** returns an `IDisposable` that removes the handler on `Dispose`.
|
||||
- **Publish** snapshots the invocation list before iterating (so handlers may safely unsubscribe during dispatch).
|
||||
|
||||
#### `Fsm<TState>` *(Libs/FSM)*
|
||||
Generic state machine used by `ColorBookFlowController`.
|
||||
```csharp
|
||||
public sealed class Fsm<TState> where TState : struct, Enum {
|
||||
public TState Current { get; }
|
||||
public event Action<TState, TState> Transitioned;
|
||||
public void Bind(TState state, IFsmState handler);
|
||||
public void Go(TState next); // calls Exit on old, Enter on new
|
||||
}
|
||||
|
||||
public interface IFsmState { void Enter(); void Exit(); }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 32.4 Feature — `DrawingCatalog`
|
||||
|
||||
#### `DrawingCatalogController` *(Systems)*
|
||||
Headless logic. Owns the list of template IDs visible in the grid.
|
||||
```csharp
|
||||
// fields: IDrawingTemplateCatalog _catalog, IEventBus _bus
|
||||
public sealed class DrawingCatalogController : IAsyncStartable {
|
||||
public IReadOnlyList<string> VisibleIds { get; }
|
||||
public event Action ListChanged;
|
||||
public UniTask StartAsync(CancellationToken ct); // pulls catalog, refreshes list
|
||||
public void OnTemplateSelected(string id); // bus.Publish(new DrawingSelectedSignal(id))
|
||||
}
|
||||
// pub: DrawingSelectedSignal
|
||||
```
|
||||
|
||||
#### `DrawingCatalogPresenter` *(UI)*
|
||||
Bridges controller ↔ view.
|
||||
```csharp
|
||||
// fields: IDrawingCatalogView _view, DrawingCatalogController _ctrl, IDrawingTemplateCatalog _catalog
|
||||
public sealed class DrawingCatalogPresenter : IStartable, IDisposable {
|
||||
public void Start(); // wires view.OnItemClicked → _ctrl.OnTemplateSelected
|
||||
public void Dispose();
|
||||
}
|
||||
```
|
||||
- **Thumbnail load:** `_catalog.GetThumbnailAsync(id)` per visible cell, with placeholder while loading.
|
||||
|
||||
#### `DrawingCatalogView : MonoBehaviour` *(UI)*
|
||||
Implements `IDrawingCatalogView`. Pure setters + click event.
|
||||
```csharp
|
||||
public interface IDrawingCatalogView {
|
||||
event Action<string> OnItemClicked;
|
||||
void SetItems(IReadOnlyList<CatalogItemVM> items); // vm = id + Sprite thumbnail
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 32.5 Feature — `ShapeBuilder`
|
||||
|
||||
#### `ShapeBuilderController` *(Systems)*
|
||||
Spawns shape pieces for the selected template, tracks snap progress, fires `ShapeAssembledSignal` when complete.
|
||||
```csharp
|
||||
// fields: IDrawingTemplateCatalog _catalog, ShapePieceFactory _factory, IEventBus _bus, ShapeBuilderConfig _cfg
|
||||
public sealed class ShapeBuilderController : IDisposable {
|
||||
public IReadOnlyList<ShapePieceView> Active { get; }
|
||||
public UniTask BuildAsync(string templateId); // load template, spawn pieces in tray
|
||||
public void Reset(); // clear, unsubscribe
|
||||
}
|
||||
// sub: DrawingSelectedSignal
|
||||
// pub: ShapeAssembledSignal
|
||||
```
|
||||
- **Internal:** counts `PieceSnappedSignal` against expected piece count.
|
||||
|
||||
#### `ShapePieceView : MonoBehaviour` *(Views)*
|
||||
World-space draggable sprite with collider. Source for snap-or-return logic shown in section 26.
|
||||
```csharp
|
||||
public sealed class ShapePieceView : MonoBehaviour {
|
||||
public string PieceId { get; }
|
||||
public bool IsLocked { get; }
|
||||
public event Action<string> Snapped; // raised when piece locks into slot
|
||||
public void Initialize(ShapePieceDTO dto, IInputReader input, IAudioService audio);
|
||||
}
|
||||
```
|
||||
- **No public mutators** for position once locked — controller treats `IsLocked` as the source of truth.
|
||||
|
||||
#### `ShapePieceFactory` *(Systems)*
|
||||
Instantiates `ShapePieceView` prefabs from a pool. Avoids re-instantiating across "Next" cycles on the same template family.
|
||||
```csharp
|
||||
public sealed class ShapePieceFactory {
|
||||
public ShapePieceView Spawn(ShapePieceDTO dto, Transform parent);
|
||||
public void Despawn(ShapePieceView view);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 32.6 Feature — `Coloring`
|
||||
|
||||
#### `ColoringStateRepository` *(Repository)*
|
||||
In-memory model. Owns "currently selected color" and the palette in use.
|
||||
```csharp
|
||||
public sealed class ColoringStateRepository {
|
||||
public IColorPalette Palette { get; private set; }
|
||||
public int SelectedIndex { get; private set; }
|
||||
public Color CurrentColor => Palette.Colors[SelectedIndex];
|
||||
public event Action<int> SelectedIndexChanged;
|
||||
public void SetPalette(IColorPalette palette); // resets SelectedIndex to 0
|
||||
public void SelectColor(int index);
|
||||
}
|
||||
```
|
||||
- **Why a repository:** presenter and controller both need to read/write current color; an event-emitting POCO is simpler than wiring two signals.
|
||||
|
||||
#### `ColoringController` *(Systems)* — implements `IColoringController`
|
||||
Builds and pushes `PaintRegionCommand` instances; spawns `ColorRegionView` per region.
|
||||
```csharp
|
||||
// fields: IUndoStack _undo, ColoringStateRepository _state, ColorRegionFactory _factory, IEventBus _bus
|
||||
public interface IColoringController {
|
||||
UniTask SpawnRegionsAsync(IDrawingTemplate template);
|
||||
void PaintRegion(ColorRegionView view); // builds command, pushes to undo stack
|
||||
void Clear();
|
||||
}
|
||||
// sub: ShapeAssembledSignal (via flow controller, not direct)
|
||||
// pub: ColorAppliedSignal (via PaintRegionCommand)
|
||||
```
|
||||
|
||||
#### `ColorRegionView : MonoBehaviour` *(Views)*
|
||||
Sprite + `PolygonCollider2D`, on `Artwork` layer. Tapped via `Physics2D.OverlapPoint` from `ColoringInputBinder`.
|
||||
```csharp
|
||||
public sealed class ColorRegionView : MonoBehaviour {
|
||||
public string RegionId { get; }
|
||||
public Color Color { get; } // current paint
|
||||
public void Initialize(ColorRegionDTO dto);
|
||||
public void SetColor(Color c); // setter only; no logic
|
||||
}
|
||||
```
|
||||
|
||||
#### `ColoringInputBinder` *(Systems)* — `IStartable, IDisposable`
|
||||
Subscribes to `IInputReader.PointerDown`, raycasts on the `Artwork` layer mask, calls `ColoringController.PaintRegion(view)` on hit.
|
||||
|
||||
#### `PaintRegionCommand` *(Commands)*
|
||||
Source in section 23. Holds `view`, `fromColor`, `toColor`, `bus`. Symmetrical execute/undo.
|
||||
|
||||
#### `ColorPaletteView`, `ColorPalettePresenter` *(UI)*
|
||||
Sources in section 25. Presenter binds `ColoringStateRepository.SelectedIndexChanged` ↔ `IColorPaletteView`.
|
||||
|
||||
#### `ColorRegionFactory` *(Systems)*
|
||||
Mirror of `ShapePieceFactory` for regions. Pool-friendly.
|
||||
|
||||
---
|
||||
|
||||
### 32.7 Feature — `History`
|
||||
|
||||
#### `HistoryController` *(Systems)* — `IStartable, IDisposable`
|
||||
Owns the per-session `IUndoStack` (registered scoped, so a new ColorBook scene = new stack).
|
||||
```csharp
|
||||
// fields: IUndoStack _stack, IEventBus _bus
|
||||
public sealed class HistoryController : IStartable, IDisposable {
|
||||
public bool CanUndo => _stack.CanUndo;
|
||||
public bool CanRedo => _stack.CanRedo;
|
||||
public event Action StateChanged;
|
||||
public void Undo(); // _stack.Undo() + StateChanged
|
||||
public void Redo();
|
||||
// sub: DrawingSelectedSignal → _stack.Clear()
|
||||
}
|
||||
```
|
||||
|
||||
#### `HistoryButtonsView : MonoBehaviour` *(UI)*
|
||||
Two big arrow buttons. Setters only.
|
||||
```csharp
|
||||
public interface IHistoryButtonsView {
|
||||
event Action UndoClicked;
|
||||
event Action RedoClicked;
|
||||
void SetUndoEnabled(bool enabled);
|
||||
void SetRedoEnabled(bool enabled);
|
||||
}
|
||||
```
|
||||
|
||||
#### `HistoryPresenter` *(UI)*
|
||||
Wires controller `StateChanged` ↔ view enable/disable; view click events → controller.
|
||||
|
||||
---
|
||||
|
||||
### 32.8 Feature — `Capture`
|
||||
|
||||
#### `CaptureController` *(Systems)*
|
||||
The orchestrator behind the "Capture" button. Stateless other than guarding against concurrent captures.
|
||||
```csharp
|
||||
// fields: ICaptureService _capture, IGalleryService _gallery, IEventBus _bus, ColorBookSceneRefs _refs
|
||||
public sealed class CaptureController {
|
||||
public bool IsCapturing { get; }
|
||||
public UniTask<SavedArtworkDTO> CaptureCurrentAsync(string templateId, Sprite paperBg);
|
||||
}
|
||||
// pub: ArtworkCapturedSignal (mid-flow), ArtworkSavedSignal (post-save)
|
||||
```
|
||||
- **Concurrency:** sets `IsCapturing = true` on entry; UI binds button enabled to `!IsCapturing` to prevent double-tap.
|
||||
|
||||
#### `CaptureButtonPresenter` *(UI)*
|
||||
Wires button click → `CaptureController.CaptureCurrentAsync`. Disables button while in progress. Shows toast on `ArtworkSavedSignal`.
|
||||
|
||||
---
|
||||
|
||||
### 32.9 Feature — `Progression`
|
||||
|
||||
#### `ProgressionService` *(Systems)* — implements `IProgressionService`
|
||||
The only place that knows what "completed" means.
|
||||
- **Persistence:** delegates to `IPersistenceService` under key `"progression"`.
|
||||
- **Load order:** `AppBoot` calls `LoadAsync()` early.
|
||||
- **Save trigger:** after `MarkCompleted`, debounced 500 ms to coalesce a burst of "Next" presses.
|
||||
|
||||
#### `ProgressionRepository` *(Repository)*
|
||||
Pure in-memory holder used by the service. Separated so tests can inspect state without going through file IO.
|
||||
|
||||
---
|
||||
|
||||
### 32.10 Feature — `ColorBookFlow`
|
||||
|
||||
#### `ColorBookFlowController` *(Systems)* — `IStartable, IDisposable`
|
||||
**The only orchestrator inside the ColorBook scene.** Drives the panel FSM: `Catalog → Building → Coloring → Done`.
|
||||
```csharp
|
||||
// fields:
|
||||
// IEventBus _bus
|
||||
// IDrawingTemplateCatalog _catalog
|
||||
// ShapeBuilderController _builder
|
||||
// IColoringController _coloring
|
||||
// CaptureController _capture
|
||||
// IProgressionService _progression
|
||||
// ColorBookSceneRefs _refs (panel roots to enable/disable)
|
||||
// Fsm<ColorBookState> _fsm
|
||||
```
|
||||
- **State table:**
|
||||
|
||||
| State | On enter | Triggers exit |
|
||||
|------------|-----------------------------------------------|--------------------------------|
|
||||
| `Catalog` | Show catalog panel | `DrawingSelectedSignal` |
|
||||
| `Building` | `_builder.BuildAsync(id)` | `ShapeAssembledSignal` |
|
||||
| `Coloring` | `_coloring.SpawnRegionsAsync(template)` | "Next" or "Capture" pressed |
|
||||
| `Done` | Run autosave capture, mark completed, `Go(Catalog)` for next | always advances |
|
||||
|
||||
- **"Next" sequence:** `_capture.CaptureCurrentAsync` → `_progression.MarkCompleted` → `_catalog.Release(current)` → `_catalog.LoadAsync(_catalog.NextUnseen(current))` → re-enter `Building`.
|
||||
|
||||
---
|
||||
|
||||
### 32.11 Feature — `ArtBook`
|
||||
|
||||
#### `GalleryPresenter` *(UI)* — `IAsyncStartable, IDisposable`
|
||||
Lists artworks, opens fullscreen view, deletes, shares.
|
||||
```csharp
|
||||
// fields: IGalleryService _gallery, IGalleryView _view, IExternalShareService _share, IEventBus _bus
|
||||
```
|
||||
- **Start:** `_gallery.ListAsync()` → `_view.SetItems(...)`.
|
||||
- **Subscribes** to `ArtworkSavedSignal` to live-refresh if the user pops back in.
|
||||
|
||||
#### `IGalleryView` *(UI)*
|
||||
```csharp
|
||||
public interface IGalleryView {
|
||||
event Action<string> OnArtworkTapped;
|
||||
event Action<string> OnDeleteRequested;
|
||||
event Action<string> OnShareRequested;
|
||||
void SetItems(IReadOnlyList<GalleryItemVM> items);
|
||||
void ShowFullscreen(Texture2D full);
|
||||
void HideFullscreen();
|
||||
}
|
||||
```
|
||||
|
||||
#### `IExternalShareService` *(Core)*
|
||||
Platform plugin shim (iOS Photos / Android MediaStore).
|
||||
```csharp
|
||||
public interface IExternalShareService {
|
||||
UniTask SaveToCameraRollAsync(byte[] png);
|
||||
UniTask ShareAsync(byte[] png, string subject);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 32.12 App Layer
|
||||
|
||||
#### `AppBoot` *(App/Boot)* — `IAsyncStartable`
|
||||
Single entry point. Steps in section 29.
|
||||
```csharp
|
||||
// fields: IAssetProviderService _assets, IPersistenceService _persist, IProgressionService _progress,
|
||||
// IAudioService _audio, ISceneService _scenes, BootConfig _cfg
|
||||
public sealed class AppBoot : IAsyncStartable {
|
||||
public UniTask StartAsync(CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
#### LifetimeScopes
|
||||
- `RootLifetimeScope` — section 21. Registers all services + `IEventBus`. Persists for app lifetime.
|
||||
- `MainMenuLifetimeScope` — registers `MainMenuPresenter` and view.
|
||||
- `ColorBookLifetimeScope` — section 21. Registers feature installers + `ColorBookFlowController` as entry point.
|
||||
- `ArtBookLifetimeScope` — registers `GalleryPresenter` + view + `IExternalShareService`.
|
||||
|
||||
All scope classes are thin: serialized fields for scene refs, `Configure(IContainerBuilder)` only.
|
||||
|
||||
---
|
||||
|
||||
### 32.13 Cross-cutting types
|
||||
|
||||
#### `ColorBookSceneRefs : MonoBehaviour` *(App)*
|
||||
Aggregates all scene-bound Unity references that features need: `Camera artCamera`, `Transform catalogRoot`, `Transform builderRoot`, `Transform coloringRoot`, `RectTransform hudRoot`, `ColorPaletteView paletteView`, `HistoryButtonsView historyView`. Registered as a singleton in `ColorBookLifetimeScope` so features don't `Find` things.
|
||||
|
||||
#### `IInstaller` *(App)*
|
||||
```csharp
|
||||
public interface IInstaller { void Install(IContainerBuilder builder); }
|
||||
```
|
||||
Implemented as `ScriptableObject` per feature so scopes can drag them in the inspector (section 22).
|
||||
|
||||
---
|
||||
|
||||
### 32.14 Class summary table
|
||||
|
||||
| Class | Layer | Role | Key dependencies |
|
||||
|---|---|---|---|
|
||||
| `AppBoot` | App | Startup sequencer | assets, persist, progression, scenes |
|
||||
| `RootLifetimeScope` | App | Root DI | configs |
|
||||
| `ColorBookLifetimeScope` | App | Scene DI | scene refs, installers |
|
||||
| `DrawingCatalogController` | Feature | Grid logic | catalog, bus |
|
||||
| `DrawingCatalogPresenter` | Feature | UI bridge | view, controller, catalog |
|
||||
| `ShapeBuilderController` | Feature | Piece spawn + snap tracking | catalog, factory, bus, cfg |
|
||||
| `ShapePieceView` | Feature | Draggable piece MB | input, audio |
|
||||
| `ColoringStateRepository` | Feature | Current color model | — |
|
||||
| `ColoringController` | Feature | Region spawn + paint cmd | undo, state, factory, bus |
|
||||
| `ColorRegionView` | Feature | Region sprite MB | — |
|
||||
| `PaintRegionCommand` | Feature | Undoable paint | view, bus |
|
||||
| `HistoryController` | Feature | Undo/redo facade | undo stack, bus |
|
||||
| `CaptureController` | Feature | Capture+save orchestration | capture svc, gallery, bus, refs |
|
||||
| `ColorBookFlowController` | Feature | Scene FSM | bus, catalog, builder, coloring, capture, progression |
|
||||
| `GalleryPresenter` | Feature | Art book listing | gallery, share, view, bus |
|
||||
| `BoundedUndoStack` | Lib | Capped undo store | — |
|
||||
| `EventBus` | Lib | Pub/sub | — |
|
||||
| `Fsm<TState>` | Lib | Generic FSM | — |
|
||||
| `AddressableAssetProviderService` | Service | Addressables wrapper | — |
|
||||
| `FileGalleryService` | Service | Gallery file IO | paths, thumb gen, bus |
|
||||
| `RenderTextureCaptureService` | Service | PNG render | — |
|
||||
| `JsonPersistenceService` | Service | Settings/progression IO | — |
|
||||
| `SceneService` | Service | Async scene loads | — |
|
||||
| `AudioService` | Service | SFX playback | assets |
|
||||
| `ProgressionService` | Service | Completion tracking | persistence |
|
||||
|
||||
If you add a class not in this table, add it here in the same PR. This table is the cheap mental-model index — keep it honest.
|
||||
|
||||
Reference in New Issue
Block a user