diff --git a/Assets/Darkmatter/Code/Features/Coloring/UI/ColorPaletteHolderView.cs b/Assets/Darkmatter/Code/Features/Coloring/UI/ColorPaletteHolderView.cs index 000fd5d..159e9c2 100644 --- a/Assets/Darkmatter/Code/Features/Coloring/UI/ColorPaletteHolderView.cs +++ b/Assets/Darkmatter/Code/Features/Coloring/UI/ColorPaletteHolderView.cs @@ -48,7 +48,7 @@ namespace Darkmatter.Features.Coloring.UI _canvasGroup.interactable = true; _canvasGroup.blocksRaycasts = true; - _activeSequence = Sequence.Create() + _activeSequence = Sequence.Create(useUnscaledTime: true) .Group(Tween.UIAnchoredPosition(animatedRoot, _shownAnchoredPos, showDuration, Ease.OutBack)) .Group(Tween.Alpha(_canvasGroup, 1f, showDuration, Ease.OutQuad)); return _activeSequence; @@ -62,7 +62,7 @@ namespace Darkmatter.Features.Coloring.UI _canvasGroup.blocksRaycasts = false; var hiddenPos = _shownAnchoredPos + hiddenOffset; - _activeSequence = Sequence.Create() + _activeSequence = Sequence.Create(useUnscaledTime: true) .Group(Tween.UIAnchoredPosition(animatedRoot, hiddenPos, hideDuration, Ease.InQuad)) .Group(Tween.Alpha(_canvasGroup, 0f, hideDuration, Ease.InQuad)) .ChainCallback(() => gameObject.SetActive(false)); diff --git a/Assets/Darkmatter/Code/Features/GameplayFlow/Systems/GameplayFlowController.cs b/Assets/Darkmatter/Code/Features/GameplayFlow/Systems/GameplayFlowController.cs index e4fdd51..7c23fe4 100644 --- a/Assets/Darkmatter/Code/Features/GameplayFlow/Systems/GameplayFlowController.cs +++ b/Assets/Darkmatter/Code/Features/GameplayFlow/Systems/GameplayFlowController.cs @@ -79,6 +79,13 @@ namespace Darkmatter.Features.GameplayFlow.Systems _scopeCts = CancellationTokenSource.CreateLinkedTokenSource(cancellation); var ct = _scopeCts.Token; + // The in-editor AdMob placeholder interstitial pauses via Time.timeScale = 0 and is + // shown fire-and-forget during the Colorbook->Gameplay swap; the Colorbook scene unload + // destroys the placeholder before it can resume, stranding timeScale at 0 (gameplay and + // scaled tweens frozen). Gameplay must never start frozen, so restore it on entry. + // No-op on device, where the real ad clients don't touch timeScale. + Time.timeScale = 1f; + _templateId = _progression.LastOpenedTemplateId; if (string.IsNullOrEmpty(_templateId)) throw new Exception( diff --git a/Assets/Darkmatter/Code/Features/ShapeBuilder/Systems/ShapeBuilderController.cs b/Assets/Darkmatter/Code/Features/ShapeBuilder/Systems/ShapeBuilderController.cs index 7dddd7e..bf5bd72 100644 --- a/Assets/Darkmatter/Code/Features/ShapeBuilder/Systems/ShapeBuilderController.cs +++ b/Assets/Darkmatter/Code/Features/ShapeBuilder/Systems/ShapeBuilderController.cs @@ -29,6 +29,7 @@ namespace Darkmatter.Features.ShapeBuilder.Systems private GameObject _piecePrefab; private IDisposable _snappedSub; private IDisposable _unsnappedSub; + private readonly CancellationTokenSource _lifetimeCts = new(); private readonly List _snappedPieceIds = new(); private readonly List _pieces = new(); @@ -174,7 +175,32 @@ namespace Darkmatter.Features.ShapeBuilder.Systems private void CheckIfShapeAssembled() { if (_expected > 0 && _snapped == _expected) - _bus.Publish(new ShapeAssembledSignal(_currentTemplateId)); + PublishAssembledWhenSettledAsync().Forget(); + } + + private async UniTaskVoid PublishAssembledWhenSettledAsync() + { + var templateId = _currentTemplateId; + try + { + // Hold the handoff to coloring until the final piece's snap + // animation has finished playing. + await UniTask.WaitWhile(AnyPieceSettling, cancellationToken: _lifetimeCts.Token); + } + catch (OperationCanceledException) + { + return; + } + + if (templateId == _currentTemplateId) + _bus.Publish(new ShapeAssembledSignal(templateId)); + } + + private bool AnyPieceSettling() + { + foreach (var piece in _pieces) + if (piece != null && piece.IsSnapSettling) return true; + return false; } private async UniTask TryLoadPiecePrefabAsync(CancellationToken ct) @@ -241,6 +267,8 @@ namespace Darkmatter.Features.ShapeBuilder.Systems { _snappedSub?.Dispose(); _unsnappedSub?.Dispose(); + _lifetimeCts.Cancel(); + _lifetimeCts.Dispose(); } } } diff --git a/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapeHolderView.cs b/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapeHolderView.cs index 2a87f76..ec8fde0 100644 --- a/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapeHolderView.cs +++ b/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapeHolderView.cs @@ -53,7 +53,7 @@ namespace Darkmatter.Features.ShapeBuilder.UI _canvasGroup.interactable = true; _canvasGroup.blocksRaycasts = true; - _activeSequence = Sequence.Create() + _activeSequence = Sequence.Create(useUnscaledTime: true) .Group(Tween.UIAnchoredPosition(animatedRoot, _shownAnchoredPos, showDuration, Ease.OutBack)) .Group(Tween.Alpha(_canvasGroup, 1f, showDuration, Ease.OutQuad)); return _activeSequence; @@ -67,7 +67,7 @@ namespace Darkmatter.Features.ShapeBuilder.UI _canvasGroup.blocksRaycasts = false; var hiddenPos = _shownAnchoredPos + hiddenOffset; - _activeSequence = Sequence.Create() + _activeSequence = Sequence.Create(useUnscaledTime: true) .Group(Tween.UIAnchoredPosition(animatedRoot, hiddenPos, hideDuration, Ease.InQuad)) .Group(Tween.Alpha(_canvasGroup, 0f, hideDuration, Ease.InQuad)) .ChainCallback(() => gameObject.SetActive(false)); diff --git a/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapePiece.cs b/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapePiece.cs index 37bc837..465ffec 100644 --- a/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapePiece.cs +++ b/Assets/Darkmatter/Code/Features/ShapeBuilder/UI/ShapePiece.cs @@ -1,3 +1,4 @@ +using Cysharp.Threading.Tasks; using Darkmatter.Core.Contracts.Features.History; using Darkmatter.Core.Contracts.Services.Audio; using Darkmatter.Core.Data.Signals.Features.ShapeBuilder; @@ -45,6 +46,7 @@ namespace Darkmatter.Features.ShapeBuilder.UI private Vector2 _dragSizeDelta; private Vector3 _dragLocalScale; private Sequence _previewSeq; + private Sequence _snapSettle; private bool _locked; private bool _inPreview; @@ -57,6 +59,7 @@ namespace Darkmatter.Features.ShapeBuilder.UI public Vector2 TrayPosition => _trayPos; public Vector2 TraySize => _traySize; public SlotMarker ActiveSlot => _activeSlot; + public bool IsSnapSettling => _snapSettle.isAlive; public void ReassignActiveSlot(SlotMarker slot) => _activeSlot = slot; @@ -236,12 +239,43 @@ namespace Darkmatter.Features.ShapeBuilder.UI internal void SnapInternal() { + _locked = true; + image.raycastTarget = false; + if (_activeSlot != null) _activeSlot.SetOccupied(true); + + _sfx.Play(SfxId.ShapeSnap); + + // Let the in-flight settle animation play to completion before locking the + // piece into its slot, so the final piece lands smoothly instead of being + // cut short the instant it's dropped. + if (_previewSeq.isAlive) + { + _snapSettle = _previewSeq; + SettleThenFinalizeAsync().Forget(); + } + else + { + FinalizeSnapPose(); + } + + _bus.Publish(new PieceSnappedSignal(_shape.Id)); + } + + private async UniTaskVoid SettleThenFinalizeAsync() + { + await _snapSettle; // completes when the settle finishes or is stopped + FinalizeSnapPose(); + } + + private void FinalizeSnapPose() + { + if (this == null || RectTransform == null) return; + _snapSettle = default; + // Bail if the piece was unsnapped (undo) or invalidated while settling. + if (!_locked || _activeSlot == null) return; StopPreviewTweens(); Lock(); FillSlot(); - - _sfx.Play(SfxId.ShapeSnap); - _bus.Publish(new PieceSnappedSignal(_shape.Id)); } private void FillSlot() @@ -267,6 +301,7 @@ namespace Darkmatter.Features.ShapeBuilder.UI _activeSlot = null; Tween.StopAll(onTarget: RectTransform); + _snapSettle = default; RectTransform.SetParent(parent, worldPositionStays: false); if (siblingIndex >= 0) RectTransform.SetSiblingIndex(siblingIndex); diff --git a/Assets/Darkmatter/Code/Services/Ads/Systems/AdMobAdService.cs b/Assets/Darkmatter/Code/Services/Ads/Systems/AdMobAdService.cs index 7cf206f..6f5db78 100644 --- a/Assets/Darkmatter/Code/Services/Ads/Systems/AdMobAdService.cs +++ b/Assets/Darkmatter/Code/Services/Ads/Systems/AdMobAdService.cs @@ -498,6 +498,11 @@ namespace Darkmatter.Services.Ads { resolved = true; // stop the watchdog within one poll unsubscribe(onClosed, onFailed); + // The in-editor placeholder ad pauses via Time.timeScale = 0; if its resume is + // dropped (e.g. the host scene unloads before its close fires) the game stays + // frozen. The ad layer owns that pause, so never leave it stranded. No-op on + // device, where real ad clients don't touch timeScale. + Time.timeScale = 1f; ScheduleReload(format); } } diff --git a/Assets/Darkmatter/Content/Colorbook UI/Prefabs/ColoringPrefabs/FrogColoring.prefab b/Assets/Darkmatter/Content/Colorbook UI/Prefabs/ColoringPrefabs/FrogColoring.prefab index 4aa5269..5b3077a 100644 --- a/Assets/Darkmatter/Content/Colorbook UI/Prefabs/ColoringPrefabs/FrogColoring.prefab +++ b/Assets/Darkmatter/Content/Colorbook UI/Prefabs/ColoringPrefabs/FrogColoring.prefab @@ -567,7 +567,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 + m_ConstrainProportionsScale: 1 m_Children: - {fileID: 8713146880366692101} - {fileID: 4954180409987358819} diff --git a/Assets/Darkmatter/Content/Colorbook UI/Prefabs/DrawingPrefabs/FrogDrawing.prefab b/Assets/Darkmatter/Content/Colorbook UI/Prefabs/DrawingPrefabs/FrogDrawing.prefab index b49349f..eb49de0 100644 --- a/Assets/Darkmatter/Content/Colorbook UI/Prefabs/DrawingPrefabs/FrogDrawing.prefab +++ b/Assets/Darkmatter/Content/Colorbook UI/Prefabs/DrawingPrefabs/FrogDrawing.prefab @@ -101,8 +101,8 @@ RectTransform: m_GameObject: {fileID: 3594252390097820856} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 + m_LocalScale: {x: 1.5, y: 1.5, z: 1.5} + m_ConstrainProportionsScale: 1 m_Children: - {fileID: 5980705335490319841} - {fileID: 3827914630025194271} diff --git a/Assets/Darkmatter/Data/Settings/ShapeBuilder/ShapeBuilderConfig.asset b/Assets/Darkmatter/Data/Settings/ShapeBuilder/ShapeBuilderConfig.asset index 263a4ce..1524e01 100644 --- a/Assets/Darkmatter/Data/Settings/ShapeBuilder/ShapeBuilderConfig.asset +++ b/Assets/Darkmatter/Data/Settings/ShapeBuilder/ShapeBuilderConfig.asset @@ -12,10 +12,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 52d6fdba64cc3491880636e34ed593d0, type: 3} m_Name: ShapeBuilderConfig m_EditorClassIdentifier: Core::Darkmatter.Core.Data.Static.Features.ShapeBuilder.ShapeBuilderConfig - snapRadius: 100 - previewRadius: 200 + snapRadius: 200 + previewRadius: 300 snapDuration: 0.25 returnDuration: 0.25 + dragScale: 1.15 previewCurve: serializedVersion: 2 m_Curve: diff --git a/Assets/Darkmatter/Scenes/Boot.unity b/Assets/Darkmatter/Scenes/Boot.unity index 068f07d..31ecbc8 100644 --- a/Assets/Darkmatter/Scenes/Boot.unity +++ b/Assets/Darkmatter/Scenes/Boot.unity @@ -1394,6 +1394,10 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: + - target: {fileID: 4405976200006927252, guid: 0bfe3b149145747cc92dc53bb4df4e9b, type: 3} + propertyPath: devKey + value: DwvwaVs4yYqfPWfWjFcAL3 + objectReference: {fileID: 0} - target: {fileID: 6207133488976360133, guid: 0bfe3b149145747cc92dc53bb4df4e9b, type: 3} propertyPath: m_LocalPosition.x value: 1021.8063 diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 1d13e19..7e18095 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -146,7 +146,6 @@ PlayerSettings: bundleVersion: 1.0 preloadedAssets: - {fileID: 11400000, guid: cc969dbecb228fa49b16da9273753a8f, type: 2} - - {fileID: -944628639613478452, guid: 2bcd2660ca9b64942af0de543d8d7100, type: 3} metroInputSource: 0 wsaTransparentSwapchain: 0 xboxOneDisableKinectGpuReservation: 1