Compare commits
6 Commits
work_branc
...
b77976039a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b77976039a | ||
|
|
f6f5474366 | ||
|
|
3abf19b5bb | ||
|
|
35e4bf8067 | ||
|
|
df43824fb1 | ||
|
|
e167af11fd |
BIN
Assets/.DS_Store
vendored
BIN
Assets/.DS_Store
vendored
Binary file not shown.
BIN
Assets/Darkmatter/.DS_Store
vendored
BIN
Assets/Darkmatter/.DS_Store
vendored
Binary file not shown.
@@ -6,9 +6,12 @@ namespace Darkmatter.Core.Contracts.Features.DrawingCatalog
|
||||
{
|
||||
public interface IDrawingTemplateCatalog
|
||||
{
|
||||
UniTask FetchAsync();
|
||||
UniTask FetchAsync();
|
||||
IReadOnlyList<string> AllTemplateIds { get; }
|
||||
UniTask<Sprite> GetThumbnailAsync(string id);
|
||||
// owned = true when the sprite (and its texture) were created for this call from a
|
||||
// saved thumbnail on disk — the caller must Destroy() both when done. owned = false
|
||||
// means the template's default thumbnail asset, which must NOT be destroyed.
|
||||
UniTask<(Sprite sprite, bool owned)> GetThumbnailAsync(string id);
|
||||
UniTask<IDrawingTemplate> LoadAsync(string id);
|
||||
void ReleaseAll();
|
||||
string GetNextTemplate(string currentId);
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"GUID:c1c03c0e5b2f4412b9f2be1c20d6a9b1",
|
||||
"GUID:b4c9f7fbf1e144933a1797dc208ece5f",
|
||||
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc",
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23"
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23",
|
||||
"GUID:75469ad4d38634e559750d17036d5f7c"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Rendering;
|
||||
using VContainer.Unity;
|
||||
|
||||
namespace Darkmatter.Features.AppBoot.Flow
|
||||
{
|
||||
// A coloring page is static most of the time; re-rendering it at a locked 60 fps only
|
||||
// turns battery into heat. Keep the player loop at 60 Hz (input still sampled every
|
||||
// frame, so the first touch never lands on a skipped frame) but render every other
|
||||
// frame while nothing has been touched for a few seconds.
|
||||
public sealed class AdaptiveFrameRate : ITickable
|
||||
{
|
||||
private const float FullRateHoldSeconds = 3f;
|
||||
private const int IdleRenderInterval = 2; // 60 Hz loop -> 30 fps rendering when idle
|
||||
|
||||
private float _lastInteractionTime;
|
||||
|
||||
public AdaptiveFrameRate()
|
||||
{
|
||||
// Treat boot as an interaction so the intro video starts at full rate.
|
||||
_lastInteractionTime = Time.unscaledTime;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (IsPointerPressed())
|
||||
_lastInteractionTime = Time.unscaledTime;
|
||||
|
||||
var desired = Time.unscaledTime - _lastInteractionTime < FullRateHoldSeconds
|
||||
? 1
|
||||
: IdleRenderInterval;
|
||||
|
||||
if (OnDemandRendering.renderFrameInterval != desired)
|
||||
OnDemandRendering.renderFrameInterval = desired;
|
||||
}
|
||||
|
||||
private static bool IsPointerPressed()
|
||||
{
|
||||
var touchscreen = Touchscreen.current;
|
||||
if (touchscreen != null && touchscreen.primaryTouch.press.isPressed)
|
||||
return true;
|
||||
|
||||
var mouse = Mouse.current;
|
||||
return mouse != null && mouse.leftButton.isPressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee0bbb69e2ec44637a7f5fb6f18a2518
|
||||
@@ -16,6 +16,7 @@ namespace Darkmatter.Features.AppBoot.Installers
|
||||
if (sceneRefs != null)
|
||||
builder.RegisterComponent(sceneRefs);
|
||||
builder.RegisterEntryPoint<AppBootFlow>();
|
||||
builder.RegisterEntryPoint<AdaptiveFrameRate>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Darkmatter.Features.Coloring.Systems
|
||||
@@ -8,6 +9,7 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
public sealed class ColoringPinchZoomController : MonoBehaviour
|
||||
{
|
||||
private const float MinimumPinchDistance = 8f;
|
||||
private const float PanZoomEpsilon = 0.0001f;
|
||||
|
||||
[SerializeField] private float minZoom = 1f;
|
||||
[SerializeField] private float maxZoom = 3f;
|
||||
@@ -22,6 +24,7 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
private float _currentZoom = 1f;
|
||||
private Vector3 _baseScale = Vector3.one;
|
||||
private Vector2 _baseAnchoredPosition;
|
||||
private PanDragRelay _panRelay;
|
||||
|
||||
public void SetTarget(RectTransform target)
|
||||
{
|
||||
@@ -34,6 +37,26 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
_baseAnchoredPosition = target != null ? target.anchoredPosition : Vector2.zero;
|
||||
_currentZoom = 1f;
|
||||
enabled = _target != null && _parent != null;
|
||||
|
||||
_panRelay = null;
|
||||
if (_target != null)
|
||||
{
|
||||
_panRelay = _target.gameObject.GetComponent<PanDragRelay>();
|
||||
if (_panRelay == null)
|
||||
_panRelay = _target.gameObject.AddComponent<PanDragRelay>();
|
||||
_panRelay.Controller = this;
|
||||
}
|
||||
|
||||
UpdatePanRelayState();
|
||||
}
|
||||
|
||||
// While not zoomed the relay is disabled so the event system never resolves it as a
|
||||
// drag target — sloppy kid taps keep registering as clicks (paints) exactly as before.
|
||||
// Zoomed in, drags become pans and are suppressed as clicks by uGUI.
|
||||
private void UpdatePanRelayState()
|
||||
{
|
||||
if (_panRelay != null)
|
||||
_panRelay.enabled = _currentZoom > 1f + PanZoomEpsilon;
|
||||
}
|
||||
|
||||
public void ResetZoom()
|
||||
@@ -46,6 +69,7 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
|
||||
_currentZoom = 1f;
|
||||
_isPinching = false;
|
||||
UpdatePanRelayState();
|
||||
}
|
||||
|
||||
public IDisposable UseNonZoomedView()
|
||||
@@ -78,6 +102,7 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
|
||||
_isPinching = false;
|
||||
TryApplyScrollZoom();
|
||||
TryApplyMousePan();
|
||||
}
|
||||
|
||||
private bool TryApplyPinchZoom()
|
||||
@@ -111,8 +136,16 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
return true;
|
||||
}
|
||||
|
||||
// Anchor the content point that was under the previous midpoint to the current
|
||||
// midpoint: distance change zooms, midpoint movement pans — one gesture does both.
|
||||
Vector2 previousMidpoint = (firstTouch.PreviousPosition + secondTouch.PreviousPosition) * 0.5f;
|
||||
if (!TryGetParentLocalPoint(previousMidpoint, out Vector2 previousMidpointLocal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float targetZoom = Mathf.Clamp(_currentZoom * (distance / previousDistance), minZoom, maxZoom);
|
||||
ZoomTowards(midpointLocal, targetZoom);
|
||||
ApplyZoomPan(previousMidpointLocal, midpointLocal, targetZoom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -152,12 +185,123 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 contentPoint = (parentLocalPoint - _target.anchoredPosition) / _currentZoom;
|
||||
ApplyZoomPan(parentLocalPoint, parentLocalPoint, targetZoom);
|
||||
}
|
||||
|
||||
private void ApplyZoomPan(Vector2 previousLocalPoint, Vector2 currentLocalPoint, float targetZoom)
|
||||
{
|
||||
Vector2 contentPoint = (previousLocalPoint - _target.anchoredPosition) / _currentZoom;
|
||||
_currentZoom = targetZoom;
|
||||
_target.localScale = _baseScale * _currentZoom;
|
||||
_target.anchoredPosition = Mathf.Approximately(_currentZoom, minZoom)
|
||||
? _baseAnchoredPosition
|
||||
: parentLocalPoint - contentPoint * _currentZoom;
|
||||
_target.anchoredPosition = ClampPan(currentLocalPoint - contentPoint * _currentZoom);
|
||||
UpdatePanRelayState();
|
||||
}
|
||||
|
||||
// Editor/desktop convenience: drag with the right or middle mouse button to pan while zoomed.
|
||||
private void TryApplyMousePan()
|
||||
{
|
||||
Mouse mouse = Mouse.current;
|
||||
if (mouse == null || (!mouse.rightButton.isPressed && !mouse.middleButton.isPressed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentZoom <= 1f + PanZoomEpsilon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 delta = mouse.delta.ReadValue();
|
||||
if (delta == Vector2.zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 screenPosition = mouse.position.ReadValue();
|
||||
if (!IsInputPointAllowed(screenPosition))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetParentLocalPoint(screenPosition, out Vector2 currentLocal) ||
|
||||
!TryGetParentLocalPoint(screenPosition - delta, out Vector2 previousLocal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_target.anchoredPosition = ClampPan(_target.anchoredPosition + (currentLocal - previousLocal));
|
||||
}
|
||||
|
||||
// Single-finger (or left-mouse) drag pan, fed by PanDragRelay on the paper root.
|
||||
// Going through uGUI's drag pipeline matters: once the drag threshold is crossed the
|
||||
// event system cancels the pending click, so a pan can never paint the region the
|
||||
// finger ends on — a plain tap still paints.
|
||||
private void OnPanDrag(PointerEventData eventData, Transform source)
|
||||
{
|
||||
if (_target == null || source != _target.transform)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isPinching || HasMultiTouch())
|
||||
{
|
||||
return; // two-finger gesture owns the view
|
||||
}
|
||||
|
||||
if (_currentZoom <= 1f + PanZoomEpsilon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetParentLocalPoint(eventData.position, out Vector2 currentLocal) ||
|
||||
!TryGetParentLocalPoint(eventData.position - eventData.delta, out Vector2 previousLocal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_target.anchoredPosition = ClampPan(_target.anchoredPosition + (currentLocal - previousLocal));
|
||||
}
|
||||
|
||||
private static bool HasMultiTouch()
|
||||
{
|
||||
Touchscreen touchscreen = Touchscreen.current;
|
||||
if (touchscreen == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
foreach (var touchControl in touchscreen.touches)
|
||||
{
|
||||
if (touchControl.press.isPressed && ++count > 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The scaled paper must keep covering its zoom-1 footprint: panning can never expose
|
||||
// background inside the area the drawing occupies when not zoomed.
|
||||
private Vector2 ClampPan(Vector2 anchoredPosition)
|
||||
{
|
||||
if (_currentZoom <= 1f + PanZoomEpsilon)
|
||||
{
|
||||
return _baseAnchoredPosition;
|
||||
}
|
||||
|
||||
Rect rect = _target.rect;
|
||||
float growth = 1f - _currentZoom; // negative while zoomed in
|
||||
|
||||
float minX = _baseAnchoredPosition.x + rect.xMax * _baseScale.x * growth;
|
||||
float maxX = _baseAnchoredPosition.x + rect.xMin * _baseScale.x * growth;
|
||||
float minY = _baseAnchoredPosition.y + rect.yMax * _baseScale.y * growth;
|
||||
float maxY = _baseAnchoredPosition.y + rect.yMin * _baseScale.y * growth;
|
||||
|
||||
return new Vector2(
|
||||
Mathf.Clamp(anchoredPosition.x, minX, maxX),
|
||||
Mathf.Clamp(anchoredPosition.y, minY, maxY));
|
||||
}
|
||||
|
||||
private bool TryGetParentLocalPoint(Vector2 screenPoint, out Vector2 localPoint)
|
||||
@@ -233,6 +377,7 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
_target.anchoredPosition = state.AnchoredPosition;
|
||||
_currentZoom = state.CurrentZoom;
|
||||
_isPinching = false;
|
||||
UpdatePanRelayState();
|
||||
}
|
||||
|
||||
private readonly struct ViewState
|
||||
@@ -285,5 +430,25 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
Delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime-attached to the paper root (SetTarget); regions are its children, so drags
|
||||
// that start on a region bubble up here while taps stay clicks. Implementing the uGUI
|
||||
// drag interfaces is what makes the event system suppress the region click on drag.
|
||||
private sealed class PanDragRelay : MonoBehaviour, IBeginDragHandler, IDragHandler
|
||||
{
|
||||
internal ColoringPinchZoomController Controller;
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (Controller != null)
|
||||
{
|
||||
Controller.OnPanDrag(eventData, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ namespace Darkmatter.Features.DrawingCatalog
|
||||
private readonly CancellationTokenSource _cts = new();
|
||||
private IDisposable _openSubscription;
|
||||
|
||||
// Sprites/textures decoded from saved thumbnails are created fresh on every refresh
|
||||
// and are owned by this presenter — without destroying the previous batch each
|
||||
// Colorbook visit leaks several MB per painted drawing until the OS kills the app.
|
||||
private readonly List<Sprite> _ownedSprites = new();
|
||||
private readonly List<Texture2D> _ownedTextures = new();
|
||||
|
||||
public DrawingCatalogPresenter(DrawingCatalogView view, IDrawingCatalogController controller,
|
||||
IDrawingTemplateCatalog catalog, IEventBus eventBus)
|
||||
{
|
||||
@@ -109,29 +115,73 @@ namespace Darkmatter.Features.DrawingCatalog
|
||||
|
||||
private async UniTask RefreshAsync(CancellationToken ct)
|
||||
{
|
||||
var ids = ToFixedRowGridOrder(_controller.VisibleIds);
|
||||
var vms = new List<CatalogItemVM>(ids.Count);
|
||||
var newSprites = new List<Sprite>();
|
||||
var newTextures = new List<Texture2D>();
|
||||
var applied = false;
|
||||
|
||||
_totalPages = (int)Math.Ceiling(ids.Count / (float)ItemPerPage);
|
||||
_currentPage = 0;
|
||||
|
||||
foreach (var id in ids)
|
||||
try
|
||||
{
|
||||
var ids = ToFixedRowGridOrder(_controller.VisibleIds);
|
||||
var vms = new List<CatalogItemVM>(ids.Count);
|
||||
|
||||
_totalPages = (int)Math.Ceiling(ids.Count / (float)ItemPerPage);
|
||||
_currentPage = 0;
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
if (ct.IsCancellationRequested) return;
|
||||
var (thumb, owned) = await _catalog.GetThumbnailAsync(id);
|
||||
if (owned)
|
||||
{
|
||||
newSprites.Add(thumb);
|
||||
newTextures.Add(thumb.texture);
|
||||
}
|
||||
|
||||
vms.Add(new CatalogItemVM(id, thumb));
|
||||
}
|
||||
|
||||
if (ct.IsCancellationRequested) return;
|
||||
var thumb = await _catalog.GetThumbnailAsync(id);
|
||||
vms.Add(new CatalogItemVM(id, thumb));
|
||||
_view.ResetScrollPosition();
|
||||
_view.SetItems(vms);
|
||||
_view.SetPagination(_currentPage, _totalPages);
|
||||
|
||||
// Buttons now reference the new batch; the previous one can go.
|
||||
DestroyOwnedThumbnails();
|
||||
_ownedSprites.AddRange(newSprites);
|
||||
_ownedTextures.AddRange(newTextures);
|
||||
applied = true;
|
||||
|
||||
// Unblock InitializeAsync: items are now on screen, so the loading screen can hide.
|
||||
_controller.NotifyPopulated();
|
||||
|
||||
// Cue the first-run tutorial that the catalog grid is on screen and tappable.
|
||||
_eventBus.Publish(new DrawingCatalogReadySignal());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[DrawingCatalog] Failed to refresh catalog: {e}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Aborted refresh (cancelled or threw): don't strand the freshly decoded batch.
|
||||
if (!applied)
|
||||
{
|
||||
foreach (var s in newSprites) UnityEngine.Object.Destroy(s);
|
||||
foreach (var t in newTextures) UnityEngine.Object.Destroy(t);
|
||||
}
|
||||
|
||||
if (ct.IsCancellationRequested) return;
|
||||
_view.ResetScrollPosition();
|
||||
_view.SetItems(vms);
|
||||
_view.SetPagination(_currentPage, _totalPages);
|
||||
// Idempotent — always unblock the flow; a stuck loading screen is worse than
|
||||
// an empty catalog.
|
||||
_controller.NotifyPopulated();
|
||||
}
|
||||
}
|
||||
|
||||
// Unblock InitializeAsync: items are now on screen, so the loading screen can hide.
|
||||
_controller.NotifyPopulated();
|
||||
|
||||
// Cue the first-run tutorial that the catalog grid is on screen and tappable.
|
||||
_eventBus.Publish(new DrawingCatalogReadySignal());
|
||||
private void DestroyOwnedThumbnails()
|
||||
{
|
||||
foreach (var s in _ownedSprites) UnityEngine.Object.Destroy(s);
|
||||
foreach (var t in _ownedTextures) UnityEngine.Object.Destroy(t);
|
||||
_ownedSprites.Clear();
|
||||
_ownedTextures.Clear();
|
||||
}
|
||||
|
||||
private static List<string> ToFixedRowGridOrder(IReadOnlyList<string> ids)
|
||||
@@ -160,6 +210,7 @@ namespace Darkmatter.Features.DrawingCatalog
|
||||
_openSubscription?.Dispose();
|
||||
_cts.Cancel();
|
||||
_cts.Dispose();
|
||||
DestroyOwnedThumbnails();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Darkmatter.Features.DrawingTemplates.Systems
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
public async UniTask<Sprite> GetThumbnailAsync(string id)
|
||||
public async UniTask<(Sprite sprite, bool owned)> GetThumbnailAsync(string id)
|
||||
{
|
||||
if (!_byId.TryGetValue(id, out var t))
|
||||
throw new KeyNotFoundException($"Template '{id}' not in catalog. Did InitializeAsync run?");
|
||||
@@ -62,9 +62,9 @@ namespace Darkmatter.Features.DrawingTemplates.Systems
|
||||
if (savedTex != null)
|
||||
{
|
||||
var rect = new Rect(0, 0, savedTex.width, savedTex.height);
|
||||
return Sprite.Create(savedTex, rect, new Vector2(0.5f, 0.5f));
|
||||
return (Sprite.Create(savedTex, rect, new Vector2(0.5f, 0.5f)), true);
|
||||
}
|
||||
return t.DefaultThumbnail;
|
||||
return (t.DefaultThumbnail, false);
|
||||
}
|
||||
|
||||
public UniTask<IDrawingTemplate> LoadAsync(string id)
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Darkmatter.Features.GameplayFlow.Systems
|
||||
private string _templateId;
|
||||
private DrawingPhase _phase = DrawingPhase.ShapeBuilding;
|
||||
private bool _allContentReported;
|
||||
private bool _navigating;
|
||||
private float _lastThumbnailTime = float.NegativeInfinity;
|
||||
|
||||
private IDisposable _assembledSub;
|
||||
@@ -80,6 +81,27 @@ namespace Darkmatter.Features.GameplayFlow.Systems
|
||||
}
|
||||
|
||||
public async UniTask StartAsync(CancellationToken cancellation)
|
||||
{
|
||||
try
|
||||
{
|
||||
await StartCoreAsync(cancellation);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Scene torn down mid-init; nothing to recover.
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Any throw here (missing template id, failed Addressables load, region init)
|
||||
// would otherwise skip _loadingScreen.Hide() and strand the kid on a frozen
|
||||
// loader forever. Log it and route back to the Colorbook, whose flow hides the
|
||||
// loading screen once its catalog is populated.
|
||||
Debug.LogError($"[GameplayFlow] Failed to start gameplay, returning to Colorbook: {e}");
|
||||
await RecoverToColorbookAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask StartCoreAsync(CancellationToken cancellation)
|
||||
{
|
||||
_scopeCts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
|
||||
var ct = _scopeCts.Token;
|
||||
@@ -127,14 +149,42 @@ namespace Darkmatter.Features.GameplayFlow.Systems
|
||||
_loadingScreen.Hide();
|
||||
}
|
||||
|
||||
private async UniTask RecoverToColorbookAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_shapeBuilder.Clear();
|
||||
_coloring.Clear();
|
||||
await _scenes.LoadSceneAsync(nameof(GameScene.Colorbook), progress: null, cancellationToken: default);
|
||||
await _scenes.UnloadSceneAsync(nameof(GameScene.Gameplay), progress: null, cancellationToken: default);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[GameplayFlow] Recovery to Colorbook failed: {e}");
|
||||
_loadingScreen.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask BackAsync(CancellationToken ct)
|
||||
{
|
||||
await SaveCurrentAsync(CancellationToken.None);
|
||||
_loadingScreen.Show();
|
||||
_shapeBuilder.Clear();
|
||||
_coloring.Clear();
|
||||
await _scenes.LoadSceneAsync(nameof(GameScene.Colorbook), progress: null, cancellationToken: ct);
|
||||
await _scenes.UnloadSceneAsync(nameof(GameScene.Gameplay), progress: null, cancellationToken: ct);
|
||||
// A second navigation while one runs (e.g. Back pressed during the completion
|
||||
// animation inside NextAsync) would interleave two saves and two Colorbook loads,
|
||||
// and Clear() would rip the celebration apart mid-flight. First navigation wins.
|
||||
if (_navigating) return;
|
||||
_navigating = true;
|
||||
try
|
||||
{
|
||||
await SaveCurrentAsync(CancellationToken.None);
|
||||
_loadingScreen.Show();
|
||||
_shapeBuilder.Clear();
|
||||
_coloring.Clear();
|
||||
await _scenes.LoadSceneAsync(nameof(GameScene.Colorbook), progress: null, cancellationToken: ct);
|
||||
await _scenes.UnloadSceneAsync(nameof(GameScene.Gameplay), progress: null, cancellationToken: ct);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_navigating = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask SaveAsync(CancellationToken ct)
|
||||
@@ -147,48 +197,48 @@ namespace Darkmatter.Features.GameplayFlow.Systems
|
||||
|
||||
public async UniTask NextAsync(CancellationToken ct)
|
||||
{
|
||||
// Drop any debounced autosave so it can't fire during/after the completion
|
||||
// animation and capture the animation (or an empty paper) into the thumbnail.
|
||||
_autosaveCts?.Cancel();
|
||||
_autosaveCts?.Dispose();
|
||||
_autosaveCts = null;
|
||||
|
||||
await SaveCurrentAsync(ct);
|
||||
_refs.Confetti.Play();
|
||||
_sfx.Play(SfxId.FireWorkLaunch);
|
||||
_sfx.Play(SfxId.LevelComplete);
|
||||
await _coloring.PlayCompletionAnimationAsync(ct);
|
||||
_progression.MarkCompleted(_templateId);
|
||||
var progressAfter = _progression.GetProgress(_templateId);
|
||||
_bus.Publish(new DrawingCompletedSignal(_templateId, progressAfter?.completionCount ?? 1));
|
||||
|
||||
// Player has cleared the whole catalog → surface the "ran out of content" milestone. Guarded
|
||||
// per session here; analytics dedupes it to once-ever.
|
||||
if (!_allContentReported &&
|
||||
_catalog.AllTemplateIds.Count > 0 &&
|
||||
_progression.CompletedTemplateIds.Count >= _catalog.AllTemplateIds.Count)
|
||||
if (_navigating) return;
|
||||
_navigating = true;
|
||||
try
|
||||
{
|
||||
_allContentReported = true;
|
||||
_bus.Publish(new AllContentCompletedSignal(_progression.CompletedTemplateIds.Count));
|
||||
}
|
||||
// Drop any debounced autosave so it can't fire during/after the completion
|
||||
// animation and capture the animation (or an empty paper) into the thumbnail.
|
||||
_autosaveCts?.Cancel();
|
||||
_autosaveCts?.Dispose();
|
||||
_autosaveCts = null;
|
||||
|
||||
var nextId = _catalog.GetNextTemplate(_templateId);
|
||||
if (string.IsNullOrEmpty(nextId))
|
||||
{
|
||||
await SaveCurrentAsync(ct);
|
||||
_refs.Confetti.Play();
|
||||
_sfx.Play(SfxId.FireWorkLaunch);
|
||||
_sfx.Play(SfxId.LevelComplete);
|
||||
await _coloring.PlayCompletionAnimationAsync(ct);
|
||||
_progression.MarkCompleted(_templateId);
|
||||
var progressAfter = _progression.GetProgress(_templateId);
|
||||
_bus.Publish(new DrawingCompletedSignal(_templateId, progressAfter?.completionCount ?? 1));
|
||||
|
||||
// Player has cleared the whole catalog → surface the "ran out of content" milestone. Guarded
|
||||
// per session here; analytics dedupes it to once-ever.
|
||||
if (!_allContentReported &&
|
||||
_catalog.AllTemplateIds.Count > 0 &&
|
||||
_progression.CompletedTemplateIds.Count >= _catalog.AllTemplateIds.Count)
|
||||
{
|
||||
_allContentReported = true;
|
||||
_bus.Publish(new AllContentCompletedSignal(_progression.CompletedTemplateIds.Count));
|
||||
}
|
||||
|
||||
var nextId = _catalog.GetNextTemplate(_templateId);
|
||||
_loadingScreen.Show();
|
||||
_shapeBuilder.Clear();
|
||||
_coloring.Clear();
|
||||
await _scenes.LoadSceneAsync(nameof(GameScene.Colorbook), progress: null, cancellationToken: default);
|
||||
await _scenes.UnloadSceneAsync(nameof(GameScene.Gameplay), progress: null, cancellationToken: default);
|
||||
return;
|
||||
if (!string.IsNullOrEmpty(nextId))
|
||||
_bus.Publish(new DrawingSelectedSignal(nextId));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_navigating = false;
|
||||
}
|
||||
|
||||
_loadingScreen.Show();
|
||||
_shapeBuilder.Clear();
|
||||
_coloring.Clear();
|
||||
await _scenes.LoadSceneAsync(nameof(GameScene.Colorbook), progress: null, cancellationToken: default);
|
||||
await _scenes.UnloadSceneAsync(nameof(GameScene.Gameplay), progress: null, cancellationToken: default);
|
||||
_bus.Publish(new DrawingSelectedSignal(nextId));
|
||||
}
|
||||
|
||||
|
||||
@@ -233,13 +283,22 @@ namespace Darkmatter.Features.GameplayFlow.Systems
|
||||
|
||||
private async UniTaskVoid EditAsync(string newTemplateId)
|
||||
{
|
||||
await SaveCurrentAsync(CancellationToken.None);
|
||||
_loadingScreen.Show();
|
||||
_shapeBuilder.Clear();
|
||||
_coloring.Clear();
|
||||
await _scenes.LoadSceneAsync(nameof(GameScene.Colorbook), progress: null, cancellationToken: default);
|
||||
await _scenes.UnloadSceneAsync(nameof(GameScene.Gameplay), progress: null, cancellationToken: default);
|
||||
_bus.Publish(new DrawingSelectedSignal(newTemplateId));
|
||||
if (_navigating) return;
|
||||
_navigating = true;
|
||||
try
|
||||
{
|
||||
await SaveCurrentAsync(CancellationToken.None);
|
||||
_loadingScreen.Show();
|
||||
_shapeBuilder.Clear();
|
||||
_coloring.Clear();
|
||||
await _scenes.LoadSceneAsync(nameof(GameScene.Colorbook), progress: null, cancellationToken: default);
|
||||
await _scenes.UnloadSceneAsync(nameof(GameScene.Gameplay), progress: null, cancellationToken: default);
|
||||
_bus.Publish(new DrawingSelectedSignal(newTemplateId));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_navigating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTaskVoid DebouncedAutosaveAsync(CancellationToken ct)
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Darkmatter.Features.Mainmenu
|
||||
private readonly IEventBus _eventBus;
|
||||
private readonly MainMenuView _view;
|
||||
private readonly ISfxPlayer _sfxPlayer;
|
||||
private IDisposable _introSubscription;
|
||||
|
||||
|
||||
public MainMenuPresenter(MainMenuView view, IEventBus eventBus, ISfxPlayer sfxPlayer)
|
||||
@@ -26,7 +27,7 @@ namespace Darkmatter.Features.Mainmenu
|
||||
{
|
||||
_view.OnPlayBtnPressedEvent += OnPlayBtnPressed;
|
||||
_view.OnPlayBtnClickedEvent += OnPlayBtnClicked;
|
||||
_eventBus.Subscribe<IntroCompletedSignal>(OnIntroComplete);
|
||||
_introSubscription = _eventBus.Subscribe<IntroCompletedSignal>(OnIntroComplete);
|
||||
}
|
||||
|
||||
private void OnIntroComplete(IntroCompletedSignal signal)
|
||||
@@ -48,6 +49,7 @@ namespace Darkmatter.Features.Mainmenu
|
||||
{
|
||||
_view.OnPlayBtnPressedEvent -= OnPlayBtnPressed;
|
||||
_view.OnPlayBtnClickedEvent -= OnPlayBtnClicked;
|
||||
_introSubscription?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,16 +86,27 @@ public sealed class ProgressionRepository
|
||||
{
|
||||
if (string.IsNullOrEmpty(templateId) || png == null || png.Length == 0) return;
|
||||
|
||||
Directory.CreateDirectory(ThumbnailDirectory());
|
||||
var path = ThumbnailPath(templateId);
|
||||
var tmp = path + ".tmp";
|
||||
|
||||
await UniTask.RunOnThreadPool(() =>
|
||||
// Never let thumbnail IO (disk full, permission churn) throw into the save path:
|
||||
// saves run before every Back/Next navigation, and an unhandled IOException there
|
||||
// aborts the scene transition and traps the user in gameplay. Losing a thumbnail
|
||||
// is fine — the colors themselves persist via PlayerPrefs.
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(tmp, png);
|
||||
if (File.Exists(path)) File.Delete(path);
|
||||
File.Move(tmp, path);
|
||||
});
|
||||
Directory.CreateDirectory(ThumbnailDirectory());
|
||||
var path = ThumbnailPath(templateId);
|
||||
var tmp = path + ".tmp";
|
||||
|
||||
await UniTask.RunOnThreadPool(() =>
|
||||
{
|
||||
File.WriteAllBytes(tmp, png);
|
||||
if (File.Exists(path)) File.Delete(path);
|
||||
File.Move(tmp, path);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[Progression] Failed writing thumbnail '{templateId}': {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask<Texture2D> LoadThumbnailAsync(string templateId)
|
||||
|
||||
@@ -15,7 +15,23 @@ namespace Darkmatter.Libs.Observer
|
||||
if (_map.TryGetValue(typeof(T), out var handlerObj))
|
||||
{
|
||||
var action = (Action<T>)handlerObj;
|
||||
action?.Invoke(evt);
|
||||
if (action == null) return;
|
||||
|
||||
// Invoke handlers one by one: a multicast invoke would let one throwing
|
||||
// subscriber silently starve every later subscriber and rethrow into the
|
||||
// publisher (paint / scene-transition code), turning a local bug into a
|
||||
// stuck flow.
|
||||
foreach (var handler in action.GetInvocationList())
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action<T>)handler).Invoke(evt);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -350,7 +350,9 @@ namespace Darkmatter.Services.Audio
|
||||
if (!source.gameObject.activeInHierarchy) source.gameObject.SetActive(true);
|
||||
|
||||
source.Play();
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
Debug.Log($"[AudioService] Play clip='{(request.Clip != null ? request.Clip.name : "null")}' ch={request.Channel} vol={source.volume:F2} mixer={(source.outputAudioMixerGroup != null ? source.outputAudioMixerGroup.name : "none")} listener={(AudioListener.volume)} muted={AudioListener.pause} isPlaying={source.isPlaying}");
|
||||
#endif
|
||||
if (IsChannelPaused(request.Channel))
|
||||
{
|
||||
source.Pause();
|
||||
|
||||
@@ -72,14 +72,14 @@ namespace Darkmatter.Services.Capture
|
||||
// GetPixels/SetPixels cropping allocated a managed Color[] at 16 bytes/pixel (tens of MB)
|
||||
// on every capture — autosave runs this after each paint, OOM-killing low-RAM devices.
|
||||
var cropped = new Texture2D(cropW, cropH, TextureFormat.RGBA32, mipChain: false);
|
||||
cropped.ReadPixels(new Rect(crop.x, crop.y, cropW, cropH), 0, 0);
|
||||
cropped.Apply();
|
||||
RenderTexture.active = prevActive;
|
||||
captureViewScope?.Dispose();
|
||||
captureViewScope = null;
|
||||
|
||||
try
|
||||
{
|
||||
cropped.ReadPixels(new Rect(crop.x, crop.y, cropW, cropH), 0, 0);
|
||||
cropped.Apply();
|
||||
RenderTexture.active = prevActive;
|
||||
captureViewScope?.Dispose();
|
||||
captureViewScope = null;
|
||||
|
||||
int targetW = Mathf.Max(1, Mathf.RoundToInt(cropW * scale));
|
||||
int targetH = Mathf.Max(1, Mathf.RoundToInt(cropH * scale));
|
||||
Texture2D output = cropped;
|
||||
@@ -97,17 +97,29 @@ namespace Darkmatter.Services.Capture
|
||||
}
|
||||
finally
|
||||
{
|
||||
RenderTexture.active = prevActive;
|
||||
Object.Destroy(cropped);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
paperCanvas.renderMode = prevMode;
|
||||
paperCanvas.worldCamera = prevWorldCam;
|
||||
paperCanvas.planeDistance = prevPlaneDist;
|
||||
cam.targetTexture = prevTarget;
|
||||
cam.clearFlags = prevFlags;
|
||||
cam.backgroundColor = prevBg;
|
||||
// The awaits above give scene teardown (autosave racing an unload) a chance to
|
||||
// destroy the canvas or camera; touching them then would throw from finally and
|
||||
// skip the rest of the restore.
|
||||
if (paperCanvas != null)
|
||||
{
|
||||
paperCanvas.renderMode = prevMode;
|
||||
paperCanvas.worldCamera = prevWorldCam;
|
||||
paperCanvas.planeDistance = prevPlaneDist;
|
||||
}
|
||||
|
||||
if (cam != null)
|
||||
{
|
||||
cam.targetTexture = prevTarget;
|
||||
cam.clearFlags = prevFlags;
|
||||
cam.backgroundColor = prevBg;
|
||||
}
|
||||
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
if (disabledGraphics != null)
|
||||
foreach (var g in disabledGraphics)
|
||||
|
||||
@@ -118,8 +118,17 @@ namespace Darkmatter.Services.Gallery
|
||||
GL.PopMatrix();
|
||||
|
||||
var output = new Texture2D(bgW, bgH, TextureFormat.RGBA32, mipChain: false);
|
||||
output.ReadPixels(new Rect(0, 0, bgW, bgH), 0, 0);
|
||||
output.Apply();
|
||||
try
|
||||
{
|
||||
output.ReadPixels(new Rect(0, 0, bgW, bgH), 0, 0);
|
||||
output.Apply();
|
||||
}
|
||||
catch
|
||||
{
|
||||
UnityEngine.Object.Destroy(output);
|
||||
throw;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
finally
|
||||
|
||||
BIN
Assets/Darkmatter/Content/.DS_Store
vendored
BIN
Assets/Darkmatter/Content/.DS_Store
vendored
Binary file not shown.
BIN
Assets/Darkmatter/Content/Colorbook UI/.DS_Store
vendored
BIN
Assets/Darkmatter/Content/Colorbook UI/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@ MonoBehaviour:
|
||||
m_RequireOpaqueTexture: 0
|
||||
m_OpaqueDownsampling: 1
|
||||
m_SupportsTerrainHoles: 1
|
||||
m_SupportsHDR: 1
|
||||
m_SupportsHDR: 0
|
||||
m_HDRColorBufferPrecision: 0
|
||||
m_MSAA: 1
|
||||
m_RenderScale: 1
|
||||
@@ -42,7 +42,7 @@ MonoBehaviour:
|
||||
m_SupportProbeVolumeScenarioBlending: 0
|
||||
m_ProbeVolumeSHBands: 1
|
||||
m_MainLightRenderingMode: 1
|
||||
m_MainLightShadowsSupported: 1
|
||||
m_MainLightShadowsSupported: 0
|
||||
m_MainLightShadowmapResolution: 2048
|
||||
m_AdditionalLightsRenderingMode: 1
|
||||
m_AdditionalLightsPerObjectLimit: 4
|
||||
@@ -99,7 +99,7 @@ MonoBehaviour:
|
||||
obsoleteHasProbeVolumes:
|
||||
m_Keys: []
|
||||
m_Values:
|
||||
m_PrefilteringModeMainLightShadows: 4
|
||||
m_PrefilteringModeMainLightShadows: 0
|
||||
m_PrefilteringModeAdditionalLight: 4
|
||||
m_PrefilteringModeAdditionalLightShadows: 0
|
||||
m_PrefilterXRKeywords: 1
|
||||
|
||||
@@ -1 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><resources><string name="com.crashlytics.android.build_id" translatable="false">8fc48ed6-41de-4dcf-a980-9aa3316fad11</string></resources>
|
||||
<?xml version="1.0" encoding="utf-8"?><resources><string name="com.crashlytics.android.build_id" translatable="false">902fe897-8c3c-4178-afb3-844ca2e340bb</string></resources>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.google.android.play.core.missingsplits;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
public class MissingSplitsManagingApplication extends Application {
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23c90ef77b914a42a239857d3e9386d0
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 3
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
Android:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
Any:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
Editor:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/Android/com.meta
Normal file
8
Assets/Plugins/Android/com.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 393d9cf56c4764b309bdf0f6115b26de
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/Android/com/google.meta
Normal file
8
Assets/Plugins/Android/com/google.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 251093c323b854663b7ced2a6f92fece
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/Android/com/google/android.meta
Normal file
8
Assets/Plugins/Android/com/google/android.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5098914d90c8144309a4a8db29063644
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/Android/com/google/android/play.meta
Normal file
8
Assets/Plugins/Android/com/google/android/play.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dcd719e6098c4a7ebac80ed0d7164e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/Android/com/google/android/play/core.meta
Normal file
8
Assets/Plugins/Android/com/google/android/play/core.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50a84b4d722374e35b2ce3232ec677cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 136d5838f159543798c8cd09221e6c9a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
70
Assets/Plugins/Android/launcherTemplate.gradle
Normal file
70
Assets/Plugins/Android/launcherTemplate.gradle
Normal file
@@ -0,0 +1,70 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: 'setupSymbols.gradle'
|
||||
apply from: '../shared/keepUnitySymbols.gradle'
|
||||
apply from: '../shared/common.gradle'
|
||||
|
||||
dependencies {
|
||||
implementation project(':unityLibrary')
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "**NAMESPACE**"
|
||||
ndkPath "**NDKPATH**"
|
||||
ndkVersion "**NDKVERSION**"
|
||||
compileSdk **APIVERSION**
|
||||
buildToolsVersion "**BUILDTOOLS**"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk **MINSDK**
|
||||
targetSdk **TARGETSDK**
|
||||
applicationId '**APPLICATIONID**'
|
||||
ndk {
|
||||
abiFilters **ABIFILTERS**
|
||||
debugSymbolLevel **DEBUGSYMBOLLEVEL**
|
||||
}
|
||||
versionCode **VERSIONCODE**
|
||||
versionName '**VERSIONNAME**'
|
||||
}
|
||||
|
||||
androidResources {
|
||||
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
|
||||
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
|
||||
}**SIGN**
|
||||
|
||||
lint {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled **MINIFY_DEBUG**
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')**SIGNCONFIG**
|
||||
}
|
||||
release {
|
||||
minifyEnabled **MINIFY_RELEASE**
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')**SIGNCONFIG**
|
||||
}
|
||||
}**PACKAGING****PLAY_ASSET_PACKS****SPLITS**
|
||||
**BUILT_APK_LOCATION**
|
||||
bundle {
|
||||
language {
|
||||
enableSplit false
|
||||
}
|
||||
density {
|
||||
enableSplit false
|
||||
}
|
||||
abi {
|
||||
enableSplit false
|
||||
}
|
||||
texture {
|
||||
enableSplit false
|
||||
}
|
||||
}
|
||||
|
||||
**GOOGLE_PLAY_DEPENDENCIES**
|
||||
}**SPLITS_VERSION_CODE****LAUNCHER_SOURCE_BUILD_SETUP**
|
||||
7
Assets/Plugins/Android/launcherTemplate.gradle.meta
Normal file
7
Assets/Plugins/Android/launcherTemplate.gradle.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93290f6ffc554b9589f42bc71d39e40b
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -13,4 +13,4 @@ MonoBehaviour:
|
||||
m_Name: SmartlookSettings
|
||||
m_EditorClassIdentifier:
|
||||
ProjectKey: 3bcfb098c5c4f6f1f48d8bbef4c0c94d7c2f9bc9
|
||||
FPS: 30
|
||||
FPS: 2
|
||||
|
||||
@@ -11,7 +11,7 @@ PlayerSettings:
|
||||
defaultScreenOrientation: 4
|
||||
targetDevice: 2
|
||||
useOnDemandResources: 0
|
||||
accelerometerFrequency: 60
|
||||
accelerometerFrequency: 0
|
||||
companyName: Darkmatter Game Production
|
||||
productName: Kidsage Colorbook
|
||||
defaultCursor: {fileID: 0}
|
||||
@@ -148,7 +148,7 @@ PlayerSettings:
|
||||
loadStoreDebugModeEnabled: 0
|
||||
visionOSBundleVersion: 1.0
|
||||
tvOSBundleVersion: 1.0
|
||||
bundleVersion: 2.0
|
||||
bundleVersion: 2.2
|
||||
preloadedAssets:
|
||||
- {fileID: 11400000, guid: cc969dbecb228fa49b16da9273753a8f, type: 2}
|
||||
- {fileID: -944628639613478452, guid: 2bcd2660ca9b64942af0de543d8d7100, type: 3}
|
||||
@@ -180,7 +180,7 @@ PlayerSettings:
|
||||
iPhone: 1
|
||||
tvOS: 0
|
||||
overrideDefaultApplicationIdentifier: 1
|
||||
AndroidBundleVersionCode: 9
|
||||
AndroidBundleVersionCode: 12
|
||||
AndroidMinSdkVersion: 26
|
||||
AndroidTargetSdkVersion: 0
|
||||
AndroidPreferredInstallLocation: 1
|
||||
@@ -268,7 +268,7 @@ PlayerSettings:
|
||||
useCustomMainManifest: 1
|
||||
useCustomLauncherManifest: 0
|
||||
useCustomMainGradleTemplate: 1
|
||||
useCustomLauncherGradleManifest: 0
|
||||
useCustomLauncherGradleManifest: 1
|
||||
useCustomBaseGradleTemplate: 0
|
||||
useCustomGradlePropertiesTemplate: 1
|
||||
useCustomGradleSettingsTemplate: 1
|
||||
|
||||
Reference in New Issue
Block a user