Success in mobile game development depends on accurately analyzing user behavior and making decisions based on this analysis. As Chipode, we will share how we collect and use analytics data to continuously improve the user experience in our Sudoku app.
Analytics Infrastructure
1- Firebase Analytics Integration
dart
class AnalyticsService {
final FirebaseAnalytics _analytics;
AnalyticsService() : _analytics = FirebaseAnalytics.instance;
Future logGameStart({
required String difficulty,
required bool isDaily,
}) async {
await _analytics.logEvent(
name: 'game_start',
parameters: {
'difficulty': difficulty,
'game_type': isDaily ? 'daily' : 'regular',
'timestamp': DateTime.now().toIso8601String(),
},
);
}
Future logGameComplete({
required String difficulty,
required int timeSpent,
required int score,
required bool usedHints,
}) async {
await _analytics.logEvent(
name: 'game_complete',
parameters: {
'difficulty': difficulty,
'time_spent': timeSpent,
'score': score,
'used_hints': usedHints,
},
);
}
}
2- Custom Event Tracking
dart
class GameAnalytics {
final AnalyticsService _analytics;
final UserMetricsRepository _metrics;
Future trackCellInput({
required int row,
required int col,
required int value,
required bool isNote,
required Duration timeToDecide,
}) async {
await _analytics.logEvent(
name: 'cell_input',
parameters: {
'position': '${row}x${col}',
'value': value,
'is_note': isNote,
'decision_time': timeToDecide.inSeconds,
},
);
}
Future trackHintUsage({
required HintType type,
required int remainingHints,
required GameProgress progress,
}) async {
await _analytics.logEvent(
name: 'hint_used',
parameters: {
'hint_type': type.toString(),
'remaining_hints': remainingHints,
'board_completion': progress.completionPercentage,
'stuck_duration': progress.timeSinceLastMove.inMinutes,
},
);
}
}
User Segmentation
1- Player Profile Analysis
dart
class PlayerProfileAnalyzer {
PlayerSegment categorizePlayer(UserMetrics metrics) {
final skillLevel = _calculateSkillLevel(metrics);
final engagement = _calculateEngagement(metrics);
final monetization = _analyzeMonetization(metrics);
return PlayerSegment(
skillLevel: skillLevel,
engagementLevel: engagement,
monetizationTier: monetization,
);
}
SkillLevel _calculateSkillLevel(UserMetrics metrics) {
final avgTimePerPuzzle = metrics.totalPlayTime.inMinutes /
metrics.completedPuzzles;
final difficultyScore = metrics.completedByDifficulty.entries
.map((e) => e.key.weight * e.value)
.reduce((a, b) => a + b);
if (avgTimePerPuzzle < 5 && difficultyScore > 100) {
return SkillLevel.expert;
} else if (avgTimePerPuzzle < 10 && difficultyScore > 50) {
return SkillLevel.advanced;
}
return SkillLevel.beginner;
}
// Diğer analiz metodları...
}
2- Cohort Analysis
dart
class CohortAnalysis {
Future
Performance Metrics
1- Oyun Performansı Takibi
dart
class GamePerformanceTracker {
final PerformanceCollector _collector;
void trackFrameTime(Duration frameTime) {
_collector.addMetric(
name: 'frame_time',
value: frameTime.inMicroseconds,
labels: {'screen': 'game_board'},
);
}
void trackLoadTime({
required String difficulty,
required Duration duration,
}) {
_collector.addMetric(
name: 'puzzle_load_time',
value: duration.inMilliseconds,
labels: {
'difficulty': difficulty,
'cache_status': duration.inSeconds > 1 ? 'miss' : 'hit',
},
);
}
void trackMemoryUsage() {
if (!kReleaseMode) return;
final usage = ProcessInfo.currentRss;
_collector.addMetric(
name: 'memory_usage',
value: usage,
labels: {'type': 'rss'},
);
}
}
2- Network Performance Monitoring
dart
class NetworkPerformanceMonitor {
final _metrics = >{};
void trackApiCall(String endpoint, Duration duration) {
_metrics.putIfAbsent(endpoint, () => []).add(duration);
if (_metrics[endpoint]!.length >= 100) {
_calculateAndReportMetrics(endpoint);
}
}
void _calculateAndReportMetrics(String endpoint) {
final durations = _metrics[endpoint]!;
durations.sort();
final p50 = durations[durations.length ~/ 2];
final p90 = durations[(durations.length * 0.9).floor()];
final p99 = durations[(durations.length * 0.99).floor()];
_reportLatencyMetrics(endpoint, {
'p50': p50.inMilliseconds,
'p90': p90.inMilliseconds,
'p99': p99.inMilliseconds,
});
_metrics[endpoint]?.clear();
}
}
A/B Testing
1- Test Configuration1- Test Konfigürasyonu
dart
class ABTestingService {
final RemoteConfig _config;
final AnalyticsService _analytics;
Future getVariant(String experiment) async {
final variantId = _config.getString('exp_${experiment}_variant');
return ExperimentVariant.fromId(variantId);
}
Future trackExposure({
required String experiment,
required String variant,
}) async {
await _analytics.logEvent(
name: 'experiment_exposure',
parameters: {
'experiment': experiment,
'variant': variant,
},
);
}
Future trackConversion({
required String experiment,
required String variant,
required String goal,
}) async {
await _analytics.logEvent(
name: 'experiment_conversion',
parameters: {
'experiment': experiment,
'variant': variant,
'goal': goal,
},
);
}
}
2- Test Implementation
dart
class DifficultyExperiment extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, abTest, child) {
return FutureBuilder(
future: abTest.getVariant('difficulty_screens'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return DefaultDifficultyScreen();
}
final variant = snapshot.data!;
abTest.trackExposure(
experiment: 'difficulty_screens',
variant: variant.id,
);
switch (variant) {
case ExperimentVariant.control:
return DefaultDifficultyScreen();
case ExperimentVariant.test:
return NewDifficultyScreen();
default:
return DefaultDifficultyScreen();
}
},
);
},
);
}
}
Data Visualization
1- Dashboard Widgets
dart
class MetricsDashboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
children: [
RetentionChart(),
EngagementMetrics(),
DifficultyDistribution(),
TimeSpentAnalysis(),
],
);
}
}
class RetentionChart extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, repository, child) {
return FutureBuilder(
future: repository.getRetentionData(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return LoadingIndicator();
}
return LineChart(
data: snapshot.data!.toChartData(),
xAxis: ChartAxis(label: 'Gün'),
yAxis: ChartAxis(
label: 'Kullanıcı Oranı',
format: percentageFormat,
),
);
},
);
},
);
}
}
Machine Learning Integration
1- Difficulty Level Recommendation
dart
class DifficultyRecommender {
final MLModel _model;
Future recommendDifficulty(UserMetrics metrics) async {
final features = _prepareFeatures(metrics);
final prediction = await _model.predict(features);
return _difficultyFromPrediction(prediction);
}
Map _prepareFeatures(UserMetrics metrics) {
return {
'avg_completion_time': metrics.averageCompletionTime.inMinutes,
'success_rate': metrics.successRate,
'hints_used_ratio': metrics.hintsUsedRatio,
'highest_difficulty_completed': metrics.highestDifficultyCompleted,
'total_games_played': metrics.totalGamesPlayed,
};
}
GameDifficulty _difficultyFromPrediction(double prediction) {
if (prediction > 0.8) return GameDifficulty.expert;
if (prediction > 0.6) return GameDifficulty.hard;
if (prediction > 0.4) return GameDifficulty.medium;
return GameDifficulty.easy;
}
}
Result
Thanks to the correct analysis and use of Analytics data:
1. We are able to continuously improve the user experience
2. We can optimize game difficulty according to users
3. We can proactively detect performance issues
4. We can safely test new features with A/B tests
5. We can make data-driven decisions
Future Plans
– Real-time analytics
– Advanced anomaly detection
– Personalized gaming experience
– Churn prediction model
– Analysis of social characteristics





