Basic Setup For Shape Builder
This commit is contained in:
755
Readme.md
755
Readme.md
@@ -8,23 +8,70 @@ This document is the canonical reference for the Color Book game's structure. Th
|
||||
|
||||
## 1. Game Flow
|
||||
|
||||
Four scenes. Each gets its own scope. Root services persist across all of them.
|
||||
|
||||
```
|
||||
App launch
|
||||
└─ Boot scene (RootLifetimeScope)
|
||||
└─ MainMenu scene (Spine mascot, Play button)
|
||||
└─ Press "Play" → ColorBook scene
|
||||
├─ Drawing catalog (grid of templates)
|
||||
├─ Select drawing
|
||||
├─ Shape Builder panel (drag pieces → snap to slots)
|
||||
├─ ↓ on assembly complete
|
||||
├─ Color panel (tap color → tap region)
|
||||
├─ Undo / Redo any time
|
||||
├─ "Save" → screenshot via CaptureCamera → native gallery plugin
|
||||
│ saves PNG to phone's Photos album. Toast confirmation.
|
||||
└─ "Next" → auto-save + load next drawing
|
||||
┌─────────────────┐
|
||||
│ Boot.unity │ RootLifetimeScope — services + cross-scene singletons
|
||||
│ │ AppBoot: Addressables.Init → Progression.Load → Catalog.Init
|
||||
└────────┬────────┘
|
||||
│ Scenes.LoadAsync("MainMenu")
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ MainMenu.unity │ Spine mascot looping idle. Single "Play" button.
|
||||
│ │ Tap → SetLastOpened(null) → load Colorbook
|
||||
└────────┬────────┘
|
||||
│ Scenes.LoadAsync("Colorbook")
|
||||
▼
|
||||
┌─────────────────┐ ◀─────────────────────────┐
|
||||
│ Colorbook.unity │ Catalog grid of drawings. │ (back from Gameplay returns here;
|
||||
│ │ Each cell shows: cached │ catalog cells refresh with cached
|
||||
│ │ user thumbnail if any, │ thumbnails written during gameplay)
|
||||
│ │ else DefaultThumbnail. │
|
||||
│ │ Tap cell → │
|
||||
│ │ Progression.SetLastOpened(id) → load Gameplay
|
||||
└────────┬────────┘ │
|
||||
│ Scenes.LoadAsync("Gameplay") │
|
||||
▼ │
|
||||
┌─────────────────┐ │
|
||||
│ Gameplay.unity │ Active drawing experience │
|
||||
│ │ │
|
||||
│ Reads Progression.LastOpenedTemplateId │
|
||||
│ Reads Progression.GetProgress(id) → null, │
|
||||
│ Building, or Coloring │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ GameplayState.Building │ │
|
||||
│ │ • Pieces in tray, drag → snap │ │
|
||||
│ │ • Pre-snapped pieces auto-locked │ │
|
||||
│ │ if resuming │ │
|
||||
│ │ • Back tap → save partial state ─┼────┤
|
||||
│ │ + load Colorbook │ │
|
||||
│ └────────────────┬─────────────────────┘ │
|
||||
│ │ ShapeAssembledSignal │
|
||||
│ │ (save phase + thumb) │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ GameplayState.Coloring │ │
|
||||
│ │ • Tap color → tap region → paint │ │
|
||||
│ │ • Undo / Redo any time │ │
|
||||
│ │ • Autosave (debounced 500 ms) │ │
|
||||
│ │ • Save tap → capture + native ─┼────┐
|
||||
│ │ Photos save + cache thumb │ │
|
||||
│ │ • Next tap → save + mark complete │ │
|
||||
│ │ + advance to next drawing ──────┼─┐ │
|
||||
│ │ • Back tap → save + load Colorbook─┼────┤
|
||||
│ └──────────────────────────────────────┘ │ │
|
||||
│ │ │
|
||||
└────────────────────────────────────────────┼──┘
|
||||
│
|
||||
┌──────────────────────────┘
|
||||
│ AdvanceToNextDrawing:
|
||||
│ Catalog.NextUnseen(currentId) → reload Gameplay
|
||||
└─ stays in Gameplay.unity, no scene transition
|
||||
```
|
||||
|
||||
The user views their captured drawings inside the phone's native **Photos** app — there is no in-app gallery viewer. Capture and gallery-save are two independent services: `ICaptureService` produces PNG bytes; `IGalleryService` is a thin shim over a native plugin that writes those bytes into the device's photo library.
|
||||
The user views captured drawings inside the phone's native **Photos** app — there is no in-app gallery viewer. `ICaptureService` produces PNG bytes; `IGalleryService` is a thin shim over a native plugin that writes those bytes into the device's photo library. The Save System (§13) decides *when* to capture and save.
|
||||
|
||||
---
|
||||
|
||||
@@ -255,24 +302,30 @@ New asmdefs follow the same convention: `Services.Capture`, `Services.Gallery`,
|
||||
|
||||
## 6. Scenes & Lifetime Scopes
|
||||
|
||||
Four scenes, each with its own scope. Root scope persists across all scene changes.
|
||||
|
||||
| Scene | Scope | Status | Contents |
|
||||
|---|---|---|---|
|
||||
| `Boot.unity` | `RootLifetimeScope` | ✅ exists | All Services + Libs. Persists forever. |
|
||||
| `MainMenu.unity` | `MainMenuLifetimeScope` | ⚠️ planned | Spine mascot, Play button. |
|
||||
| `ColorBook.unity` | `ColorBookLifetimeScope` | ⚠️ planned | DrawingCatalog, ShapeBuilder, Coloring, History, Capture, ColorBookFlow. |
|
||||
| `Boot.unity` | `RootLifetimeScope` | ✅ exists | All Services + Libs + cross-scene singletons (`IProgressionSystem`, `IDrawingTemplateCatalog`). Persists forever. |
|
||||
| `MainMenu.unity` | `MainMenuLifetimeScope` | ⚠️ planned | Spine mascot, single "Play" button. |
|
||||
| `Colorbook.unity` | `ColorbookLifetimeScope` | ⚠️ planned | `ColorbookFlow` + `DrawingCatalog`. Catalog grid where the player picks a drawing. |
|
||||
| `Gameplay.unity` | `GameplayLifetimeScope` | ⚠️ scene exists, scope empty | `ShapeBuilder` + `Coloring` + `History` + `Capture` (the feature wrapper) + `GameplayFlow`. The active drawing experience. |
|
||||
|
||||
Only `Boot.unity` exists today; the two scene scope classes haven't been written yet either (only `RootLifetimeScope` exists in [App/LifetimeScopes/](Assets/Darkmatter/Code/App/LifetimeScopes/)).
|
||||
Only `Boot.unity` and an empty `Gameplay.unity` exist today; the four scene scope classes need full implementation (only `RootLifetimeScope` is functional).
|
||||
|
||||
Scopes nest: `Root → (MainMenu | ColorBook)`. Services resolved from the root parent. Scene scopes only register their own features. There is **no in-app gallery** — captured drawings go to the phone's native Photos app via the gallery service.
|
||||
Scopes nest: `Root → (MainMenu | Colorbook | Gameplay)`. Services and cross-scene features (Progression, DrawingTemplate) resolve from the root parent. Scene scopes only register their own features.
|
||||
|
||||
**No in-app gallery** — captured drawings go to the phone's native Photos app via `IGalleryService`. The catalog grid in `Colorbook.unity` shows the user's progress by reading cached thumbnails from `IProgressionSystem`.
|
||||
|
||||
### Boot chain (planned)
|
||||
|
||||
No `AppBoot` class exists yet — today `RootLifetimeScope` only registers services and stops there. When `AppBoot` is added (as an `IAsyncStartable` registered via `builder.RegisterEntryPoint<AppBoot>()`), it should run once, in order:
|
||||
No `AppBoot` class exists yet — today `RootLifetimeScope` only registers services and stops. When `AppBoot` is added (as an `IAsyncStartable` registered via `RootLifetimeScope`), it should run once, in order:
|
||||
|
||||
1. Initialize `IAssetProviderService` (Addressables init).
|
||||
2. Preload essential bundles (palettes, UI sounds).
|
||||
3. Load `IProgressionService` from disk.
|
||||
4. Load `MainMenu` scene.
|
||||
1. `IAssetProviderService.InitializeAsync()` — Addressables init.
|
||||
2. `IProgressionSystem.LoadAsync()` — hydrate per-template state from PlayerPrefs.
|
||||
3. `IDrawingTemplateCatalog.InitializeAsync()` — batch-load all `DrawingTemplateSO`s by Addressables label.
|
||||
4. Optional: preload UI sounds, palette assets.
|
||||
5. `ISceneService.LoadAsync(MainMenu)`.
|
||||
|
||||
Failures show a child-friendly retry screen; never crash.
|
||||
|
||||
@@ -563,54 +616,55 @@ public readonly struct PaperSavedSignal {
|
||||
|
||||
### `ShapeBuilder`
|
||||
|
||||
- Listens to `DrawingSelectedSignal`.
|
||||
- Loads template, instantiates the single piece prefab once per `ShapeSO` in the template, parents under the HUD tray panel (`ColorBookSceneRefs.TrayPanel`). Each instance is `Assign(shape)`ed to its `ShapeSO`.
|
||||
- `SlotMarker`s in the drawing's per-drawing prefab (under `ColorBookSceneRefs.SlotsParent`) provide target poses + matching `ShapeSO` refs.
|
||||
- Each piece has `IBeginDragHandler` / `IDragHandler` / `IEndDragHandler` plus a per-piece `ShapePieceFsm`. Drag updates `RectTransform.anchoredPosition` directly from `PointerEventData`, converted to canvas-local via `RectTransformUtility.ScreenPointToLocalPointInRectangle`.
|
||||
- On entering preview radius of the matching slot: reactive `Lerp` of `anchoredPosition` / `sizeDelta` / `localRotation` toward `SlotMarker`'s `RectTransform`. Drives off pointer distance, not time.
|
||||
- On `OnEndDrag` inside snap radius: piece reparents to `ColorBookSceneRefs.PiecesParent`, DOTween ease-out to exact slot pose, disable input. Otherwise DOTween back to tray slot.
|
||||
- Fires `ShapeAssembledSignal` when all pieces locked.
|
||||
- Listens to `DrawingSelectedSignal` (raised by the Colorbook scene before scene change; resume reads `LastOpenedTemplateId` in Gameplay scope startup).
|
||||
- Loads the per-drawing prefab via `IDrawingTemplateCatalog`, instantiates it under `GameplaySceneRefs.PaperRoot`. The prefab carries the `SlotMarker`s at their authored poses.
|
||||
- Spawns one **`ShapePiece` MonoBehaviour** per `ShapeSO` in the template via `Instantiate(piecePrefab, tray)` and calls `piece.Setup(shape, slot, cfg, sfx, bus, trayPos, preSnapped)`. If `progress.Phase == ShapeBuilding`, pieces in `progress.SnappedPieces` are pre-snapped (start locked).
|
||||
- `ShapePiece` is a single MB handling all three behaviors inline: drag (Unity UI `IBeginDrag / IDrag / IEndDrag`), reactive preview lerp when within `cfg.PreviewRadius`, snap (PrimeTween — `Tween.UIAnchoredPosition` / `UISizeDelta` / `LocalRotation`) on release inside `cfg.SnapRadius`, otherwise tween back to tray. No FSM, no factory — just the MB.
|
||||
- Publishes `PieceSnappedSignal(pieceId)` on lock. Controller counts against expected; fires `ShapeAssembledSignal(templateId)` when all locked.
|
||||
|
||||
### `Coloring`
|
||||
|
||||
- Listens to `ShapeAssembledSignal`.
|
||||
- Spawns one UI `Image` per `ColorRegionDTO` under `ColorBookSceneRefs.RegionsParent`. Each region's `Image.alphaHitTestMinimumThreshold = 0.5f` so taps on transparent pixels pass through to the next region.
|
||||
- Spawns one UI `Image` per `ColorRegionDTO` under `GameplaySceneRefs.RegionsParent`. Each region's `Image.alphaHitTestMinimumThreshold = 0.5f` so taps on transparent pixels pass through to the next region below.
|
||||
- Each region has `IPointerClickHandler`. On click → `ColoringController.PaintRegion(view)`.
|
||||
- Listens to palette selection (current color held in `ColoringStateRepository`).
|
||||
- Controller builds `PaintRegionCommand(regionId, oldColor, newColor)` and pushes to `IUndoStack`.
|
||||
- Command sets `Image.color` on undo/redo.
|
||||
- Fires `ColorAppliedSignal` for SFX / sparkle effects.
|
||||
- Controller builds `PaintRegionCommand(regionId, oldColor, newColor)` and pushes to `IUndoStack`. Command sets `Image.color` on Execute/Undo.
|
||||
- Publishes `ColorAppliedSignal` for SFX / sparkle effects.
|
||||
- **Resume:** if `progress.RegionColors` is non-empty, spawned regions are initialized with those saved colors instead of `region.InitialColor`.
|
||||
- **Autosave hook:** after each `PaintRegion`, debounces 500 ms then calls `GameplayFlowController.AutosaveAsync` so the colors hit disk without thumbnail re-render. See §13.
|
||||
|
||||
### `History`
|
||||
|
||||
- Owns the scoped `IUndoStack` for the current ColorBook session.
|
||||
- Cleared on `DrawingSelectedSignal` (new drawing = fresh history).
|
||||
- Capped at 20 entries (memory + cognitive simplicity).
|
||||
- UI: two big arrow buttons; disabled state when `CanUndo / CanRedo` is false.
|
||||
- Owns the scoped `IUndoStack` for the current Gameplay session.
|
||||
- Cleared on Gameplay scope startup (new drawing = fresh history).
|
||||
- Capped at 20 entries.
|
||||
- UI: two big arrow buttons; disabled state when `CanUndo` / `CanRedo` is false.
|
||||
|
||||
### `Capture`
|
||||
|
||||
- Bound to the "Save" button (and triggered silently by "Next").
|
||||
- `CaptureController.SaveAsync(templateId)`:
|
||||
1. `_capture.CaptureAsync()` → PNG bytes (one-shot `CaptureCamera.Render()` into a temp RT)
|
||||
2. Publish `PaperCapturedSignal(templateId)`
|
||||
3. `_gallery.SaveToDeviceAsync(bytes, "Color Book")` → native plugin writes into phone's Photos
|
||||
4. Publish `PaperSavedSignal(templateId)`
|
||||
- HUD shows a brief "Saved to Photos" toast on `PaperSavedSignal`.
|
||||
- `CaptureController` is the only place that orchestrates capture-then-save. Other features never call `IGalleryService` directly.
|
||||
- Wraps `ICaptureService.CaptureAsync()` (one-shot `CaptureCamera.Render()` into a temp RT, ReadPixels, EncodeToPNG). Returns raw PNG bytes.
|
||||
- The **Capture feature does NOT decide what to do with the bytes** — `GameplayFlowController` calls it, then routes to gallery + thumbnail cache depending on the trigger. See §13.
|
||||
|
||||
### `Progression`
|
||||
|
||||
- Tracks completed template IDs and the in-progress draft.
|
||||
- On "Next" button: silently runs `CaptureController.SaveAsync`, marks current as completed, calls `IDrawingTemplateCatalog.NextUnseen()`.
|
||||
- Persists JSON via `Libs.PlayerPrefs` (`ProtectedPlayerPrefs`).
|
||||
- Single source of truth for per-template user state. Implements `IProgressionSystem`, persists `DrawingProgress` records via `Libs.PlayerPrefs` (single JSON blob under `PlayerPrefsKeys.Progression`).
|
||||
- Internally stores thumbnails per template as PNG files in `Application.persistentDataPath/thumbs/{safeId}.png` (large blobs don't belong in PlayerPrefs).
|
||||
- `ProgressionRepository` does the IO; `ProgressionSystem` keeps an in-memory cache and exposes a clean API.
|
||||
- Exposes: `GetProgress(id)`, `SaveProgressAsync(progress)`, `SaveProgressAsync(progress, png)`, `ClearProgressAsync(id)`, `IsCompleted(id)`, `CompletedTemplateIds`, `LastOpenedTemplateId / SetLastOpened`, `GetCachedThumbnailAsync(id)`.
|
||||
- See §13 for the full save matrix.
|
||||
|
||||
### `ColorBookFlow`
|
||||
### `ColorbookFlow` (in `Colorbook.unity`)
|
||||
|
||||
- The only orchestrator inside ColorBook scope.
|
||||
- Subscribes to flow-relevant signals and toggles UI panels (catalog → builder → coloring).
|
||||
- Coordinates "Next" sequence: `CaptureController.SaveAsync` → `IProgressionService.MarkCompleted` → `IDrawingTemplateCatalog.Release(currentId)` → load next.
|
||||
- Built as a small FSM (`Catalog → Building → Coloring → Done`).
|
||||
- The orchestrator for the catalog scene.
|
||||
- On scope start: calls `_drawingCatalog.InitializeAsync()` to populate the visible-IDs list.
|
||||
- Subscribes to `DrawingSelectedSignal`: `_progression.SetLastOpened(id)` + `_scenes.LoadAsync(Gameplay)`.
|
||||
|
||||
### `GameplayFlow` (in `Gameplay.unity`)
|
||||
|
||||
- The orchestrator for the active drawing scene.
|
||||
- On scope start: reads `_progression.LastOpenedTemplateId`, fetches its `DrawingProgress`, enters either Building (no progress / Phase==ShapeBuilding) or Coloring (Phase==Coloring) state.
|
||||
- Handles all save points (§13): Back button, ShapeAssembled transition, Save button, Next button, app lifecycle pause/quit.
|
||||
- Uses `Libs.FSM` (StateMachine) for `Building` ↔ `Coloring` runtime states.
|
||||
|
||||
---
|
||||
|
||||
@@ -740,6 +794,92 @@ Notes:
|
||||
|
||||
---
|
||||
|
||||
## 12b. Save System
|
||||
|
||||
Everything the user does that affects their drawing state must end up persisted. `GameplayFlowController` is the **single owner** of all save calls — feature controllers expose getters; the flow controller assembles the `DrawingProgress` record and hands it to `IProgressionSystem`.
|
||||
|
||||
### What is saved
|
||||
|
||||
| Field on `DrawingProgress` | Meaning |
|
||||
|---|---|
|
||||
| `templateId` | Which drawing this record is about |
|
||||
| `phase` | `ShapeBuilding` or `Coloring` — where to resume |
|
||||
| `snappedPieces` | Pieces locked into slots (relevant in ShapeBuilding) |
|
||||
| `regionColors` | Per-region color (relevant in Coloring) |
|
||||
| `hasThumbnail` | Whether a thumbnail PNG exists on disk for catalog display |
|
||||
| `hasBeenCompleted` | Flipped true on first Next; never flips back |
|
||||
| `completionCount` | Number of times Next was pressed (optional stats) |
|
||||
| `updatedUtcIso` / `firstCompletedUtcIso` | Timestamps (ISO 8601 strings for JsonUtility) |
|
||||
|
||||
### Save matrix
|
||||
|
||||
| Trigger | Phase saved | Snapped pieces | Region colors | Thumbnail? | Native gallery? |
|
||||
|---|---|---|---|---|---|
|
||||
| **ShapeAssembledSignal** (Building → Coloring transition) | `Coloring` | all | empty | **yes** (bare-assembled paper) | no |
|
||||
| **Each paint** (debounced 500 ms) | `Coloring` | all | current | no | no |
|
||||
| **Save button** | `Coloring` | all | current | **yes** | **yes** |
|
||||
| **Next button** | `Coloring` | all | current | **yes** | **yes** |
|
||||
| **Back button (during Building)** | `ShapeBuilding` | current | empty | no | no |
|
||||
| **Back button (during Coloring)** | `Coloring` | all | current | **yes** | no |
|
||||
| **OnApplicationPause(true) / OnApplicationQuit** | current phase | current | current | no | no |
|
||||
|
||||
Two design principles drive the matrix:
|
||||
|
||||
- **Thumbnail capture is expensive** (render + ReadPixels + PNG encode). Skip it on partial-assembly saves and per-paint autosaves. Only generate a thumbnail when the user takes an explicit save-style action.
|
||||
- **Defensive saves never block UX.** App pause/quit saves whatever is in memory without capturing — fast path, no awaitable IO holding up shutdown.
|
||||
|
||||
### `Next` adds two extras
|
||||
|
||||
- Flips `hasBeenCompleted = true` (preserves first `firstCompletedUtcIso`); increments `completionCount`.
|
||||
- Plays completion animation, then calls `AdvanceToNextDrawing` → `Catalog.NextUnseen(currentId)` → reload Gameplay scope for the new drawing.
|
||||
|
||||
### Storage layout
|
||||
|
||||
| What | Where |
|
||||
|---|---|
|
||||
| `DrawingProgress` records + `lastOpened` | One JSON blob in `ProtectedPlayerPrefs[PlayerPrefsKeys.Progression]` (see `ProgressionRootDto`) |
|
||||
| Thumbnail PNGs | `Application.persistentDataPath/thumbs/{safeId}.png` (one file per template that has a thumbnail) |
|
||||
|
||||
`safeId` replaces `/` and `\` with `_` so `animals/elephant` becomes `animals_elephant.png`.
|
||||
|
||||
### Resume / load decision
|
||||
|
||||
On Gameplay scope startup:
|
||||
|
||||
```csharp
|
||||
var id = _progression.LastOpenedTemplateId;
|
||||
var progress = _progression.GetProgress(id); // null if untouched
|
||||
|
||||
if (progress == null || progress.Value.phase == DrawingPhase.ShapeBuilding) {
|
||||
fsm.Go(Building); // spawn pieces in tray, pre-snap any in progress.snappedPieces
|
||||
} else {
|
||||
fsm.Go(Coloring); // skip ShapeBuilder; auto-snap pieces; spawn regions with progress.regionColors
|
||||
}
|
||||
```
|
||||
|
||||
### Catalog cells reflect saves automatically
|
||||
|
||||
The Colorbook scene reloads on Back. Its presenter calls `_progression.GetCachedThumbnailAsync(id)` per cell → returns the most recent save's PNG. Drawings the user touched display their progress; untouched drawings fall back to `IDrawingTemplate.DefaultThumbnail`. No live-update plumbing needed — re-entry is the refresh.
|
||||
|
||||
### Files touching the save system
|
||||
|
||||
| Path | Role |
|
||||
|---|---|
|
||||
| `Core/Contracts/Features/Progression/IProgressionSystem.cs` | Contract |
|
||||
| `Core/Data/Dynamic/Features/Progression/DrawingProgress.cs` | The struct |
|
||||
| `Core/Data/Dynamic/Features/Progression/ProgressionRootDto.cs` | JSON root (records + lastOpened) |
|
||||
| `Core/Data/Dynamic/Features/Progression/RegionColorEntry.cs` | Flattened color entry (JsonUtility-friendly) |
|
||||
| `Core/Enums/Features/Progression/DrawingPhase.cs` | Phase enum |
|
||||
| `Features/Progression/Systems/ProgressionSystem.cs` | In-memory cache + write serialization (SemaphoreSlim) |
|
||||
| `Features/Progression/Systems/ProgressionRepository.cs` | PlayerPrefs JSON + thumbnail file IO |
|
||||
| `Features/Progression/Installers/ProgressionFeatureModule.cs` | Registers `IProgressionSystem` as Singleton in Root scope |
|
||||
|
||||
### Single rule
|
||||
|
||||
> Only `GameplayFlowController` calls `_progression.SaveProgressAsync(...)`. Feature controllers expose getters; they never touch the tracker themselves. This means there is exactly one place to audit when save behavior changes.
|
||||
|
||||
---
|
||||
|
||||
## 13. Communication Rules
|
||||
|
||||
| Use case | Mechanism |
|
||||
@@ -1145,44 +1285,109 @@ Same shape repeats for every feature's UI.
|
||||
|
||||
## 26. ShapeBuilder — Snap Algorithm
|
||||
|
||||
All math is in canvas-local space — `anchoredPosition`, `sizeDelta`, `localRotation`. No world coords.
|
||||
All math is in canvas-local space — `anchoredPosition`, `sizeDelta`, `localRotation`. No world coords. Behavior lives inline in `ShapePiece : MonoBehaviour` — no FSM, no factory, no state classes. Three behaviors expressed across three Unity drag handlers.
|
||||
|
||||
### `OnDrag` — reactive preview lerp
|
||||
|
||||
```csharp
|
||||
// In ShapePieceFsm.OnDragEnd (state: Dragging or Preview):
|
||||
public void OnDragEnd() {
|
||||
var pieceRT = _ui.RectTransform;
|
||||
var slotRT = _targetSlot.RectTransform;
|
||||
var d = Vector2.Distance(pieceRT.anchoredPosition, slotRT.anchoredPosition);
|
||||
public void OnDrag(PointerEventData e)
|
||||
{
|
||||
if (_locked) return;
|
||||
|
||||
if (d <= _cfg.SnapRadius) {
|
||||
SnapToSlot();
|
||||
} else if (d <= _cfg.SnapRadius * 1.5f) {
|
||||
// Toddler grace zone — snap anyway, play happy sound
|
||||
SnapToSlot();
|
||||
_audio.PlayOneShot(SfxId.NiceTry);
|
||||
} else {
|
||||
ReturnToTrayAnimated();
|
||||
var pointerLocal = ScreenToLocal(e.position) + _grabOffset;
|
||||
var slotPos = _slot.RectTransform.anchoredPosition;
|
||||
float dist = Vector2.Distance(pointerLocal, slotPos);
|
||||
|
||||
if (dist <= _cfg.PreviewRadius)
|
||||
{
|
||||
if (!_inPreview) { _inPreview = true; _sfx.Play(SfxId.ShapeHover); }
|
||||
ApplyPreviewLerp(pointerLocal, dist);
|
||||
}
|
||||
else
|
||||
{
|
||||
_inPreview = false;
|
||||
RectTransform.anchoredPosition = pointerLocal;
|
||||
RectTransform.sizeDelta = _traySize;
|
||||
RectTransform.localRotation = Quaternion.identity;
|
||||
}
|
||||
}
|
||||
|
||||
private void SnapToSlot() {
|
||||
_ui.RectTransform.SetParent(_paper.PiecesParent, worldPositionStays: false);
|
||||
var slot = _targetSlot.RectTransform;
|
||||
_ui.RectTransform.DOAnchorPos(slot.anchoredPosition, 0.25f).SetEase(Ease.OutBack);
|
||||
_ui.RectTransform.DOSizeDelta(slot.sizeDelta, 0.25f).SetEase(Ease.OutBack);
|
||||
_ui.RectTransform.DOLocalRotateQuaternion(slot.localRotation, 0.25f).SetEase(Ease.OutBack);
|
||||
_audio.PlayOneShot(SfxId.Snap);
|
||||
_bus.Publish(new PieceSnappedSignal(_ui.PieceId));
|
||||
private void ApplyPreviewLerp(Vector2 pointerLocal, float dist)
|
||||
{
|
||||
float t = Mathf.Clamp01(1f - dist / _cfg.PreviewRadius);
|
||||
if (_cfg.PreviewCurve != null) t = _cfg.PreviewCurve.Evaluate(t);
|
||||
var slot = _slot.RectTransform;
|
||||
RectTransform.anchoredPosition = Vector2.Lerp(pointerLocal, slot.anchoredPosition, t);
|
||||
RectTransform.sizeDelta = Vector2.Lerp(_traySize, slot.sizeDelta, t);
|
||||
RectTransform.localRotation = Quaternion.Slerp(Quaternion.identity, slot.localRotation, t);
|
||||
}
|
||||
```
|
||||
|
||||
Three things to note:
|
||||
### `OnEndDrag` — snap or return
|
||||
|
||||
1. **Reparent** the piece from `TrayPanel` (HUD canvas) to `ColorBookSceneRefs.PiecesParent` (PaperCanvas) so it'll be included in capture. `worldPositionStays: false` because we want the new `anchoredPosition` to be relative to the new parent, not the world.
|
||||
2. **Three simultaneous tweens** — position, size, rotation. Use `DOAnchorPos`, `DOSizeDelta`, `DOLocalRotateQuaternion`. They start together so the piece visually snaps as one motion.
|
||||
```csharp
|
||||
public void OnEndDrag(PointerEventData e)
|
||||
{
|
||||
if (_locked) return;
|
||||
float dist = Vector2.Distance(
|
||||
RectTransform.anchoredPosition,
|
||||
_slot.RectTransform.anchoredPosition);
|
||||
if (dist <= _cfg.SnapRadius) Snap();
|
||||
else ReturnToTray();
|
||||
}
|
||||
|
||||
private void Snap()
|
||||
{
|
||||
Lock(); // reparent + raycast off + _locked = true
|
||||
var slot = _slot.RectTransform;
|
||||
Tween.UIAnchoredPosition(RectTransform, slot.anchoredPosition, _cfg.SnapDuration, Ease.OutBack);
|
||||
Tween.UISizeDelta (RectTransform, slot.sizeDelta, _cfg.SnapDuration, Ease.OutBack);
|
||||
Tween.LocalRotation (RectTransform, slot.localRotation, _cfg.SnapDuration, Ease.OutBack);
|
||||
_sfx.Play(SfxId.ShapeSnap);
|
||||
_bus.Publish(new PieceSnappedSignal(_shape.Id));
|
||||
}
|
||||
```
|
||||
|
||||
### Four things worth noting
|
||||
|
||||
1. **Reparent on lock** — `Lock()` calls `RectTransform.SetParent(_slot.RectTransform.parent, false)`. The piece moves from the HUD-side tray to the per-drawing slot parent so it travels with the paper and gets included in the captured PNG.
|
||||
2. **Three parallel PrimeTween calls** — position, size, rotation. Tweens start together so the piece visually snaps as one motion. Zero allocations per tween.
|
||||
3. **`SnapRadius` is in canvas units** (from `ShapeBuilderConfig`, e.g. 80–120), not world units. Same `CanvasScaler` reference resolution across devices = same hit feel.
|
||||
4. **Preview hover sound fires once per drag**, on the boundary cross into the preview radius. `_inPreview` flag resets on `OnBeginDrag`.
|
||||
|
||||
Controller listens for `PieceSnappedSignal`, counts against expected piece count, fires `ShapeAssembledSignal` when complete.
|
||||
### Pre-snapped resume
|
||||
|
||||
If the user closes the app mid-assembly (or after completing the drawing), the saved `DrawingProgress.snappedPieces` lists which pieces were locked. On resume, the controller passes `preSnapped: true` to `Setup` for those, and `ShapePiece.SnapInstantly()` puts them straight into their slots — no tween, no input. The user can keep snapping the remaining pieces.
|
||||
|
||||
```csharp
|
||||
private void SnapInstantly()
|
||||
{
|
||||
Lock();
|
||||
var slot = _slot.RectTransform;
|
||||
RectTransform.anchoredPosition = slot.anchoredPosition;
|
||||
RectTransform.sizeDelta = slot.sizeDelta;
|
||||
RectTransform.localRotation = slot.localRotation;
|
||||
}
|
||||
```
|
||||
|
||||
### Spawn loop in `ShapeBuilderController.BuildAsync`
|
||||
|
||||
```csharp
|
||||
var preSnappedIds = progress?.snappedPieces;
|
||||
foreach (var (shape, idx) in template.Pieces.Select((s, i) => (s, i)))
|
||||
{
|
||||
var go = Instantiate(_piecePrefab, _refs.TrayPanel);
|
||||
var piece = go.GetComponent<ShapePiece>();
|
||||
var slot = FindSlotForShape(slots, shape);
|
||||
var trayPos = _trayLayout.GetSlotPosition(idx, template.Pieces.Count);
|
||||
var preSnapped = preSnappedIds != null && preSnappedIds.Contains(shape.Id);
|
||||
|
||||
piece.Setup(shape, slot, _cfg, _sfx, _bus, trayPos, preSnapped);
|
||||
_alive.Add(piece);
|
||||
}
|
||||
```
|
||||
|
||||
Controller listens for `PieceSnappedSignal`, counts against expected piece count, fires `ShapeAssembledSignal` when complete → `GameplayFlowController` captures bare-assembled thumbnail, transitions to Coloring (see §13).
|
||||
|
||||
---
|
||||
|
||||
@@ -1273,19 +1478,24 @@ Toddler-mode error UI:
|
||||
|
||||
| Class | Layer | Asmdef |
|
||||
|---|---|---|
|
||||
| `IDrawingTemplate`, `ColorRegionDTO` | Core | `Core` |
|
||||
| `ShapeSO` (ScriptableObject) | Core | `Core` |
|
||||
| `ICommand`, `IUndoStack` | Core | `Core` |
|
||||
| `UndoStack` | Features | `Features.History` |
|
||||
| `IDrawingTemplate`, `IDrawingTemplateCatalog`, `IDrawingCatalogController` | Core | `Core` |
|
||||
| `ColorRegionDTO`, `PaintCommandDTO`, `ColorPaletteSO` | Core | `Core` |
|
||||
| `ShapeSO`, `ShapeBuilderConfig` (ScriptableObjects) | Core | `Core` |
|
||||
| `DrawingProgress`, `DrawingPhase`, `ProgressionRootDto`, `RegionColorEntry` | Core | `Core` |
|
||||
| `ICommand`, `IUndoStack`, `IProgressionSystem` | Core | `Core` |
|
||||
| `UndoStack`, `HistoryButtonsView`, `HistoryButtonsPresenter` | Features | `Features.History` |
|
||||
| `AddressableAssetProviderService` | Services | `Services.Assets` |
|
||||
| `NativeGallerySaveService` | Services | `Services.Gallery` |
|
||||
| `RenderTextureCaptureService` | Services | `Services.Capture` |
|
||||
| `ColoringController`, `PaintRegionCommand` | Features | `Features.Coloring` |
|
||||
| `ShapeBuilderController`, `ShapePieceUI` | Features | `Features.ShapeBuilder` |
|
||||
| `HistoryController` | Features | `Features.History` |
|
||||
| `ColorBookFlowController` | Features | `Features.ColorBookFlow` |
|
||||
| `CaptureService` | Services | `Services.Capture` |
|
||||
| `ColoringController`, `ColoringStateRepository`, `ColorRegionView`, `PaintRegionCommand` | Features | `Features.Coloring` |
|
||||
| `ShapePiece`, `SlotMarker`, `ShapeBuilderController`, `TrayLayout` | Features | `Features.ShapeBuilder` |
|
||||
| `AddressableDrawingTemplateCatalog` | Features | `Features.DrawingTemplate` |
|
||||
| `DrawingCatalogController`, `DrawingCatalogPresenter`, `DrawingCatalogView`, `CatalogItemVM` | Features | `Features.DrawingCatalog` |
|
||||
| `ColorbookFlowController` | Features | `Features.Colorbook` |
|
||||
| `GameplayFlowController` | Features | `Features.GameplayFlow` |
|
||||
| `ProgressionSystem`, `ProgressionRepository` | Features | `Features.Progression` |
|
||||
| `MenuMascotView`, `MenuMascotPresenter` | Features | `Features.MainMenu` |
|
||||
| `ColorBookSceneRefs`, `ColorBookLifetimeScope`, `AppBoot` | App | `Darkmatter.App` |
|
||||
| `RootLifetimeScope`, `MainMenuLifetimeScope`, `ColorbookLifetimeScope`, `GameplayLifetimeScope`, `GameplaySceneRefs`, `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.
|
||||
|
||||
@@ -1305,18 +1515,23 @@ Comprehensive index — every script (existing or planned) grouped by its module
|
||||
| `Core/Contracts/Services/Scenes/` | `ISceneService` | ✅ |
|
||||
| `Core/Contracts/Services/Capture/` | `ICaptureService` | ⚠️ |
|
||||
| `Core/Contracts/Services/Gallery/` | `IGalleryService` | ⚠️ |
|
||||
| `Core/Contracts/Features/Drawing/` | `IDrawingTemplate`, `IDrawingTemplateCatalog` | ⚠️ |
|
||||
| `Core/Contracts/Features/Coloring/` | `IColorPalette` | ⚠️ |
|
||||
| `Core/Contracts/Features/DrawingCatalog/` | `IDrawingCatalogController`, `IDrawingTemplate`, `IDrawingTemplateCatalog` | ✅ |
|
||||
| `Core/Contracts/Features/Coloring/` | `IColorPalette` | ✅ |
|
||||
| `Core/Contracts/Features/History/` | `ICommand`, `IUndoStack` | ✅ |
|
||||
| `Core/Contracts/Features/Progression/` | `IProgressionService` | ⚠️ |
|
||||
| `Core/Contracts/Features/Progression/` | `IProgressionSystem` | ✅ |
|
||||
| `Core/Data/Dynamic/Services/Audio/` | `AudioHandle`, `AudioRequest` | ✅ |
|
||||
| `Core/Data/Static/Services/Audio/` | `SfxCatalogSO` | ✅ |
|
||||
| `Core/Data/Static/Features/Drawing/` | `DrawingTemplateSO`, `ShapeSO` | ⚠️ |
|
||||
| `Core/Data/Static/Features/Coloring/` | `ColorPaletteSO` | ⚠️ |
|
||||
| `Core/Data/Dynamic/Features/Drawing/` | `ColorRegionDTO` | ⚠️ |
|
||||
| `Core/Data/Dynamic/Features/Coloring/` | `PaintCommandDTO` | ⚠️ |
|
||||
| `Core/Data/Dynamic/Features/Signals/` | `DrawingSelectedSignal`, `ShapeAssembledSignal`, `ColorAppliedSignal`, `PieceSnappedSignal`, `PaperCapturedSignal`, `PaperSavedSignal` | ⚠️ |
|
||||
| `Core/Enums/Services/Audio/` | `AudioChannel`, `AudioPlayMode`, `SfxId` | ✅ |
|
||||
| `Core/Data/Static/Features/DrawingTemplate/` | `DrawingTemplateSO` | ✅ |
|
||||
| `Core/Data/Static/Features/ShapeBuilder/` | `ShapeSO`, `ShapeBuilderConfig` | ✅ |
|
||||
| `Core/Data/Static/Features/Coloring/` | `ColorPaletteSO` | ✅ |
|
||||
| `Core/Data/Dynamic/Features/Coloring/` | `ColorRegionDTO`, `PaintCommandDTO` | ✅ |
|
||||
| `Core/Data/Dynamic/Features/Progression/` | `DrawingProgress`, `ProgressionRootDto`, `RegionColorEntry` | ✅ |
|
||||
| `Core/Data/Signals/Features/DrawingCatalog/` | `DrawingSelectedSignal` | ✅ |
|
||||
| `Core/Data/Signals/Features/ShapeBuilder/` | `ShapeAssembledSignal`, `PieceSnappedSignal` | ✅ |
|
||||
| `Core/Data/Signals/Features/Coloring/` | `ColorAppliedSignal` | ⚠️ |
|
||||
| `Core/Data/Signals/Features/Capture/` | `PaperCapturedSignal`, `PaperSavedSignal` | ⚠️ |
|
||||
| `Core/Enums/Features/Progression/` | `DrawingPhase` (`ShapeBuilding`, `Coloring`) | ✅ |
|
||||
| `Core/Enums/Services/Audio/` | `SfxId` (`None`, `ShapeHover`, `ShapeSnap`, `ShapeNiceTry`, `ShapeReturn`) | ✅ |
|
||||
| `Core/Enums/Services/Camera/` | `CameraType` (add `CaptureCamera` value) | ✅ |
|
||||
| `Core/Enums/Services/Scenes/` | `GameScene` | ✅ |
|
||||
|
||||
@@ -1324,8 +1539,8 @@ Comprehensive index — every script (existing or planned) grouped by its module
|
||||
|
||||
| Module (path) | Scripts | Status |
|
||||
|---|---|---|
|
||||
| `Libs/FSM/` | `IState`, `State`, `StateMachine` | ✅ |
|
||||
| `Libs/Installers/` | `IServiceModule` | ✅ |
|
||||
| `Libs/FSM/` | `IState`, `State<T>`, `StateMachine` (abstract) | ✅ |
|
||||
| `Libs/Installers/` | `IModule` | ✅ |
|
||||
| `Libs/Observer/` | `IEventBus`, `EventBus` | ✅ |
|
||||
| `Libs/PlayerPrefs/Runtime/` | `ProtectedPlayerPrefs`, `ProtectedPlayerPrefsSettings`, `PlayerPrefsKeys`, `PlayerPrefsKeyRegistry`, `LocalWriteTracker`, `PendingWriteResync` | ✅ |
|
||||
| `Libs/PlayerPrefs/Editor/` | `PlayerPrefsEditorWindow`, `ProtectedPlayerPrefsGettingStartedWindow`, `ProtectedPlayerPrefsSettingsUtility`, `ProtectedPlayerPrefsSetupBootstrap` | ✅ |
|
||||
@@ -1335,55 +1550,61 @@ Comprehensive index — every script (existing or planned) grouped by its module
|
||||
|
||||
| Module (path) | Scripts | Status |
|
||||
|---|---|---|
|
||||
| `Services/Analytics/Installers/` | `AnalyticsServiceModule` | ✅ |
|
||||
| `Services/Analytics/Installers/` | `AnalyticsModule` | ✅ |
|
||||
| `Services/Analytics/Systems/` | `FirebaseAnalyticsSystem` | ✅ |
|
||||
| `Services/Assets/` | `AddressableAssetProviderService`, `AddressableLoadHandleTracker` | ✅ |
|
||||
| `Services/Audio/` | `AudioService`, `SfxPlayer` | ✅ |
|
||||
| `Services/Camera/Service/` | `CameraService` | ✅ |
|
||||
| `Services/Camera/Installers/` | `CameraServiceModule` | ✅ |
|
||||
| `Services/Camera/Installers/` | `CameraModule` | ✅ |
|
||||
| `Services/Inputs/Generated/` | `GameInputs` (Input System codegen) | ✅ |
|
||||
| `Services/Inputs/Readers/` | `InputReaderSO` | ✅ |
|
||||
| `Services/Inputs/Installers/` | `InputServiceModule` | ✅ |
|
||||
| `Services/Inputs/` | (Inputs feature partial — Reader + Installer location TBD) | ⚠️ |
|
||||
| `Services/Scenes/` | `SceneService` | ✅ |
|
||||
| `Services/Capture/` | `RenderTextureCaptureService`, `CaptureServiceModule` | ⚠️ |
|
||||
| `Services/Gallery/` | `NativeGallerySaveService`, `GalleryServiceModule` | ⚠️ |
|
||||
| `Services/Capture/Systems/` | `CaptureService` | ✅ (stub) |
|
||||
| `Services/Capture/Installers/` | `CaptureModule` | ✅ |
|
||||
| `Services/Gallery/Core/` | `GalleryService` | ✅ (stub — needs native plugin wiring) |
|
||||
| `Services/Gallery/Installers/` | `GalleryModule` | ✅ |
|
||||
|
||||
#### Features
|
||||
|
||||
| Module (path) | Scripts | Status |
|
||||
|---|---|---|
|
||||
| `Features/History/Stack/` | `UndoStack` | ✅ |
|
||||
| `Features/History/Installers/` | `HistoryServiceModule` | ✅ |
|
||||
| `Features/History/UI/` | `HistoryButtonsView`, `HistoryPresenter`, `HistoryController` | ⚠️ |
|
||||
| `Features/MainMenu/Installers/` | `MainMenuModule` | ⚠️ |
|
||||
| `Features/History/Installers/` | `HistoryFeatureModule` | ✅ |
|
||||
| `Features/History/UI/` | `HistoryButtonsView`, `HistoryButtonsPresenter` | ✅ |
|
||||
| `Features/MainMenu/Installers/` | `MainMenuFeatureModule` | ⚠️ |
|
||||
| `Features/MainMenu/Systems/` | `MainMenuModel`, `MenuMascotPresenter` | ⚠️ |
|
||||
| `Features/MainMenu/UI/` | `MenuMascotView`, `IMenuMascotView` | ⚠️ |
|
||||
| `Features/DrawingCatalog/Systems/` | `DrawingCatalogController` | ⚠️ |
|
||||
| `Features/DrawingCatalog/UI/` | `DrawingCatalogPresenter`, `DrawingCatalogView`, `IDrawingCatalogView`, `CatalogItemVM` | ⚠️ |
|
||||
| `Features/DrawingCatalog/Installers/` | `DrawingCatalogModule` | ⚠️ |
|
||||
| `Features/ShapeBuilder/Systems/` | `ShapeBuilderController`, `ShapePieceFsm`, `ShapePieceFactory`, `TrayLayout` | ⚠️ |
|
||||
| `Features/ShapeBuilder/UI/` | `ShapePieceUI`, `SlotMarker`, `TrayPanel` | ⚠️ |
|
||||
| `Features/ShapeBuilder/Installers/` | `ShapeBuilderModule` | ⚠️ |
|
||||
| `Features/DrawingCatalog/Systems/` | `DrawingCatalogController` | ✅ |
|
||||
| `Features/DrawingCatalog/UI/` | `DrawingCatalogPresenter`, `DrawingCatalogView`, `DrawingCatalogButton`, `CatalogItemVM` | ✅ |
|
||||
| `Features/DrawingCatalog/Installers/` | `DrawingCatalogFeatureModule` | ✅ |
|
||||
| `Features/DrawingTemplate/Systems/` | `AddressableDrawingTemplateCatalog` | ✅ |
|
||||
| `Features/DrawingTemplate/Installers/` | `DrawingTemplateFeatureModule` | ✅ |
|
||||
| `Features/ShapeBuilder/UI/` | `ShapePiece`, `SlotMarker` | ✅ |
|
||||
| `Features/ShapeBuilder/Systems/` | `ShapeBuilderController`, `TrayLayout` | ⚠️ |
|
||||
| `Features/ShapeBuilder/Installers/` | `ShapeBuilderFeatureModule` | ✅ |
|
||||
| `Features/Coloring/Systems/` | `ColoringController`, `ColoringStateRepository`, `ColorRegionFactory` | ⚠️ |
|
||||
| `Features/Coloring/UI/` | `ColorRegionView`, `ColorPaletteView`, `ColorPalettePresenter` | ⚠️ |
|
||||
| `Features/Coloring/Commands/` | `PaintRegionCommand` | ⚠️ |
|
||||
| `Features/Coloring/Installers/` | `ColoringModule` | ⚠️ |
|
||||
| `Features/Capture/Systems/` | `CaptureController` | ⚠️ |
|
||||
| `Features/Coloring/Installers/` | `ColoringFeatureModule` | ⚠️ |
|
||||
| `Features/Capture/Systems/` | `CaptureController` (light wrapper around `ICaptureService`) | ⚠️ |
|
||||
| `Features/Capture/UI/` | `CaptureButtonPresenter`, `SaveToastView` | ⚠️ |
|
||||
| `Features/Capture/Installers/` | `CaptureFeatureModule` | ⚠️ |
|
||||
| `Features/Progression/Systems/` | `ProgressionService`, `ProgressionRepository` | ⚠️ |
|
||||
| `Features/Progression/Installers/` | `ProgressionModule` | ⚠️ |
|
||||
| `Features/ColorBookFlow/Systems/` | `ColorBookFlowController` | ⚠️ |
|
||||
| `Features/ColorBookFlow/Installers/` | `ColorBookFlowModule` | ⚠️ |
|
||||
| `Features/Progression/Systems/` | `ProgressionSystem`, `ProgressionRepository` | ✅ (stubs) |
|
||||
| `Features/Progression/Installers/` | `ProgressionFeatureModule` | ✅ |
|
||||
| `Features/ColorbookFlow/System/` | `ColorbookFlowController` | ✅ (needs constructor injection) |
|
||||
| `Features/ColorbookFlow/Installers/` | `ColorbookFlowFeatureModule` | ✅ |
|
||||
| `Features/GameplayFlow/Systems/` | `GameplayFlowController` (single owner of all saves — §13) | ⚠️ |
|
||||
| `Features/GameplayFlow/Installers/` | `GameplayFlowFeatureModule` | ⚠️ |
|
||||
|
||||
#### App
|
||||
|
||||
| Module (path) | Scripts | Status |
|
||||
|---|---|---|
|
||||
| `App/LifetimeScopes/` | `RootLifetimeScope` | ✅ |
|
||||
| `App/LifetimeScopes/` | `MainMenuLifetimeScope`, `ColorBookLifetimeScope` | ⚠️ |
|
||||
| `App/Boot/` | `AppBoot` | ⚠️ |
|
||||
| `App/SceneRefs/` | `ColorBookSceneRefs` | ⚠️ |
|
||||
| `App/LifetimeScopes/` | `BaseLifetimeScope` (abstract), `RootLifetimeScope` | ✅ |
|
||||
| `App/LifetimeScopes/` | `GameLifetimeScope` (placeholder, empty), `GameplayLifetimescope` (typo — needs rename to `GameplayLifetimeScope`) | ⚠️ |
|
||||
| `App/LifetimeScopes/` | `MainMenuLifetimeScope`, `ColorbookLifetimeScope`, `GameplayLifetimeScope` (final) | ⚠️ planned |
|
||||
| `App/Boot/` | `AppBoot` | ⚠️ planned |
|
||||
| `App/SceneRefs/` | `GameplaySceneRefs` (PaperRoot, SlotsParent, PiecesParent, RegionsParent, TrayPanel, CaptureCamera) | ⚠️ planned |
|
||||
|
||||
---
|
||||
|
||||
@@ -1728,109 +1949,124 @@ public interface IDrawingCatalogView {
|
||||
|
||||
---
|
||||
|
||||
### 32.5 Feature — `ShapeBuilder` *(planned)*
|
||||
### 32.5 Feature — `ShapeBuilder` *(planned controller, MBs exist)*
|
||||
|
||||
#### `ShapeBuilderController` *(Systems)*
|
||||
Spawns shape pieces for the selected template, tracks snap progress, fires `ShapeAssembledSignal` when complete.
|
||||
The simplified post-FSM design. **No state machine per piece, no factory class.** Three roles: piece MB, slot MB, controller. Plus a tunables SO.
|
||||
|
||||
#### `ShapePiece : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler` *(UI — ✅ exists)*
|
||||
The single MonoBehaviour that handles drag, reactive preview lerp, snap (PrimeTween), and return-to-tray. Spawned by the controller; `Setup` binds dependencies and starting pose.
|
||||
```csharp
|
||||
// fields: IDrawingTemplateCatalog _catalog, ShapePieceFactory _factory,
|
||||
// ColorBookSceneRefs _refs, TrayPanel _tray, IEventBus _bus, ShapeBuilderConfig _cfg
|
||||
public sealed class ShapeBuilderController : IDisposable {
|
||||
public IReadOnlyList<ShapePieceUI> 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.
|
||||
- **Slot discovery:** after a drawing's per-drawing prefab is instantiated under `ColorBookSceneRefs.PaperRoot`, the controller queries `GetComponentsInChildren<SlotMarker>()` to discover all slots in the loaded drawing. Each slot's `_shape` field tells which `ShapeSO` it expects; matching pieces are spawned in the tray.
|
||||
|
||||
#### `ShapePieceUI : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler` *(UI)*
|
||||
The UI Image that the toddler drags. One prefab; the assigned `ShapeSO` determines visual identity and snap params.
|
||||
|
||||
```csharp
|
||||
public sealed class ShapePieceUI : MonoBehaviour,
|
||||
public sealed class ShapePiece : MonoBehaviour,
|
||||
IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
[SerializeField] private ShapeSO _shape; // set by controller at spawn (or in inspector for testing)
|
||||
[SerializeField] private Image _image;
|
||||
[SerializeField] private Image image;
|
||||
private ShapeSO _shape; // bound by Setup
|
||||
private SlotMarker _slot;
|
||||
private ShapeBuilderConfig _cfg;
|
||||
private ISfxPlayer _sfx;
|
||||
private IEventBus _bus;
|
||||
private Vector2 _trayPos;
|
||||
private Vector2 _traySize;
|
||||
private bool _locked;
|
||||
|
||||
public ShapeSO Shape => _shape;
|
||||
public string PieceId => _shape != null ? _shape.Id : null;
|
||||
public ShapeSO Shape => _shape;
|
||||
public RectTransform RectTransform => (RectTransform)transform;
|
||||
public bool IsLocked { get; private set; }
|
||||
public event Action<ShapeSO> Snapped;
|
||||
public bool IsLocked => _locked;
|
||||
public RectTransform RectTransform { get; }
|
||||
|
||||
// Controller calls this at spawn time
|
||||
public void Assign(ShapeSO shape) {
|
||||
_shape = shape;
|
||||
ApplyShape();
|
||||
public void Setup(
|
||||
ShapeSO shape, SlotMarker slot, ShapeBuilderConfig cfg,
|
||||
ISfxPlayer sfx, IEventBus bus,
|
||||
Vector2 trayPos, bool preSnapped);
|
||||
}
|
||||
```
|
||||
- Drag handlers run inline in this MB — no separate FSM class.
|
||||
- `Snap()` is a PrimeTween triple (`Tween.UIAnchoredPosition` / `UISizeDelta` / `LocalRotation`); `SnapInstantly()` is the resume path that puts a pre-snapped piece directly into its slot pose without animation.
|
||||
- `ReturnToTray()` builds a PrimeTween `Sequence` of three parallel tweens.
|
||||
- See §26 for the snap algorithm walkthrough.
|
||||
|
||||
#### `SlotMarker : MonoBehaviour` *(UI — ✅ exists)*
|
||||
Authored per drawing — designer places one in the per-drawing prefab at each slot location with the `RectTransform` set to the target pose and `_shape` field assigned to the matching `ShapeSO`. The `RectTransform` itself **is** the target pose.
|
||||
```csharp
|
||||
public sealed class SlotMarker : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private ShapeSO shape;
|
||||
[SerializeField] private Image outline; // optional faint outline UI
|
||||
|
||||
public ShapeSO Shape => shape;
|
||||
public string SlotId => shape != null ? shape.Id : null;
|
||||
public RectTransform RectTransform => (RectTransform)transform;
|
||||
|
||||
public void SetOutlineVisible(bool visible);
|
||||
}
|
||||
```
|
||||
**Matching is by `ShapeSO` reference equality** — the controller pairs each `ShapePiece.Shape` with the `SlotMarker.Shape` of the same SO asset.
|
||||
|
||||
#### `ShapeBuilderConfig : ScriptableObject` *(Static data — ✅ exists)*
|
||||
```csharp
|
||||
[CreateAssetMenu(menuName = "Darkmatter/ShapeBuilder/Config")]
|
||||
public sealed class ShapeBuilderConfig : ScriptableObject
|
||||
{
|
||||
public float SnapRadius; // 80–120 canvas units
|
||||
public float SnapGraceMultiplier; // (currently unused — grace zone removed)
|
||||
public float PreviewRadius; // ~2× SnapRadius
|
||||
public float SnapDuration; // 0.25s
|
||||
public float ReturnDuration; // 0.25s
|
||||
public AnimationCurve PreviewCurve; // easing for the reactive lerp
|
||||
|
||||
public Vector2 DragSizeDelta(ShapeSO shape);
|
||||
}
|
||||
```
|
||||
|
||||
#### `ShapeBuilderController` *(Systems — planned)*
|
||||
Spawns pieces for the selected template, tracks snap progress, fires `ShapeAssembledSignal` when complete. **Replaces the old `ShapePieceFactory` — spawning is now a 5-line inline loop, not a separate class.**
|
||||
```csharp
|
||||
// fields: IDrawingTemplateCatalog _catalog, IAssetProviderService _assets,
|
||||
// GameplaySceneRefs _refs, TrayLayout _trayLayout,
|
||||
// ShapeBuilderConfig _cfg, ISfxPlayer _sfx, IEventBus _bus
|
||||
public sealed class ShapeBuilderController : IAsyncStartable, IDisposable
|
||||
{
|
||||
private readonly List<ShapePiece> _alive = new();
|
||||
private GameObject _piecePrefab;
|
||||
|
||||
public async UniTask StartAsync(CancellationToken ct) {
|
||||
_piecePrefab = await _assets.LoadAssetAsync<GameObject>(
|
||||
"shapebuilder/piece", null, ct);
|
||||
_pieceSnappedSub = _bus.Subscribe<PieceSnappedSignal>(OnPieceSnapped);
|
||||
}
|
||||
|
||||
private void OnValidate() => ApplyShape(); // editor inspector edits
|
||||
private void Awake() => ApplyShape(); // runtime safety
|
||||
public IReadOnlyList<ShapePiece> Alive => _alive;
|
||||
public IReadOnlyCollection<string> GetSnappedPieceIds(); // for save records
|
||||
|
||||
private void ApplyShape() {
|
||||
if (_shape == null || _image == null) return;
|
||||
_image.sprite = _shape.Sprite;
|
||||
RectTransform.sizeDelta = _shape.DefaultSizeDelta;
|
||||
}
|
||||
public async UniTask BuildAsync(
|
||||
IDrawingTemplate template,
|
||||
IReadOnlyCollection<string> preSnappedIds = null);
|
||||
|
||||
public void Reset(); // despawns all pieces; called by GameplayFlowController on Teardown
|
||||
}
|
||||
// sub: PieceSnappedSignal
|
||||
// pub: ShapeAssembledSignal
|
||||
```
|
||||
- Handlers forward to `ShapePieceFsm` (`OnDragBegin / OnDrag(localPos) / OnDragEnd`).
|
||||
- `OnDrag` converts `PointerEventData.position` to canvas-local via `RectTransformUtility.ScreenPointToLocalPointInRectangle` against the piece's parent rect.
|
||||
- No collider, no Physics2D anywhere.
|
||||
- **Identity follows the SO** — change `_shape` in inspector and the visual + ID update on the next `OnValidate`. At runtime, `Assign(...)` is the only mutation path.
|
||||
- **Slot discovery:** after the per-drawing prefab is instantiated, `GetComponentsInChildren<SlotMarker>(includeInactive: true)` finds all slots. Each slot's `_shape` tells which `ShapeSO` it expects.
|
||||
- **Pre-snap on resume:** if `preSnappedIds.Contains(shape.Id)`, the spawned `ShapePiece` is initialized with `preSnapped: true` → `SnapInstantly()` lands it in the slot at scope start.
|
||||
- **Snapped count:** subscribes to `PieceSnappedSignal`, counts against expected, fires `ShapeAssembledSignal` when count == `template.Pieces.Count`.
|
||||
|
||||
#### `ShapePieceFsm` *(Systems)*
|
||||
Per-piece state machine using `Libs.FSM`. States: `InTray → Dragging → Preview → (Snapped | Returning)`.
|
||||
#### `TrayLayout` *(Systems — planned)*
|
||||
Tiny stateless helper. Given (`index`, `total`), returns the tray slot's `anchoredPosition`. Either uses a `HorizontalLayoutGroup`'s computed positions or a hand-rolled even spacing across the tray's width.
|
||||
```csharp
|
||||
// fields: ShapePieceUI _ui, SlotMarker _targetSlot, ShapeBuilderConfig _cfg,
|
||||
// IAudioService _audio, IEventBus _bus
|
||||
public sealed class ShapePieceFsm {
|
||||
public void OnDragBegin();
|
||||
public void OnDrag(Vector2 canvasLocalPos);
|
||||
public void OnDragEnd();
|
||||
public bool IsLocked { get; }
|
||||
public sealed class TrayLayout
|
||||
{
|
||||
[SerializeField] private RectTransform trayRect;
|
||||
public Vector2 GetSlotPosition(int index, int total);
|
||||
}
|
||||
```
|
||||
- **Preview-state update**: reactive lerp of `anchoredPosition / sizeDelta / localRotation` toward `_targetSlot`'s pose, driven by `1 - dist/PreviewRadius`. No DOTween while previewing — it's per-frame.
|
||||
- **Snapped enter**: DOTween ease-out to exact slot pose (~0.2s), disable drag, fire `PieceSnappedSignal`.
|
||||
- **Returning enter**: DOTween back to tray slot (`anchoredPosition` from `TrayLayout`).
|
||||
|
||||
#### `SlotMarker : MonoBehaviour` *(UI)*
|
||||
The outline `Image` under `ColorBookSceneRefs.SlotsParent` showing where a piece should snap. Authored per drawing — designer places one in the per-drawing prefab at each slot location, with its `RectTransform` set to the target pose and `_shape` field assigned to the matching `ShapeSO`.
|
||||
```csharp
|
||||
public sealed class SlotMarker : MonoBehaviour {
|
||||
[SerializeField] private ShapeSO _shape; // which shape fits here
|
||||
[SerializeField] private Image _outline; // optional faint outline UI
|
||||
#### Removed / not needed
|
||||
|
||||
public ShapeSO Shape => _shape;
|
||||
public string SlotId => _shape != null ? _shape.Id : null;
|
||||
public RectTransform RectTransform => (RectTransform)transform;
|
||||
}
|
||||
```
|
||||
- **Pose lives on this MB's `RectTransform`** — `anchoredPosition`, `sizeDelta`, `localRotation` directly. No pose data on the SO.
|
||||
- **Matching:** `ShapePieceFsm` compares `_piece.Shape == _slot.Shape` (Unity Object reference equality). No string lookups.
|
||||
|
||||
#### `TrayPanel : MonoBehaviour` *(UI)*
|
||||
HUD-side panel (on `HUDCanvas`) where pieces start out. Has a `HorizontalLayoutGroup` + `ContentSizeFitter`. Provides spawn anchors via `RectTransform Slot(int index)` for the controller.
|
||||
|
||||
#### `ShapePieceFactory` *(Systems)*
|
||||
Pool of `ShapePieceUI` GameObjects (one prefab) + their associated FSMs. Reused across template loads.
|
||||
```csharp
|
||||
public sealed class ShapePieceFactory {
|
||||
// Instantiates the single piece prefab under `parent`, calls Assign(shape) on it,
|
||||
// and wires up its FSM with the matching SlotMarker.
|
||||
public ShapePieceUI Spawn(ShapeSO shape, SlotMarker targetSlot, RectTransform parent);
|
||||
public void Despawn(ShapePieceUI piece);
|
||||
}
|
||||
```
|
||||
- One prefab in `Content/Gameplay/Prefabs/ShapePiece.prefab` is instantiated repeatedly. Visual identity comes from the `ShapeSO` passed to `Assign`.
|
||||
|
||||
#### `ShapeBuilderInputBinder` *(Systems)*
|
||||
With UI handlers on the piece itself, an explicit input binder isn't strictly needed — drag events route via the EventSystem. Keep this class only if you need to listen for "any tap outside any piece" (e.g. to dismiss a preview). Otherwise skip.
|
||||
- **`ShapePieceFsm`** — was a per-piece state machine. Replaced by inline drag handlers + a single `_locked` bool on `ShapePiece`.
|
||||
- **`ShapePieceFactory`** — was a wrapper around `Instantiate` + FSM wiring. Replaced by a 5-line inline loop in `ShapeBuilderController.BuildAsync`.
|
||||
- **Five state classes** (`InTray`, `Dragging`, `Preview`, `Snapped`, `Returning`) — gone. Their behavior maps to: `_locked = false` (idle/dragging/preview all share the same handlers), `_inPreview` flag (preview boundary detection), `Snap()` method, `ReturnToTray()` method.
|
||||
- **`ShapeBuilderInputBinder`** — never needed; UI handlers on the piece are sufficient.
|
||||
|
||||
---
|
||||
|
||||
@@ -1855,16 +2091,27 @@ public sealed class ColoringStateRepository {
|
||||
Builds and pushes `PaintRegionCommand` instances; spawns `ColorRegionView` per region.
|
||||
```csharp
|
||||
// fields: IUndoStack _undo, ColoringStateRepository _state, ColorRegionFactory _factory,
|
||||
// ColorBookSceneRefs _refs, IEventBus _bus
|
||||
// GameplaySceneRefs _refs, IEventBus _bus
|
||||
public interface IColoringController {
|
||||
UniTask SpawnRegionsAsync(IDrawingTemplate template);
|
||||
void PaintRegion(ColorRegionView view); // builds command, pushes to undo stack
|
||||
void Clear();
|
||||
// Spawn regions on the paper. Pass non-null savedColors to restore colors
|
||||
// from a DrawingProgress record; null = use ColorRegionDTO.InitialColor.
|
||||
UniTask SpawnRegionsAsync(
|
||||
IDrawingTemplate template,
|
||||
IReadOnlyDictionary<string, Color> savedColors = null);
|
||||
|
||||
void PaintRegion(ColorRegionView view); // builds command, pushes to undo stack
|
||||
|
||||
// Snapshot current paint state for save records (see §13).
|
||||
IReadOnlyDictionary<string, Color> GetCurrentColors();
|
||||
|
||||
void Clear(); // despawn all regions
|
||||
}
|
||||
// sub: ShapeAssembledSignal (via flow controller, not direct)
|
||||
// pub: ColorAppliedSignal (via PaintRegionCommand)
|
||||
```
|
||||
Spawns each region as a UI `Image` under `_paper.RegionsParent`. No `Physics2D`.
|
||||
Spawns each region as a UI `Image` under `_refs.RegionsParent`. No `Physics2D`.
|
||||
|
||||
**Autosave integration:** after each successful `PaintRegion`, the controller calls a debounced `GameplayFlowController.ScheduleAutosave()` so the flow can write the new color state to `IProgressionSystem` 500 ms later (no thumbnail, cheap). The flow controller cancels and resets the timer on each paint — only the last paint in a burst triggers the write.
|
||||
|
||||
#### `ColorRegionView : MonoBehaviour, IPointerClickHandler` *(UI)*
|
||||
UI Image with alpha-based hit detection. Tap routes through Unity's EventSystem directly to `OnPointerClick`.
|
||||
@@ -2045,41 +2292,47 @@ Implemented as `MonoBehaviour` per feature/service so scopes can drag them in th
|
||||
|---|---|---|---|
|
||||
| `AppBoot` | App | Startup sequencer | assets, progression, audio, scenes |
|
||||
| `RootLifetimeScope` | App | Root DI | service modules |
|
||||
| `ColorBookLifetimeScope` | App | Scene DI | scene refs, feature modules |
|
||||
| `MainMenuLifetimeScope` | App | Menu scene DI | feature modules |
|
||||
| `ColorBookSceneRefs` | App | Scene-bound RectTransform / Camera holder | — |
|
||||
| `MenuMascotView` | Feature | Spine mascot UI (SkeletonGraphic wrapper) | — |
|
||||
| `MenuMascotPresenter` | Feature | Drives mascot animations from model events | view, model |
|
||||
| `DrawingCatalogController` | Feature | Grid logic | catalog, bus |
|
||||
| `DrawingCatalogPresenter` | Feature | UI bridge | view, controller, catalog |
|
||||
| `ShapeSO` | Core asset | Authored shape (sprite + snap params, reusable) | — |
|
||||
| `ShapeBuilderController` | Feature | Piece spawn + snap tracking | catalog, factory, refs, tray, bus, cfg |
|
||||
| `ShapePieceUI` | Feature | Draggable UI piece prefab; holds `[SerializeField] ShapeSO _shape` | fsm |
|
||||
| `ShapePieceFsm` | Feature | Per-piece state machine (Tray/Drag/Preview/Snapped/Returning) | ui, slot, cfg, audio, bus |
|
||||
| `SlotMarker` | Feature | Slot outline UI Image at target pose; holds `_shape` | — |
|
||||
| `TrayPanel` | Feature | HUD-side tray with LayoutGroup | — |
|
||||
| `ColoringStateRepository` | Feature | Current color model | — |
|
||||
| `ColoringController` | Feature | Region spawn + paint cmd | undo, state, factory, refs, bus |
|
||||
| `ColorRegionView` | Feature | Region UI Image + IPointerClickHandler | controller |
|
||||
| `PaintRegionCommand` | Feature | Undoable paint (sets Image.color) | view, bus |
|
||||
| `HistoryController` | Feature | Undo/redo facade | undo stack, bus |
|
||||
| `UndoStack` | Feature | Bounded undo store | — |
|
||||
| `CaptureController` | Feature | Capture-then-save orchestration | capture svc, gallery svc, bus |
|
||||
| `ColorBookFlowController` | Feature | Scene FSM (Catalog → Building → Coloring → Done) | bus, catalog, builder, coloring, capture, progression |
|
||||
| `ProgressionService` | Feature | Completion tracking | PlayerPrefs lib |
|
||||
| `EventBus` | Lib | Pub/sub | — |
|
||||
| `StateMachine` | Lib | Generic FSM | — |
|
||||
| `IServiceModule` | Lib | DI installer interface | — |
|
||||
| `ProtectedPlayerPrefs` | Lib | Encrypted PlayerPrefs wrapper | — |
|
||||
| `AddressableAssetProviderService` | Service | Addressables wrapper | — |
|
||||
| `RenderTextureCaptureService` | Service | One-shot PNG render via CaptureCamera | scene refs |
|
||||
| `NativeGallerySaveService` | Service | Native gallery save (thin plugin shim) | — |
|
||||
| `SceneService` | Service | Async scene loads | — |
|
||||
| `AudioService`, `SfxPlayer` | Service | SFX playback | assets |
|
||||
| `CameraService` | Service | Camera registry (MainCamera, UICamera, CaptureCamera) | — |
|
||||
| `InputReaderSO` | Service | New Input System reader | — |
|
||||
| `FirebaseAnalyticsSystem` | Service | Analytics events | — |
|
||||
| `ColorbookLifetimeScope` | App | Catalog scene DI | feature modules |
|
||||
| `GameplayLifetimeScope` | App | Active drawing scene DI | scene refs, feature modules |
|
||||
| `GameplaySceneRefs` | App | Scene-bound RectTransform + CaptureCamera holder | — |
|
||||
| `MenuMascotView` | Feature.MainMenu | Spine mascot UI (`SkeletonGraphic` wrapper) | — |
|
||||
| `MenuMascotPresenter` | Feature.MainMenu | Drives mascot animations from model events | view, model |
|
||||
| `DrawingCatalogController` | Feature.DrawingCatalog | Visible-ID list + selection signal | catalog, progression, bus |
|
||||
| `DrawingCatalogPresenter` | Feature.DrawingCatalog | UI bridge | view, controller, catalog, progression |
|
||||
| `DrawingCatalogView` | Feature.DrawingCatalog | UI; renders cells | — |
|
||||
| `CatalogItemVM` | Feature.DrawingCatalog | View-model per cell | — |
|
||||
| `AddressableDrawingTemplateCatalog` | Feature.DrawingTemplate | Loads `DrawingTemplateSO`s, exposes `NextUnseen` | assets, progression |
|
||||
| `ShapeSO` | Core asset | Authored shape (id + sprite + DefaultSizeDelta) | — |
|
||||
| `ShapeBuilderConfig` | Core asset | Tunables (radii, durations, curve) | — |
|
||||
| `ShapePiece` | Feature.ShapeBuilder | Draggable piece MB (drag + preview lerp + snap + return) | shape, slot, cfg, sfx, bus |
|
||||
| `SlotMarker` | Feature.ShapeBuilder | Slot anchor MB; `RectTransform` == target pose | — |
|
||||
| `ShapeBuilderController` | Feature.ShapeBuilder | Spawns pieces, tracks snap count | catalog, assets, refs, tray, cfg, sfx, bus |
|
||||
| `TrayLayout` | Feature.ShapeBuilder | Computes piece tray positions | — |
|
||||
| `ColoringStateRepository` | Feature.Coloring | Current color model | — |
|
||||
| `ColoringController` | Feature.Coloring | Region spawn + paint cmd + autosave hook | undo, state, factory, refs, flow, bus |
|
||||
| `ColorRegionView` | Feature.Coloring | Region UI Image + `IPointerClickHandler` | controller |
|
||||
| `PaintRegionCommand` | Feature.Coloring | Undoable paint (sets `Image.color`) | view, bus |
|
||||
| `HistoryController` | Feature.History | Undo/redo facade | undo stack, bus |
|
||||
| `UndoStack` | Feature.History | Bounded undo store | — |
|
||||
| `CaptureController` | Feature.Capture | (light wrapper) calls `ICaptureService.CaptureAsync` | capture svc |
|
||||
| `ColorbookFlowController` | Feature.ColorbookFlow | Catalog scene orchestrator (init + selection→scene-load) | catalog, progression, scenes, bus |
|
||||
| `GameplayFlowController` | Feature.GameplayFlow | Active drawing FSM + **single owner of all saves** (see §13) | catalog, builder, coloring, capture, gallery, progression, scenes, bus |
|
||||
| `ProgressionSystem` | Feature.Progression | Per-template state + completed view | repository |
|
||||
| `ProgressionRepository` | Feature.Progression | PlayerPrefs JSON + thumbnail file IO | — |
|
||||
| `EventBus` | Lib.Observer | Pub/sub | — |
|
||||
| `StateMachine` (abstract) + `State<T>` | Lib.FSM | Generic FSM base (Enter/Tick/Exit, ChangeState) | — |
|
||||
| `IModule` | Lib.Installers | DI installer interface | — |
|
||||
| `ProtectedPlayerPrefs` | Lib.PlayerPrefs | Encrypted PlayerPrefs wrapper | — |
|
||||
| `AddressableAssetProviderService` | Service.Assets | Addressables wrapper | — |
|
||||
| `CaptureService` | Service.Capture | One-shot PNG render via `CaptureCamera` | refs |
|
||||
| `NativeGallerySaveService` | Service.Gallery | Native gallery save (thin plugin shim) | — |
|
||||
| `SceneService` | Service.Scenes | Async scene loads | — |
|
||||
| `AudioService`, `SfxPlayer` | Service.Audio | SFX playback | assets |
|
||||
| `CameraService` | Service.Camera | Camera registry | — |
|
||||
| `InputReaderSO` | Service.Inputs | New Input System reader | — |
|
||||
| `FirebaseAnalyticsSystem` | Service.Analytics | Analytics events | — |
|
||||
|
||||
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. See §31b for the full path-by-path inventory.
|
||||
|
||||
> Today only these rows are real on disk: `RootLifetimeScope` (App), `AddressableAssetProviderService`, `AudioService`/`SfxPlayer`, `CameraService`, `SceneService`, `InputReaderSO`, `FirebaseAnalyticsSystem` (Services), `UndoStack` + `HistoryServiceModule` (Features.History), plus `Libs.*` entries (`EventBus`, `StateMachine`, `IServiceModule`, PlayerPrefs lib, UI toggles). Everything else is the target.
|
||||
> **What's real on disk today (2026-05):** All Service classes (`AddressableAssetProviderService`, `AudioService`/`SfxPlayer`, `CameraService`, `SceneService`, `InputReaderSO`, `FirebaseAnalyticsSystem`, stub `CaptureService`, stub `GalleryService`), all Lib classes, `ShapePiece` + `SlotMarker` + `ShapeBuilderConfig`, `UndoStack` + `HistoryServiceModule`, `ProgressionSystem` + `ProgressionRepository` (stubs), `AddressableDrawingTemplateCatalog` + module, `DrawingCatalogController` + presenter + view, `ColorbookFlowController` (partial — needs constructor injection wired). Empty / planned: `MainMenu` feature, `GameplayFlow` feature, `Coloring` feature, `MainMenu`/`Colorbook`/`Gameplay` scene scopes, all scenes except `Boot.unity`.
|
||||
|
||||
Reference in New Issue
Block a user