Difficulty based sorting and analytics fixes

This commit is contained in:
Savya Bikram Shah
2026-07-06 13:20:27 +05:45
parent b28e1f637d
commit 3df3d923fb
43 changed files with 4231 additions and 64 deletions

View File

@@ -56,8 +56,9 @@ namespace Darkmatter.Services.Analytics
_subs.Add(_bus.Subscribe<IntroCompletedSignal>(_ => _analytics.LogEvent(AnalyticsEvents.IntroCompleted)));
_subs.Add(_bus.Subscribe<PlayBtnClickedSignal>(_ => _analytics.LogEvent(AnalyticsEvents.PlayClicked)));
// Navigation
_subs.Add(_bus.Subscribe<OpenColorBookSignal>(_ => _analytics.LogEvent(AnalyticsEvents.ColorbookOpened)));
// Navigation. ColorbookShownSignal (not OpenColorBookSignal, which is the show-view command
// and misses scene-entry paths) fires on every way the catalog becomes visible.
_subs.Add(_bus.Subscribe<ColorbookShownSignal>(_ => OnColorbookShown()));
_subs.Add(_bus.Subscribe<OpenArtBookSignal>(_ => _analytics.LogEvent(AnalyticsEvents.ArtbookOpened)));
_subs.Add(_bus.Subscribe<ReturnToMainMenuSignal>(_ => OnReturnToMainMenu()));
@@ -90,6 +91,10 @@ namespace Darkmatter.Services.Analytics
private void OnDrawingSelected(string drawingId)
{
// GameplayFlowController relays the selection a second time after the scene swap
// (artbook edit path) — same id while already active is that relay, not a new selection.
if (drawingId == _activeDrawingId) return;
// A new selection while a drawing is still open (uncompleted) = the previous one was abandoned.
EndActiveDrawingIfAbandoned();
@@ -156,7 +161,7 @@ namespace Darkmatter.Services.Analytics
if (dur >= 0f) p[AnalyticsParams.DurationSeconds] = dur;
_analytics.LogEvent(AnalyticsEvents.DrawingCompleted, p);
// GA4 recommended games event (lights up built-in level funnels).
// Companion to GA4's level_start (custom name — GA4's own pair would be level_end).
_analytics.LogEvent(AnalyticsEvents.LevelComplete, new Dictionary<string, object>
{
[AnalyticsParams.LevelName] = s.TemplateId,
@@ -178,9 +183,11 @@ namespace Darkmatter.Services.Analytics
private void OnGallerySaveCompleted(GallerySaveCompletedSignal s)
{
_analytics.LogEvent(AnalyticsEvents.GallerySaveCompleted, AnalyticsParams.Success, s.Success ? "true" : "false");
// Prefer the id carried by the signal: art-book saves happen with no active drawing.
string drawingId = string.IsNullOrEmpty(s.TemplateId) ? _activeDrawingId ?? string.Empty : s.TemplateId;
_analytics.LogEvent(AnalyticsEvents.DrawingSaved, new Dictionary<string, object>
{
[AnalyticsParams.DrawingId] = _activeDrawingId ?? string.Empty,
[AnalyticsParams.DrawingId] = drawingId,
[AnalyticsParams.Success] = s.Success ? 1 : 0,
});
}
@@ -191,6 +198,15 @@ namespace Darkmatter.Services.Analytics
_analytics.LogEvent(AnalyticsEvents.MainMenuReturned);
}
private void OnColorbookShown()
{
// Reaching the catalog with a drawing still open means the player backed out of gameplay
// mid-drawing — the abandon moment. (Completion clears the active drawing before the
// catalog reloads, so the finish path never lands here with one open.)
EndActiveDrawingIfAbandoned();
_analytics.LogEvent(AnalyticsEvents.ColorbookOpened);
}
// Emits drawing_abandoned for an open, uncompleted drawing. Completion clears _activeDrawingId, so
// reaching here with a non-null id means the drawing was left without finishing — the leak signal.
private void EndActiveDrawingIfAbandoned()

View File

@@ -68,44 +68,36 @@ namespace Darkmatter.Services.Capture
var prevActive = RenderTexture.active;
RenderTexture.active = rt;
var fullScreen = new Texture2D(sw, sh, TextureFormat.RGBA32, mipChain: false);
fullScreen.ReadPixels(new Rect(0, 0, sw, sh), 0, 0);
fullScreen.Apply();
// Read only the crop region straight off the RT. A full-screen ReadPixels followed by
// 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
{
var cropped = new Texture2D(cropW, cropH, TextureFormat.RGBA32, mipChain: false);
int targetW = Mathf.Max(1, Mathf.RoundToInt(cropW * scale));
int targetH = Mathf.Max(1, Mathf.RoundToInt(cropH * scale));
Texture2D output = cropped;
if (output.width != targetW || output.height != targetH)
output = Resize(cropped, targetW, targetH);
try
{
cropped.SetPixels(fullScreen.GetPixels((int)crop.x, (int)crop.y, cropW, cropH));
cropped.Apply();
int targetW = Mathf.Max(1, Mathf.RoundToInt(cropW * scale));
int targetH = Mathf.Max(1, Mathf.RoundToInt(cropH * scale));
Texture2D output = cropped;
if (output.width != targetW || output.height != targetH)
output = Resize(cropped, targetW, targetH);
try
{
return output.EncodeToPNG();
}
finally
{
if (output != cropped) Object.Destroy(output);
}
return output.EncodeToPNG();
}
finally
{
Object.Destroy(cropped);
if (output != cropped) Object.Destroy(output);
}
}
finally
{
Object.Destroy(fullScreen);
Object.Destroy(cropped);
}
}
finally