Compare commits
1 Commits
b77976039a
...
work_branc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa2b8f1e8e |
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,12 +6,9 @@ namespace Darkmatter.Core.Contracts.Features.DrawingCatalog
|
||||
{
|
||||
public interface IDrawingTemplateCatalog
|
||||
{
|
||||
UniTask FetchAsync();
|
||||
UniTask FetchAsync();
|
||||
IReadOnlyList<string> AllTemplateIds { get; }
|
||||
// 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<Sprite> GetThumbnailAsync(string id);
|
||||
UniTask<IDrawingTemplate> LoadAsync(string id);
|
||||
void ReleaseAll();
|
||||
string GetNextTemplate(string currentId);
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
"GUID:c1c03c0e5b2f4412b9f2be1c20d6a9b1",
|
||||
"GUID:b4c9f7fbf1e144933a1797dc208ece5f",
|
||||
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc",
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23",
|
||||
"GUID:75469ad4d38634e559750d17036d5f7c"
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee0bbb69e2ec44637a7f5fb6f18a2518
|
||||
@@ -16,7 +16,6 @@ namespace Darkmatter.Features.AppBoot.Installers
|
||||
if (sceneRefs != null)
|
||||
builder.RegisterComponent(sceneRefs);
|
||||
builder.RegisterEntryPoint<AppBootFlow>();
|
||||
builder.RegisterEntryPoint<AdaptiveFrameRate>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Darkmatter.Features.Coloring.Systems
|
||||
@@ -9,7 +8,6 @@ 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;
|
||||
@@ -24,7 +22,6 @@ 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)
|
||||
{
|
||||
@@ -37,26 +34,6 @@ 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()
|
||||
@@ -69,7 +46,6 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
|
||||
_currentZoom = 1f;
|
||||
_isPinching = false;
|
||||
UpdatePanRelayState();
|
||||
}
|
||||
|
||||
public IDisposable UseNonZoomedView()
|
||||
@@ -102,7 +78,6 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
|
||||
_isPinching = false;
|
||||
TryApplyScrollZoom();
|
||||
TryApplyMousePan();
|
||||
}
|
||||
|
||||
private bool TryApplyPinchZoom()
|
||||
@@ -136,16 +111,8 @@ 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);
|
||||
ApplyZoomPan(previousMidpointLocal, midpointLocal, targetZoom);
|
||||
ZoomTowards(midpointLocal, targetZoom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,123 +152,12 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyZoomPan(parentLocalPoint, parentLocalPoint, targetZoom);
|
||||
}
|
||||
|
||||
private void ApplyZoomPan(Vector2 previousLocalPoint, Vector2 currentLocalPoint, float targetZoom)
|
||||
{
|
||||
Vector2 contentPoint = (previousLocalPoint - _target.anchoredPosition) / _currentZoom;
|
||||
Vector2 contentPoint = (parentLocalPoint - _target.anchoredPosition) / _currentZoom;
|
||||
_currentZoom = targetZoom;
|
||||
_target.localScale = _baseScale * _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));
|
||||
_target.anchoredPosition = Mathf.Approximately(_currentZoom, minZoom)
|
||||
? _baseAnchoredPosition
|
||||
: parentLocalPoint - contentPoint * _currentZoom;
|
||||
}
|
||||
|
||||
private bool TryGetParentLocalPoint(Vector2 screenPoint, out Vector2 localPoint)
|
||||
@@ -377,7 +233,6 @@ namespace Darkmatter.Features.Coloring.Systems
|
||||
_target.anchoredPosition = state.AnchoredPosition;
|
||||
_currentZoom = state.CurrentZoom;
|
||||
_isPinching = false;
|
||||
UpdatePanRelayState();
|
||||
}
|
||||
|
||||
private readonly struct ViewState
|
||||
@@ -430,25 +285,5 @@ 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,12 +25,6 @@ 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)
|
||||
{
|
||||
@@ -115,73 +109,29 @@ namespace Darkmatter.Features.DrawingCatalog
|
||||
|
||||
private async UniTask RefreshAsync(CancellationToken ct)
|
||||
{
|
||||
var newSprites = new List<Sprite>();
|
||||
var newTextures = new List<Texture2D>();
|
||||
var applied = false;
|
||||
var ids = ToFixedRowGridOrder(_controller.VisibleIds);
|
||||
var vms = new List<CatalogItemVM>(ids.Count);
|
||||
|
||||
try
|
||||
_totalPages = (int)Math.Ceiling(ids.Count / (float)ItemPerPage);
|
||||
_currentPage = 0;
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
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;
|
||||
_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());
|
||||
var thumb = await _catalog.GetThumbnailAsync(id);
|
||||
vms.Add(new CatalogItemVM(id, thumb));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// Idempotent — always unblock the flow; a stuck loading screen is worse than
|
||||
// an empty catalog.
|
||||
_controller.NotifyPopulated();
|
||||
}
|
||||
}
|
||||
if (ct.IsCancellationRequested) return;
|
||||
_view.ResetScrollPosition();
|
||||
_view.SetItems(vms);
|
||||
_view.SetPagination(_currentPage, _totalPages);
|
||||
|
||||
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();
|
||||
// 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 static List<string> ToFixedRowGridOrder(IReadOnlyList<string> ids)
|
||||
@@ -210,7 +160,6 @@ 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 sprite, bool owned)> GetThumbnailAsync(string id)
|
||||
public async UniTask<Sprite> 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)), true);
|
||||
return Sprite.Create(savedTex, rect, new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
return (t.DefaultThumbnail, false);
|
||||
return t.DefaultThumbnail;
|
||||
}
|
||||
|
||||
public UniTask<IDrawingTemplate> LoadAsync(string id)
|
||||
|
||||
@@ -46,7 +46,6 @@ 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;
|
||||
@@ -81,27 +80,6 @@ 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;
|
||||
@@ -149,42 +127,14 @@ 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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
public async UniTask SaveAsync(CancellationToken ct)
|
||||
@@ -197,48 +147,48 @@ namespace Darkmatter.Features.GameplayFlow.Systems
|
||||
|
||||
public async UniTask NextAsync(CancellationToken ct)
|
||||
{
|
||||
if (_navigating) return;
|
||||
_navigating = true;
|
||||
try
|
||||
// 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)
|
||||
{
|
||||
// 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;
|
||||
_allContentReported = true;
|
||||
_bus.Publish(new AllContentCompletedSignal(_progression.CompletedTemplateIds.Count));
|
||||
}
|
||||
|
||||
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);
|
||||
var nextId = _catalog.GetNextTemplate(_templateId);
|
||||
if (string.IsNullOrEmpty(nextId))
|
||||
{
|
||||
_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);
|
||||
if (!string.IsNullOrEmpty(nextId))
|
||||
_bus.Publish(new DrawingSelectedSignal(nextId));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_navigating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_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));
|
||||
}
|
||||
|
||||
|
||||
@@ -283,22 +233,13 @@ namespace Darkmatter.Features.GameplayFlow.Systems
|
||||
|
||||
private async UniTaskVoid EditAsync(string 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;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
private async UniTaskVoid DebouncedAutosaveAsync(CancellationToken ct)
|
||||
|
||||
@@ -13,7 +13,6 @@ 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)
|
||||
@@ -27,7 +26,7 @@ namespace Darkmatter.Features.Mainmenu
|
||||
{
|
||||
_view.OnPlayBtnPressedEvent += OnPlayBtnPressed;
|
||||
_view.OnPlayBtnClickedEvent += OnPlayBtnClicked;
|
||||
_introSubscription = _eventBus.Subscribe<IntroCompletedSignal>(OnIntroComplete);
|
||||
_eventBus.Subscribe<IntroCompletedSignal>(OnIntroComplete);
|
||||
}
|
||||
|
||||
private void OnIntroComplete(IntroCompletedSignal signal)
|
||||
@@ -49,7 +48,6 @@ namespace Darkmatter.Features.Mainmenu
|
||||
{
|
||||
_view.OnPlayBtnPressedEvent -= OnPlayBtnPressed;
|
||||
_view.OnPlayBtnClickedEvent -= OnPlayBtnClicked;
|
||||
_introSubscription?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,27 +86,16 @@ public sealed class ProgressionRepository
|
||||
{
|
||||
if (string.IsNullOrEmpty(templateId) || png == null || png.Length == 0) return;
|
||||
|
||||
// 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
|
||||
{
|
||||
Directory.CreateDirectory(ThumbnailDirectory());
|
||||
var path = ThumbnailPath(templateId);
|
||||
var tmp = path + ".tmp";
|
||||
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)
|
||||
await UniTask.RunOnThreadPool(() =>
|
||||
{
|
||||
Debug.LogWarning($"[Progression] Failed writing thumbnail '{templateId}': {e.Message}");
|
||||
}
|
||||
File.WriteAllBytes(tmp, png);
|
||||
if (File.Exists(path)) File.Delete(path);
|
||||
File.Move(tmp, path);
|
||||
});
|
||||
}
|
||||
|
||||
public async UniTask<Texture2D> LoadThumbnailAsync(string templateId)
|
||||
|
||||
@@ -15,23 +15,7 @@ namespace Darkmatter.Libs.Observer
|
||||
if (_map.TryGetValue(typeof(T), out var handlerObj))
|
||||
{
|
||||
var action = (Action<T>)handlerObj;
|
||||
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);
|
||||
}
|
||||
}
|
||||
action?.Invoke(evt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -350,9 +350,7 @@ 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,29 +97,17 @@ namespace Darkmatter.Services.Capture
|
||||
}
|
||||
finally
|
||||
{
|
||||
RenderTexture.active = prevActive;
|
||||
Object.Destroy(cropped);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
paperCanvas.renderMode = prevMode;
|
||||
paperCanvas.worldCamera = prevWorldCam;
|
||||
paperCanvas.planeDistance = prevPlaneDist;
|
||||
cam.targetTexture = prevTarget;
|
||||
cam.clearFlags = prevFlags;
|
||||
cam.backgroundColor = prevBg;
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
if (disabledGraphics != null)
|
||||
foreach (var g in disabledGraphics)
|
||||
|
||||
@@ -118,17 +118,8 @@ namespace Darkmatter.Services.Gallery
|
||||
GL.PopMatrix();
|
||||
|
||||
var output = new Texture2D(bgW, bgH, TextureFormat.RGBA32, mipChain: false);
|
||||
try
|
||||
{
|
||||
output.ReadPixels(new Rect(0, 0, bgW, bgH), 0, 0);
|
||||
output.Apply();
|
||||
}
|
||||
catch
|
||||
{
|
||||
UnityEngine.Object.Destroy(output);
|
||||
throw;
|
||||
}
|
||||
|
||||
output.ReadPixels(new Rect(0, 0, bgW, bgH), 0, 0);
|
||||
output.Apply();
|
||||
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.
@@ -23,7 +23,7 @@ MonoBehaviour:
|
||||
m_RequireOpaqueTexture: 0
|
||||
m_OpaqueDownsampling: 1
|
||||
m_SupportsTerrainHoles: 1
|
||||
m_SupportsHDR: 0
|
||||
m_SupportsHDR: 1
|
||||
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: 0
|
||||
m_MainLightShadowsSupported: 1
|
||||
m_MainLightShadowmapResolution: 2048
|
||||
m_AdditionalLightsRenderingMode: 1
|
||||
m_AdditionalLightsPerObjectLimit: 4
|
||||
@@ -99,7 +99,7 @@ MonoBehaviour:
|
||||
obsoleteHasProbeVolumes:
|
||||
m_Keys: []
|
||||
m_Values:
|
||||
m_PrefilteringModeMainLightShadows: 0
|
||||
m_PrefilteringModeMainLightShadows: 4
|
||||
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">902fe897-8c3c-4178-afb3-844ca2e340bb</string></resources>
|
||||
<?xml version="1.0" encoding="utf-8"?><resources><string name="com.crashlytics.android.build_id" translatable="false">8fc48ed6-41de-4dcf-a980-9aa3316fad11</string></resources>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.google.android.play.core.missingsplits;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
public class MissingSplitsManagingApplication extends Application {
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
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:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 393d9cf56c4764b309bdf0f6115b26de
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 251093c323b854663b7ced2a6f92fece
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5098914d90c8144309a4a8db29063644
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dcd719e6098c4a7ebac80ed0d7164e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50a84b4d722374e35b2ce3232ec677cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 136d5838f159543798c8cd09221e6c9a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,70 +0,0 @@
|
||||
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**
|
||||
@@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93290f6ffc554b9589f42bc71d39e40b
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -13,4 +13,4 @@ MonoBehaviour:
|
||||
m_Name: SmartlookSettings
|
||||
m_EditorClassIdentifier:
|
||||
ProjectKey: 3bcfb098c5c4f6f1f48d8bbef4c0c94d7c2f9bc9
|
||||
FPS: 2
|
||||
FPS: 30
|
||||
|
||||
@@ -11,7 +11,7 @@ PlayerSettings:
|
||||
defaultScreenOrientation: 4
|
||||
targetDevice: 2
|
||||
useOnDemandResources: 0
|
||||
accelerometerFrequency: 0
|
||||
accelerometerFrequency: 60
|
||||
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.2
|
||||
bundleVersion: 2.0
|
||||
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: 12
|
||||
AndroidBundleVersionCode: 9
|
||||
AndroidMinSdkVersion: 26
|
||||
AndroidTargetSdkVersion: 0
|
||||
AndroidPreferredInstallLocation: 1
|
||||
@@ -268,7 +268,7 @@ PlayerSettings:
|
||||
useCustomMainManifest: 1
|
||||
useCustomLauncherManifest: 0
|
||||
useCustomMainGradleTemplate: 1
|
||||
useCustomLauncherGradleManifest: 1
|
||||
useCustomLauncherGradleManifest: 0
|
||||
useCustomBaseGradleTemplate: 0
|
||||
useCustomGradlePropertiesTemplate: 1
|
||||
useCustomGradleSettingsTemplate: 1
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
m_EditorVersion: 6000.5.1f1
|
||||
m_EditorVersionWithRevision: 6000.5.1f1 (0d9463e84828)
|
||||
m_EditorVersion: 6000.5.3f1
|
||||
m_EditorVersionWithRevision: 6000.5.3f1 (c2eb47b3a2a9)
|
||||
|
||||
Reference in New Issue
Block a user