Refactored and Animaiton for HOlder

This commit is contained in:
Savya Bikram Shah
2026-05-28 17:45:58 +05:45
parent 252c1b8a41
commit 9ac752b23c
85 changed files with 1392 additions and 147 deletions

View File

@@ -5,9 +5,4 @@ namespace Darkmatter.Core.Contracts.Features.GameplayFlow;
public interface IGameplaySceneRefs
{
RectTransform PaperRoot { get; }
RectTransform SlotsParent { get; }
RectTransform PiecesParent { get; }
RectTransform RegionsParent { get; }
RectTransform HudRoot { get; }
RectTransform TrayPanel { get; }
}

View File

@@ -0,0 +1,4 @@
namespace Darkmatter.Core.Data.Signals.Features.ShapeBuilder
{
public record struct ShapeBuilderStartedSignal(string TemplateId);
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 40f2733e19ad34fe5ba5d63aa2e9a39b

View File

@@ -1,14 +1,22 @@
using Darkmatter.Features.Coloring.UI;
using Darkmatter.Libs.Installers;
using UnityEngine;
using VContainer;
using VContainer.Unity;
namespace Darkmatter.Features.Coloring
{
public class ColoringFeatureModule : MonoBehaviour, IModule
{
[SerializeField] private ColorPaletteHolderView paletteHolderView;
public void Register(IContainerBuilder builder)
{
if (paletteHolderView != null)
{
builder.RegisterComponent(paletteHolderView);
builder.RegisterEntryPoint<ColorPaletteHolderPresenter>().WithParameter(paletteHolderView);
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 569e45483f54348a593b6571740cd8a2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 416f96737010f40c085ac404743fa8ef
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
using System;
using Darkmatter.Core.Data.Signals.Features.ShapeBuilder;
using Darkmatter.Libs.Observer;
using VContainer.Unity;
namespace Darkmatter.Features.Coloring.UI
{
public class ColorPaletteHolderPresenter : IStartable, IDisposable
{
private readonly ColorPaletteHolderView _view;
private readonly IEventBus _bus;
private IDisposable _assembledSub;
public ColorPaletteHolderPresenter(ColorPaletteHolderView view, IEventBus bus)
{
_view = view;
_bus = bus;
}
public void Start()
{
_view.HideInstant();
_assembledSub = _bus.Subscribe<ShapeAssembledSignal>(_ => _view.Show());
}
public void Dispose()
{
_assembledSub?.Dispose();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cc0590d8f6fce4487943575bfb96c32a

View File

@@ -0,0 +1,76 @@
using PrimeTween;
using UnityEngine;
namespace Darkmatter.Features.Coloring.UI
{
[RequireComponent(typeof(CanvasGroup))]
public class ColorPaletteHolderView : MonoBehaviour
{
[SerializeField] private RectTransform spawnRoot;
[SerializeField] private RectTransform animatedRoot;
[SerializeField] private float showDuration = 0.35f;
[SerializeField] private float hideDuration = 0.25f;
[SerializeField] private Vector2 hiddenOffset = new(1500f, 0f);
private CanvasGroup _canvasGroup;
private Vector2 _shownAnchoredPos;
private Sequence _activeSequence;
public RectTransform SpawnRoot => spawnRoot;
private void Awake()
{
_canvasGroup = GetComponent<CanvasGroup>();
if (animatedRoot == null) animatedRoot = (RectTransform)transform;
_shownAnchoredPos = animatedRoot.anchoredPosition;
}
public Sequence Show()
{
KillActive();
gameObject.SetActive(true);
_canvasGroup.interactable = true;
_canvasGroup.blocksRaycasts = true;
_activeSequence = Sequence.Create()
.Group(Tween.UIAnchoredPosition(animatedRoot, _shownAnchoredPos, showDuration, Ease.OutBack))
.Group(Tween.Alpha(_canvasGroup, 1f, showDuration, Ease.OutQuad));
return _activeSequence;
}
public Sequence Hide()
{
KillActive();
_canvasGroup.interactable = false;
_canvasGroup.blocksRaycasts = false;
var hiddenPos = _shownAnchoredPos + hiddenOffset;
_activeSequence = Sequence.Create()
.Group(Tween.UIAnchoredPosition(animatedRoot, hiddenPos, hideDuration, Ease.InQuad))
.Group(Tween.Alpha(_canvasGroup, 0f, hideDuration, Ease.InQuad))
.ChainCallback(() => gameObject.SetActive(false));
return _activeSequence;
}
public void HideInstant()
{
KillActive();
animatedRoot.anchoredPosition = _shownAnchoredPos + hiddenOffset;
_canvasGroup.alpha = 0f;
_canvasGroup.interactable = false;
_canvasGroup.blocksRaycasts = false;
gameObject.SetActive(false);
}
private void KillActive()
{
if (_activeSequence.isAlive) _activeSequence.Stop();
_activeSequence = default;
}
private void OnDisable()
{
KillActive();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c960fbf3dc5a54f5399178665d20b1ce

View File

@@ -0,0 +1,19 @@
using VContainer.Unity;
namespace Darkmatter.Features.Coloring.UI
{
public class ColorPalettePresenter : IStartable
{
private readonly ColorPaletteView _view;
public ColorPalettePresenter(ColorPaletteView view)
{
_view = view;
}
public void Start()
{
throw new System.NotImplementedException();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0146ec43f1205479194de6448b58407e

View File

@@ -0,0 +1,8 @@
using UnityEngine;
namespace Darkmatter.Features.Coloring.UI
{
public class ColorPaletteView : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0db39fd358084d4caa1fb8f030523c4c
timeCreated: 1779969088

View File

@@ -0,0 +1,37 @@
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Darkmatter.Features.Coloring.UI
{
[RequireComponent(typeof(Image))]
public class ColorRegionView : MonoBehaviour, IPointerClickHandler
{
[field: SerializeField] public string RegionId { get; private set; }
[SerializeField, Range(0f, 1f)] private float alphaHitThreshold = 0.5f;
public Color Color => _image.color;
private Image _image;
private void Awake()
{
_image = GetComponent<Image>();
_image.alphaHitTestMinimumThreshold = alphaHitThreshold;
}
public void Initialize(string id, Color color)
{
RegionId = id;
_image.color = color;
}
public void SetColor(Color color)
{
_image.color = color;
}
public void OnPointerClick(PointerEventData eventData)
{
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7667901d8aea645d28b5125a2ed546ce

View File

@@ -6,19 +6,7 @@ namespace Darkmatter.Features.GameplayFlow.SceneRefs
public class GameplaySceneRefs : MonoBehaviour, IGameplaySceneRefs
{
[SerializeField] private RectTransform paperRoot;
[SerializeField] private RectTransform slotsParent;
[SerializeField] private RectTransform piecesParent;
[SerializeField] private RectTransform regionsParent;
[SerializeField] private RectTransform hudRoot;
[SerializeField] private RectTransform trayPanel;
public RectTransform PaperRoot => paperRoot;
public RectTransform SlotsParent => slotsParent;
public RectTransform PiecesParent => piecesParent;
public RectTransform RegionsParent => regionsParent;
public RectTransform HudRoot => hudRoot;
public RectTransform TrayPanel => trayPanel;
}
}

View File

@@ -1,6 +1,7 @@
using Darkmatter.Core.Contracts.Features.ShapeBuilder;
using Darkmatter.Core.Data.Static.Features.ShapeBuilder;
using Darkmatter.Features.ShapeBuilder.Systems;
using Darkmatter.Features.ShapeBuilder.UI;
using Darkmatter.Libs.Installers;
using UnityEngine;
using VContainer;
@@ -11,11 +12,20 @@ namespace Darkmatter.Features.ShapeBuilder.Installers
public class ShapeBuilderFeatureModule : MonoBehaviour, IModule
{
[SerializeField] private ShapeBuilderConfig config;
[SerializeField] private ShapeHolderView holderView;
public void Register(IContainerBuilder builder)
{
if (config != null)
builder.RegisterComponent(config);
if (holderView != null)
{
builder.RegisterComponent(holderView);
builder.RegisterEntryPoint<ShapeHolderPresenter>().WithParameter(holderView);
}
builder.Register<IShapePieceFactory, ShapePieceFactory>(Lifetime.Singleton);
builder.Register<IShapeBuilderController, ShapeBuilderController>(Lifetime.Singleton);
}
}

View File

@@ -0,0 +1,11 @@
using Darkmatter.Core.Data.Static.Features.ShapeBuilder;
using Darkmatter.Features.ShapeBuilder.UI;
using UnityEngine;
namespace Darkmatter.Features.ShapeBuilder.Systems
{
public interface IShapePieceFactory
{
ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker slot, Vector2 trayPos, bool preSnapped);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7eac815466c2c48b3b37bede20218b99

View File

@@ -4,11 +4,9 @@ using System.Linq;
using System.Threading;
using Cysharp.Threading.Tasks;
using Darkmatter.Core.Contracts.Features.DrawingCatalog;
using Darkmatter.Core.Contracts.Features.History;
using Darkmatter.Core.Contracts.Features.GameplayFlow;
using Darkmatter.Core.Contracts.Features.ShapeBuilder;
using Darkmatter.Core.Contracts.Services.Assets;
using Darkmatter.Core.Contracts.Services.Audio;
using Darkmatter.Core.Contracts.Features.GameplayFlow;
using Darkmatter.Core.Data.Signals.Features.ShapeBuilder;
using Darkmatter.Core.Data.Static.Features.ShapeBuilder;
using Darkmatter.Features.ShapeBuilder.UI;
@@ -30,38 +28,32 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
private IDisposable _snappedSub;
private readonly List<string> _snappedPieceIds = new();
private readonly ShapeBuilderConfig _cfg;
private readonly ISfxPlayer _sfx;
private readonly List<ShapePiece> _pieces = new();
private readonly IEventBus _bus;
private readonly IUndoStack _undo;
private readonly IAssetProviderService _assetProviderService;
private readonly IEventBus _eventBus;
private readonly IShapePieceFactory _factory;
private readonly ShapeHolderView _holder;
private readonly IGameplaySceneRefs _refs;
public ShapeBuilderController(
ShapeBuilderConfig cfg,
ISfxPlayer sfx,
IEventBus bus,
IUndoStack undo,
IAssetProviderService assetProviderService,
IEventBus eventBus,
IShapePieceFactory factory,
ShapeHolderView holder,
IGameplaySceneRefs refs)
{
_cfg = cfg;
_sfx = sfx;
_bus = bus;
_undo = undo;
_assetProviderService = assetProviderService;
_eventBus = eventBus;
_factory = factory;
_holder = holder;
_refs = refs;
}
public async UniTask InitializeAsync(CancellationToken cancellationToken)
{
await TryLoadPiecePrefabAsync(cancellationToken);
_snappedSub = _eventBus.Subscribe<PieceSnappedSignal>(OnPieceSnapped);
_snappedSub = _bus.Subscribe<PieceSnappedSignal>(OnPieceSnapped);
}
private void OnPieceSnapped(PieceSnappedSignal obj)
@@ -88,13 +80,15 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
await TryLoadPiecePrefabAsync(ct);
int count = template.Pieces.Count;
float trayW = _refs.TrayPanel.rect.width;
float trayW = _holder.SpawnWidth;
float pitch = trayW / (count + 1);
_currentTemplateId = template.Id;
_expected = count;
_snapped = 0;
_bus.Publish(new ShapeBuilderStartedSignal(template.Id));
CreateShapePieceInstances(template, preSnappedIds, count, slots, pitch, trayW);
CheckIfShapeAssembled();
@@ -117,11 +111,8 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
var trayPos = new Vector2(pitch * (i + 1) - trayW * 0.5f, 0f);
bool preSnapped = preSnappedIds != null && preSnappedIds.Contains(shape.Id);
var go = Object.Instantiate(_piecePrefab, _refs.TrayPanel);
go.name = $"Piece_{shape.Id}";
var piece = go.GetComponent<ShapePiece>();
piece.Setup(shape, slot, _cfg, _sfx, _bus, _undo, trayPos, preSnapped);
var piece = _factory.Create(_piecePrefab, shape, slot, trayPos, preSnapped);
_pieces.Add(piece);
if (preSnapped) _snapped++;
}
@@ -159,6 +150,17 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
public void Clear()
{
foreach (var piece in _pieces)
if (piece != null) Object.Destroy(piece.gameObject);
_pieces.Clear();
if (_drawingInstance != null) Object.Destroy(_drawingInstance);
_drawingInstance = null;
_snappedPieceIds.Clear();
_snapped = 0;
_expected = 0;
_currentTemplateId = null;
}
public void Dispose()

View File

@@ -0,0 +1,42 @@
using Darkmatter.Core.Contracts.Features.History;
using Darkmatter.Core.Contracts.Services.Audio;
using Darkmatter.Core.Data.Static.Features.ShapeBuilder;
using Darkmatter.Features.ShapeBuilder.UI;
using Darkmatter.Libs.Observer;
using UnityEngine;
namespace Darkmatter.Features.ShapeBuilder.Systems
{
public sealed class ShapePieceFactory : IShapePieceFactory
{
private readonly ShapeHolderView _holder;
private readonly ShapeBuilderConfig _cfg;
private readonly ISfxPlayer _sfx;
private readonly IEventBus _bus;
private readonly IUndoStack _undo;
public ShapePieceFactory(
ShapeHolderView holder,
ShapeBuilderConfig cfg,
ISfxPlayer sfx,
IEventBus bus,
IUndoStack undo)
{
_holder = holder;
_cfg = cfg;
_sfx = sfx;
_bus = bus;
_undo = undo;
}
public ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker slot, Vector2 trayPos, bool preSnapped)
{
var go = Object.Instantiate(prefab, _holder.SpawnRoot);
go.name = $"Piece_{shape.Id}";
var piece = go.GetComponent<ShapePiece>();
piece.Setup(shape, slot, _cfg, _sfx, _bus, _undo, trayPos, preSnapped);
return piece;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 34f1057e9a8774bbbaf0aac5ac1ae1a8

View File

@@ -0,0 +1,35 @@
using System;
using Darkmatter.Core.Data.Signals.Features.ShapeBuilder;
using Darkmatter.Libs.Observer;
using VContainer.Unity;
namespace Darkmatter.Features.ShapeBuilder.UI
{
public class ShapeHolderPresenter : IStartable, IDisposable
{
private readonly ShapeHolderView _view;
private readonly IEventBus _bus;
private IDisposable _startedSub;
private IDisposable _assembledSub;
public ShapeHolderPresenter(ShapeHolderView view, IEventBus bus)
{
_view = view;
_bus = bus;
}
public void Start()
{
_view.HideInstant();
_startedSub = _bus.Subscribe<ShapeBuilderStartedSignal>(_ => _view.Show());
_assembledSub = _bus.Subscribe<ShapeAssembledSignal>(_ => _view.Hide());
}
public void Dispose()
{
_startedSub?.Dispose();
_assembledSub?.Dispose();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bdc42ff3cf54249eba34beb822f4e9fb

View File

@@ -0,0 +1,77 @@
using PrimeTween;
using UnityEngine;
namespace Darkmatter.Features.ShapeBuilder.UI
{
[RequireComponent(typeof(CanvasGroup))]
public class ShapeHolderView : MonoBehaviour
{
[SerializeField] private RectTransform spawnRoot;
[SerializeField] private RectTransform animatedRoot;
[SerializeField] private float showDuration = 0.35f;
[SerializeField] private float hideDuration = 0.25f;
[SerializeField] private Vector2 hiddenOffset = new(1500f, 0f);
private CanvasGroup _canvasGroup;
private Vector2 _shownAnchoredPos;
private Sequence _activeSequence;
public RectTransform SpawnRoot => spawnRoot;
public float SpawnWidth => spawnRoot.rect.width;
private void Awake()
{
_canvasGroup = GetComponent<CanvasGroup>();
if (animatedRoot == null) animatedRoot = (RectTransform)transform;
_shownAnchoredPos = animatedRoot.anchoredPosition;
}
public Sequence Show()
{
KillActive();
gameObject.SetActive(true);
_canvasGroup.interactable = true;
_canvasGroup.blocksRaycasts = true;
_activeSequence = Sequence.Create()
.Group(Tween.UIAnchoredPosition(animatedRoot, _shownAnchoredPos, showDuration, Ease.OutBack))
.Group(Tween.Alpha(_canvasGroup, 1f, showDuration, Ease.OutQuad));
return _activeSequence;
}
public Sequence Hide()
{
KillActive();
_canvasGroup.interactable = false;
_canvasGroup.blocksRaycasts = false;
var hiddenPos = _shownAnchoredPos + hiddenOffset;
_activeSequence = Sequence.Create()
.Group(Tween.UIAnchoredPosition(animatedRoot, hiddenPos, hideDuration, Ease.InQuad))
.Group(Tween.Alpha(_canvasGroup, 0f, hideDuration, Ease.InQuad))
.ChainCallback(() => gameObject.SetActive(false));
return _activeSequence;
}
public void HideInstant()
{
KillActive();
animatedRoot.anchoredPosition = _shownAnchoredPos + hiddenOffset;
_canvasGroup.alpha = 0f;
_canvasGroup.interactable = false;
_canvasGroup.blocksRaycasts = false;
gameObject.SetActive(false);
}
private void KillActive()
{
if (_activeSequence.isAlive) _activeSequence.Stop();
_activeSequence = default;
}
private void OnDisable()
{
KillActive();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7763b07d532494bda8fa4cc10344dbd6

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []

View File

@@ -119,6 +119,19 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -109,6 +109,32 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -109,6 +109,32 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -109,6 +109,32 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -109,6 +109,32 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -109,6 +109,32 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

View File

@@ -109,6 +109,32 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:

245
Readme.md
View File

@@ -440,7 +440,7 @@ public interface IDrawingTemplate {
public readonly struct ColorRegionDTO {
public string RegionId { get; }
public Sprite Sprite { get; } // assigned to Image.sprite
public Vector2 AnchoredPosition { get; } // canvas units, relative to RegionsParent
public Vector2 AnchoredPosition { get; } // canvas units, relative to PaperRoot (region's authored parent)
public Vector2 SizeDelta { get; } // canvas units
public Color InitialColor { get; } // usually white
// Hit shape comes from the sprite alpha — set Image.alphaHitTestMinimumThreshold = 0.5.
@@ -496,27 +496,26 @@ public readonly struct PaintCommandDTO {
### Scene composition (no Paper feature)
The paper area is just a `PaperPanel` GameObject in the `ColorBook.unity` scene with child `RectTransform`s for slots, pieces, and regions. There is **no `IPaperSurface` contract** and no Paper feature. Features that need to parent UI under one of these roots read them from a small `ColorBookSceneRefs : MonoBehaviour` that the scene scope registers as a singleton:
The paper area is just a `PaperPanel` GameObject in the `Gameplay.unity` scene. There is **no `IPaperSurface` contract** and no Paper feature. The single cross-feature anchor — `PaperRoot` — is exposed via a small `GameplaySceneRefs : MonoBehaviour, IGameplaySceneRefs`:
```csharp
public sealed class ColorBookSceneRefs : MonoBehaviour {
[SerializeField] public RectTransform PaperRoot;
[SerializeField] public RectTransform SlotsParent;
[SerializeField] public RectTransform PiecesParent;
[SerializeField] public RectTransform RegionsParent;
[SerializeField] public Camera CaptureCamera; // disabled, used by ICaptureService
[SerializeField] public RectTransform HudRoot;
[SerializeField] public RectTransform TrayPanel;
public sealed class GameplaySceneRefs : MonoBehaviour, IGameplaySceneRefs {
[SerializeField] private RectTransform paperRoot;
public RectTransform PaperRoot => paperRoot;
}
```
Registered once via `ColorBookLifetimeScope`:
Scene refs hold only **what must be shared across features**. Everything else — the shape tray, the color palette, the drawing-instance prefab (which carries its own `SlotMarker`s + `ColorRegionView`s as children) — is owned by its feature's **holder view**. This keeps `GameplaySceneRefs` from becoming a god object as features are added.
The drawing prefab is instantiated under `PaperRoot`; slot markers and color regions are pre-authored as children of that prefab, so neither `SlotsParent` nor `RegionsParent` needs its own ref. The `CaptureCamera` will live on a separate scene MB (planned, owned by the Capture feature).
Registered once via `GameplayLifetimeScope`:
```csharp
builder.RegisterInstance(_sceneRefs);
builder.RegisterComponent<IGameplaySceneRefs>(_sceneRefs);
```
Features inject `ColorBookSceneRefs` directly and read the rects they need. This keeps the scene refs visible in the inspector while avoiding a one-feature wrapper that adds nothing.
Features inject `IGameplaySceneRefs` (or their own holder views) directly.
### History
@@ -618,16 +617,21 @@ public readonly struct PaperSavedSignal {
- 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.
- **`ShapeHolderView`** owns the tray `RectTransform` + slide-in/out animation (PrimeTween — `Tween.UIAnchoredPosition` + `Tween.Alpha` on `CanvasGroup`). Default hide direction is right (off-screen +X), tunable in inspector.
- **`ShapeHolderPresenter`** subscribes to `ShapeBuilderStartedSignal``view.Show()` and `ShapeAssembledSignal` `view.Hide()`. No controller poking the view directly.
- **`IShapePieceFactory`** (`ShapePieceFactory` impl) owns the per-piece dependencies (`ShapeBuilderConfig`, `ISfxPlayer`, `IEventBus`, `IUndoStack`) and the spawn parent (read from `ShapeHolderView.SpawnRoot`). Factory creates one `ShapePiece` MB and runs its `Setup`. Controller calls `factory.Create(prefab, shape, slot, trayPos, preSnapped)` per piece.
- `ShapeBuilderController` only orchestrates: loads piece prefab via `IAssetProviderService`, computes tray positions from `holder.SpawnWidth`, publishes `ShapeBuilderStartedSignal` before spawn, and listens for `PieceSnappedSignal` to count down to `ShapeAssembledSignal`. If `progress.Phase == ShapeBuilding`, pieces in `progress.SnappedPieces` are passed to the factory with `preSnapped: true`.
- `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.
- 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 `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`).
- Regions are pre-authored as children of the drawing prefab (under `PaperRoot`). Each is a `ColorRegionView` (UI `Image` + `IPointerClickHandler`). On `Awake`, the view sets `Image.alphaHitTestMinimumThreshold` from a serialized field (default `0.01f`; tune up to `0.5f` for tighter hits) so taps on transparent pixels pass through to the next region below.
- **`ColorPaletteHolderView`** owns the palette container `RectTransform` + slide-in/out animation (same pattern as `ShapeHolderView``Tween.UIAnchoredPosition` + `Tween.Alpha` on `CanvasGroup`, hides off-screen right).
- **`ColorPaletteHolderPresenter`** subscribes to `ShapeAssembledSignal``view.Show()`. Hide is invoked from scene tear-down or future phase-exit hook.
- **`ColorPaletteView` + `ColorPalettePresenter`** (planned): view spawns one color button per `IColorPalette.Colors` entry under the holder's `SpawnRoot`; presenter wires button click → `ColoringStateRepository.CurrentColor`.
- Each region's `OnPointerClick``ColoringController.PaintRegion(view)`.
- 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`.
@@ -1083,7 +1087,7 @@ Drag the scene's installer MonoBehaviours into `sceneModules[]`:
Each registers its own classes via `IServiceModule.Register(IContainerBuilder)`.
> If a scope needs a non-installer reference (e.g. a `ColorBookSceneRefs` MB holding camera + roots), expose it as a separate `[SerializeField]` and `builder.RegisterInstance(...)` it inside the scope's `Configure`. Don't put scene refs inside an installer — keep installers stateless across scenes.
> If a scope needs a non-installer reference (e.g. a `GameplaySceneRefs` MB holding `PaperRoot`), expose it as a separate `[SerializeField]` and `builder.RegisterComponent(...)` it inside the scope's `Configure`. Don't put scene refs inside an installer — keep installers stateless across scenes.
---
@@ -1114,7 +1118,7 @@ Convention:
- `MonoBehaviour` lives on a GameObject under the scope's hierarchy; dragged into the scope's `serviceModules[]` / `sceneModules[]` inspector list.
- Method name is `Register`, not `Install`. There is **no `IInstaller`** in this project — uses `IServiceModule` from [Libs.Installers](Assets/Darkmatter/Code/Libs/Installers/IServiceModule.cs).
- Registers only its own types. Never touches another feature's types.
- If the installer needs to wire scene-bound MonoBehaviours into DI, expose them as `[SerializeField]` fields on the installer itself and `builder.RegisterInstance<IFoo>(_foo)` them. `ColorBookSceneRefs` (§32.13) is registered this way directly from the scope's serialized field.
- If the installer needs to wire scene-bound MonoBehaviours into DI, expose them as `[SerializeField]` fields on the installer itself and `builder.RegisterComponent<IFoo>(_foo)` them. `GameplaySceneRefs` (§32.13) is registered this way directly from the scope's serialized field. Per-feature holder views (e.g. `ShapeHolderView`, `ColorPaletteHolderView`) are registered the same way from each feature's `IServiceModule`.
---
@@ -1372,18 +1376,23 @@ private void SnapInstantly()
### Spawn loop in `ShapeBuilderController.BuildAsync`
The controller no longer parents pieces directly nor knows about `Setup` dependencies. It delegates to `IShapePieceFactory`, which owns the parent (`ShapeHolderView.SpawnRoot`) and the per-piece deps (cfg/sfx/bus/undo). Tray width is read from the holder, not from scene refs.
```csharp
var preSnappedIds = progress?.snappedPieces;
foreach (var (shape, idx) in template.Pieces.Select((s, i) => (s, i)))
_bus.Publish(new ShapeBuilderStartedSignal(template.Id)); // ShapeHolderPresenter → view.Show()
int count = template.Pieces.Count;
float trayW = _holder.SpawnWidth;
float pitch = trayW / (count + 1);
for (int i = 0; i < count; i++)
{
var go = Instantiate(_piecePrefab, _refs.TrayPanel);
var piece = go.GetComponent<ShapePiece>();
var shape = template.Pieces[i];
var slot = FindSlotForShape(slots, shape);
var trayPos = _trayLayout.GetSlotPosition(idx, template.Pieces.Count);
var trayPos = new Vector2(pitch * (i + 1) - trayW * 0.5f, 0f);
var preSnapped = preSnappedIds != null && preSnappedIds.Contains(shape.Id);
piece.Setup(shape, slot, _cfg, _sfx, _bus, trayPos, preSnapped);
_alive.Add(piece);
_factory.Create(_piecePrefab, shape, slot, trayPos, preSnapped);
}
```
@@ -1487,8 +1496,8 @@ Toddler-mode error UI:
| `AddressableAssetProviderService` | Services | `Services.Assets` |
| `NativeGallerySaveService` | Services | `Services.Gallery` |
| `CaptureService` | Services | `Services.Capture` |
| `ColoringController`, `ColoringStateRepository`, `ColorRegionView`, `PaintRegionCommand` | Features | `Features.Coloring` |
| `ShapePiece`, `SlotMarker`, `ShapeBuilderController`, `TrayLayout` | Features | `Features.ShapeBuilder` |
| `ColoringController`, `ColoringStateRepository`, `ColorRegionView`, `ColorPaletteHolderView`, `ColorPaletteHolderPresenter`, `PaintRegionCommand` | Features | `Features.Coloring` |
| `ShapePiece`, `SlotMarker`, `ShapeBuilderController`, `IShapePieceFactory`, `ShapePieceFactory`, `ShapeHolderView`, `ShapeHolderPresenter` | Features | `Features.ShapeBuilder` |
| `AddressableDrawingTemplateCatalog` | Features | `Features.DrawingTemplate` |
| `DrawingCatalogController`, `DrawingCatalogPresenter`, `DrawingCatalogView`, `CatalogItemVM` | Features | `Features.DrawingCatalog` |
| `ColorbookFlowController` | Features | `Features.Colorbook` |
@@ -1579,11 +1588,11 @@ Comprehensive index — every script (existing or planned) grouped by its module
| `Features/DrawingCatalog/Installers/` | `DrawingCatalogFeatureModule` | ✅ |
| `Features/DrawingTemplate/Systems/` | `AddressableDrawingTemplateCatalog` | ✅ |
| `Features/DrawingTemplate/Installers/` | `DrawingTemplateFeatureModule` | ✅ |
| `Features/ShapeBuilder/UI/` | `ShapePiece`, `SlotMarker` | ✅ |
| `Features/ShapeBuilder/Systems/` | `ShapeBuilderController`, `TrayLayout` | ⚠️ |
| `Features/ShapeBuilder/UI/` | `ShapePiece`, `SlotMarker`, `ShapeHolderView`, `ShapeHolderPresenter` | ✅ |
| `Features/ShapeBuilder/Systems/` | `ShapeBuilderController`, `IShapePieceFactory`, `ShapePieceFactory` | |
| `Features/ShapeBuilder/Installers/` | `ShapeBuilderFeatureModule` | ✅ |
| `Features/Coloring/Systems/` | `ColoringController`, `ColoringStateRepository`, `ColorRegionFactory` | ⚠️ |
| `Features/Coloring/UI/` | `ColorRegionView`, `ColorPaletteView`, `ColorPalettePresenter` | ⚠️ |
| `Features/Coloring/Systems/` | `ColoringController`, `ColoringStateRepository` | ⚠️ planned |
| `Features/Coloring/UI/` | `ColorRegionView`, `ColorPaletteHolderView`, `ColorPaletteHolderPresenter` ✅, `ColorPaletteView` ⚠️, `ColorPalettePresenter` ⚠️ | |
| `Features/Coloring/Commands/` | `PaintRegionCommand` | ⚠️ |
| `Features/Coloring/Installers/` | `ColoringFeatureModule` | ⚠️ |
| `Features/Capture/Systems/` | `CaptureController` (light wrapper around `ICaptureService`) | ⚠️ |
@@ -1604,7 +1613,7 @@ Comprehensive index — every script (existing or planned) grouped by its module
| `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 |
| `Features/GameplayFlow/SceneRefs/` | `GameplaySceneRefs` (PaperRoot only — per-feature containers moved to holder views) | ✅ |
---
@@ -1689,7 +1698,7 @@ public interface ICaptureService {
#### Removed contracts
- `IPaperRig`, `IArtInputBridge`, `IPaperSurface` — paper is just RectTransforms in the scene now, exposed via `ColorBookSceneRefs`. No contract.
- `IPaperRig`, `IArtInputBridge`, `IPaperSurface` — paper is just RectTransforms in the scene now, exposed via `GameplaySceneRefs.PaperRoot`. No contract.
- `SavedArtworkDTO`, `IGalleryService.ListAsync/LoadFullAsync/LoadThumbnailAsync/DeleteAsync/GetLatestThumbnailAsync` — no app-side gallery store.
- `ArtworkCapturedSignal`, `ArtworkSavedSignal` — replaced by `PaperCapturedSignal` / `PaperSavedSignal` (templateId only, no DTO).
@@ -2018,53 +2027,59 @@ public sealed class ShapeBuilderConfig : ScriptableObject
}
```
#### `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.**
#### `ShapeBuilderController` *(Systems — ✅ exists)*
Orchestrates per-template build: loads piece prefab, instantiates drawing layout under `PaperRoot`, computes tray positions from `ShapeHolderView.SpawnWidth`, publishes `ShapeBuilderStartedSignal`, delegates per-piece spawn to `IShapePieceFactory`. Subscribes to `PieceSnappedSignal` and fires `ShapeAssembledSignal` when count matches expected.
```csharp
// fields: IDrawingTemplateCatalog _catalog, IAssetProviderService _assets,
// GameplaySceneRefs _refs, TrayLayout _trayLayout,
// ShapeBuilderConfig _cfg, ISfxPlayer _sfx, IEventBus _bus
public sealed class ShapeBuilderController : IAsyncStartable, IDisposable
// fields: IEventBus _bus, IAssetProviderService _assetProviderService,
// IShapePieceFactory _factory, ShapeHolderView _holder, IGameplaySceneRefs _refs
public sealed class ShapeBuilderController : IShapeBuilderController, 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);
}
public IReadOnlyList<ShapePiece> Alive => _alive;
public async UniTask InitializeAsync(CancellationToken ct); // load prefab + subscribe
public IReadOnlyCollection<string> GetSnappedPieceIds(); // for save records
public async UniTask BuildAsync(
IDrawingTemplate template,
IReadOnlyCollection<string> preSnappedIds = null);
IReadOnlyCollection<string> preSnappedIds,
CancellationToken ct = default);
public void Reset(); // despawns all pieces; called by GameplayFlowController on Teardown
public void Clear(); // destroys current drawing instance + resets counters
}
// sub: PieceSnappedSignal
// pub: ShapeAssembledSignal
// pub: ShapeBuilderStartedSignal (before spawn), ShapeAssembledSignal (when all locked)
```
- **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`.
- **Pre-snap on resume:** if `preSnappedIds.Contains(shape.Id)`, the factory is called with `preSnapped: true``ShapePiece.SnapInstantly()` lands it in the slot at scope start.
- **Tray width source:** read from `_holder.SpawnWidth` — the controller never touches `_refs` for HUD geometry.
#### `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.
#### `IShapePieceFactory` / `ShapePieceFactory` *(Systems — ✅ exists)*
Encapsulates piece instantiation. Owns the parent (from `ShapeHolderView.SpawnRoot`) and the per-piece deps so the controller stays focused on flow.
```csharp
public sealed class TrayLayout
{
[SerializeField] private RectTransform trayRect;
public Vector2 GetSlotPosition(int index, int total);
public interface IShapePieceFactory {
ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker slot, Vector2 trayPos, bool preSnapped);
}
// fields: ShapeHolderView _holder, ShapeBuilderConfig _cfg, ISfxPlayer _sfx, IEventBus _bus, IUndoStack _undo
public sealed class ShapePieceFactory : IShapePieceFactory {
public ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker slot, Vector2 trayPos, bool preSnapped) {
var go = Object.Instantiate(prefab, _holder.SpawnRoot);
var piece = go.GetComponent<ShapePiece>();
piece.Setup(shape, slot, _cfg, _sfx, _bus, _undo, trayPos, preSnapped);
return piece;
}
}
```
#### `ShapeHolderView` / `ShapeHolderPresenter` *(UI — ✅ exists)*
View owns the tray container `RectTransform` (`SpawnRoot`) and a PrimeTween `Sequence` driving slide + alpha on a `CanvasGroup`. Default hide direction is `+X = 1500` (slide right off-screen), tunable per inspector. Public: `SpawnRoot`, `SpawnWidth`, `Show()`, `Hide()`, `HideInstant()`. Presenter is an `IStartable, IDisposable`:
- On `Start`: `view.HideInstant()`, subscribe `ShapeBuilderStartedSignal``Show`, subscribe `ShapeAssembledSignal``Hide`.
- On `Dispose`: dispose subscriptions.
#### Removed / not needed
- **`TrayLayout`** — was a stateless tray-position helper. Tray positions are now computed inline from `holder.SpawnWidth` (~3 lines).
- **`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.
@@ -2087,15 +2102,15 @@ public sealed class ColoringStateRepository {
```
- **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.
#### `ColoringController` *(Systems — planned)* — implements `IColoringController`
Wires pre-authored `ColorRegionView` children, applies saved colors on resume, builds and pushes `PaintRegionCommand` instances on click.
```csharp
// fields: IUndoStack _undo, ColoringStateRepository _state, ColorRegionFactory _factory,
// GameplaySceneRefs _refs, IEventBus _bus
// fields: IUndoStack _undo, ColoringStateRepository _state, IGameplaySceneRefs _refs, IEventBus _bus
public interface IColoringController {
// Spawn regions on the paper. Pass non-null savedColors to restore colors
// from a DrawingProgress record; null = use ColorRegionDTO.InitialColor.
UniTask SpawnRegionsAsync(
// Initialize regions on the paper after the drawing prefab is instantiated.
// Pass non-null savedColors to restore colors from a DrawingProgress record;
// null = use ColorRegionDTO.InitialColor.
UniTask InitializeRegionsAsync(
IDrawingTemplate template,
IReadOnlyDictionary<string, Color> savedColors = null);
@@ -2104,39 +2119,52 @@ public interface IColoringController {
// Snapshot current paint state for save records (see §13).
IReadOnlyDictionary<string, Color> GetCurrentColors();
void Clear(); // despawn all regions
void Clear(); // detach handlers
}
// sub: ShapeAssembledSignal (via flow controller, not direct)
// pub: ColorAppliedSignal (via PaintRegionCommand)
```
Spawns each region as a UI `Image` under `_refs.RegionsParent`. No `Physics2D`.
Regions are pre-authored as children of the drawing prefab (parented under `PaperRoot`) — the controller does not spawn them. Each `ColorRegionView` sets its own `Image.alphaHitTestMinimumThreshold` in `Awake`; the controller subscribes to each view's click event and routes to `PaintRegion`. 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)*
#### `ColorRegionView : MonoBehaviour, IPointerClickHandler` *(UI — ✅ exists)*
UI Image with alpha-based hit detection. Tap routes through Unity's EventSystem directly to `OnPointerClick`.
```csharp
[RequireComponent(typeof(Image))]
public sealed class ColorRegionView : MonoBehaviour, IPointerClickHandler {
public string RegionId { get; }
[field: SerializeField] public string RegionId { get; private set; }
[SerializeField, Range(0f, 1f)] private float alphaHitThreshold = 0.01f;
public Color Color => _image.color;
public void Initialize(ColorRegionDTO dto, IColoringController controller);
public void SetColor(Color c); // setter only; no logic
public void OnPointerClick(PointerEventData e) => _controller.PaintRegion(this);
private void Awake() {
_image = GetComponent<Image>();
_image.alphaHitTestMinimumThreshold = alphaHitThreshold; // pass-through transparent pixels
}
public void Initialize(string id, Color color);
public void SetColor(Color color);
public void OnPointerClick(PointerEventData e); // routes to ColoringController.PaintRegion(this)
}
```
- **Required sprite setup:** sprite import inspector → **Read/Write Enabled = on**, **Generate Physics Shape = off** (not needed). `Image.alphaHitTestMinimumThreshold = 0.5f` on Initialize so taps on transparent pixels pass through to the next region below.
- **Sibling order matters** for stacked regions — top sibling gets first crack at the click; with alpha hit-test, transparent areas defer correctly.
- **Required sprite import:** **Read/Write Enabled = on**, **Mesh Type = Full Rect**, keep alpha channel (RGBA32 or Automatic-with-alpha). Without these, Unity throws `UnityException: Texture is not readable` on first hover/click.
- **Threshold tuning:** default `0.01f` is very permissive (any non-fully-transparent pixel counts). Raise toward `0.5f` for tighter hits when regions overlap visually.
- **Sibling order matters** for stacked regions — top sibling gets first crack at the click; with alpha hit-test, transparent areas defer correctly to siblings below.
No `ColoringInputBinder` class needed. Unity's EventSystem fires `OnPointerClick` on the topmost UI element under the pointer that consumes it — exactly what we want.
No `ColoringInputBinder` class needed. Unity's EventSystem fires `OnPointerClick` on the topmost UI element under the pointer whose `Image.alphaHitTestMinimumThreshold` is met.
#### `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`.
#### `ColorPaletteView`, `ColorPalettePresenter` *(UI — stubs exist)*
Files exist but bodies are empty. Target shape: view spawns one color button per `IColorPalette.Colors` entry under `ColorPaletteHolderView.SpawnRoot`; presenter binds `ColoringStateRepository.SelectedIndexChanged`view highlight, and view click events → repository.
#### `ColorRegionFactory` *(Systems)*
Mirror of `ShapePieceFactory` for regions. Pool-friendly.
#### `ColorPaletteHolderView` / `ColorPaletteHolderPresenter` *(UI — ✅ exists)*
View owns the palette container `RectTransform` (`SpawnRoot`) + slide/fade show-hide animation (same `Tween.UIAnchoredPosition` + `Tween.Alpha` pattern as `ShapeHolderView`, default hide off-screen right). Presenter subscribes `ShapeAssembledSignal``view.Show()`. Hide is invoked externally (scope tear-down or future phase-exit signal).
#### Removed / not needed
- **`ColorRegionFactory`** — regions are pre-authored as children of the drawing prefab. The controller wires existing views; it doesn't spawn anything.
---
@@ -2219,7 +2247,7 @@ Pure in-memory holder used by the service. Separated so tests can inspect state
// IColoringController _coloring
// CaptureController _capture
// IProgressionService _progression
// ColorBookSceneRefs _refs (panel roots to enable/disable)
// IGameplaySceneRefs _refs (PaperRoot only — panel show/hide owned by holder views)
// Fsm<ColorBookState> _fsm
```
- **State table:**
@@ -2250,7 +2278,7 @@ public sealed class AppBoot : IAsyncStartable {
#### LifetimeScopes
- `RootLifetimeScope` — ✅ exists ([source](Assets/Darkmatter/Code/App/LifetimeScopes/RootLifetimeScope.cs)). Iterates a serialized `MonoBehaviour[] serviceModules` and calls `Register` on each `IServiceModule`. Persists for app lifetime.
- `MainMenuLifetimeScope` — planned. Same pattern as Root (serialized installer list, no hardcoded registrations).
- `ColorBookLifetimeScope` — planned. Same pattern; installer list includes feature installers + the flow controller installer. Also has a `[SerializeField] ColorBookSceneRefs _sceneRefs;` and registers it via `builder.RegisterInstance(_sceneRefs)`.
- `GameplayLifetimeScope` — planned. Same pattern; installer list includes feature installers + the flow controller installer. Also has a `[SerializeField] GameplaySceneRefs _sceneRefs;` and registers it via `builder.RegisterComponent<IGameplaySceneRefs>(_sceneRefs)`.
All scope classes are thin: a serialized installer-MonoBehaviour list (+ optional scene refs as separate fields) and a `Configure(IContainerBuilder)` that iterates and calls `Register`.
@@ -2258,23 +2286,38 @@ All scope classes are thin: a serialized installer-MonoBehaviour list (+ optiona
### 32.13 Cross-cutting types
#### `ColorBookSceneRefs : MonoBehaviour` *(App — planned)*
The single source of scene-bound Unity references for the ColorBook scene. Registered in `ColorBookLifetimeScope` via `builder.RegisterInstance(_sceneRefs)` so features don't `Find` things.
#### `GameplaySceneRefs : MonoBehaviour, IGameplaySceneRefs` *(Features/GameplayFlow/SceneRefs — ✅ exists)*
Single source of the one cross-feature scene anchor (`PaperRoot`). Per-feature UI containers (tray, palette) live on their own holder views — see §32.X. Registered in `GameplayLifetimeScope` via `builder.RegisterComponent<IGameplaySceneRefs>(_sceneRefs)`.
```csharp
public sealed class ColorBookSceneRefs : MonoBehaviour {
public RectTransform PaperRoot;
public RectTransform SlotsParent;
public RectTransform PiecesParent;
public RectTransform RegionsParent;
public RectTransform HudRoot;
public RectTransform TrayPanel;
public Camera CaptureCamera; // disabled — used by ICaptureService
public ColorPaletteView PaletteView; // optional inline ref
public HistoryButtonsView HistoryButtons;
public sealed class GameplaySceneRefs : MonoBehaviour, IGameplaySceneRefs {
[SerializeField] private RectTransform paperRoot;
public RectTransform PaperRoot => paperRoot;
}
```
Replaces the dropped `IPaperSurface` contract — features that need a paper-area RectTransform read it off this MB.
Replaces the dropped `IPaperSurface` contract. The drawing prefab (with `SlotMarker` + `ColorRegionView` children) is instantiated under `PaperRoot`; the `CaptureCamera` will live on its own scene MB owned by the Capture feature.
#### `ShapeHolderView : MonoBehaviour` + `ShapeHolderPresenter` *(Features/ShapeBuilder/UI — ✅ exists)*
Owns the tray container `RectTransform` (`SpawnRoot`) and the slide/fade show/hide animation (PrimeTween — `Tween.UIAnchoredPosition` + `Tween.Alpha` on `CanvasGroup`). Default hide offset is `+1500` on X (slide right off-screen), tunable per inspector. Exposes `SpawnRoot`, `SpawnWidth`, `Show()`, `Hide()`, `HideInstant()`. Presenter is an `IStartable` that subscribes:
- `ShapeBuilderStartedSignal``view.Show()`
- `ShapeAssembledSignal``view.Hide()`
Registered in `ShapeBuilderFeatureModule` via `builder.RegisterComponent(holderView)` + `builder.RegisterEntryPoint<ShapeHolderPresenter>().WithParameter(holderView)`.
#### `ColorPaletteHolderView : MonoBehaviour` + `ColorPaletteHolderPresenter` *(Features/Coloring/UI — ✅ exists)*
Same pattern as `ShapeHolderView` — owns the palette container `RectTransform` (`SpawnRoot`) and slide/fade animation. Presenter subscribes to `ShapeAssembledSignal``view.Show()`. Hide is invoked externally (scope tear-down or future phase-exit signal). Registered analogously in `ColoringFeatureModule`.
#### `IShapePieceFactory` / `ShapePieceFactory` *(Features/ShapeBuilder/Systems — ✅ exists)*
Encapsulates piece instantiation. Owns the per-piece dependencies and the spawn parent so the controller stays focused on flow.
```csharp
public interface IShapePieceFactory {
ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker slot, Vector2 trayPos, bool preSnapped);
}
// fields: ShapeHolderView _holder, ShapeBuilderConfig _cfg, ISfxPlayer _sfx, IEventBus _bus, IUndoStack _undo
public sealed class ShapePieceFactory : IShapePieceFactory { /* Instantiate under _holder.SpawnRoot, run piece.Setup(...) */ }
```
#### `IServiceModule` *(Libs/Installers — ✅ exists)*
```csharp
@@ -2307,10 +2350,12 @@ Implemented as `MonoBehaviour` per feature/service so scopes can drag them in th
| `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 | — |
| `ShapeBuilderController` | Feature.ShapeBuilder | Orchestrates per-template build, delegates spawn to factory, tracks snap count | bus, assets, factory, holder, refs |
| `IShapePieceFactory` / `ShapePieceFactory` | Feature.ShapeBuilder | Instantiates a `ShapePiece` under `ShapeHolderView.SpawnRoot` + runs `Setup` | holder, cfg, sfx, bus, undo |
| `ShapeHolderView` / `ShapeHolderPresenter` | Feature.ShapeBuilder | Tray container + slide/fade show/hide; presenter reacts to `ShapeBuilderStartedSignal` / `ShapeAssembledSignal` | bus |
| `ColoringStateRepository` | Feature.Coloring | Current color model | — |
| `ColoringController` | Feature.Coloring | Region spawn + paint cmd + autosave hook | undo, state, factory, refs, flow, bus |
| `ColoringController` | Feature.Coloring | Wires pre-authored regions + paint cmd + autosave hook | undo, state, refs, flow, bus |
| `ColorPaletteHolderView` / `ColorPaletteHolderPresenter` | Feature.Coloring | Palette container + slide/fade show/hide; presenter reacts to `ShapeAssembledSignal` | 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 |