Mobil oyun geliştirmede başarı, kullanıcı davranışlarını doğru analiz etmek ve bu analizler doğrultusunda kararlar almaktan geçer. Chipode olarak, Sudoku uygulamamızda kullanıcı deneyimini sürekli iyileştirmek için analytics verilerini nasıl topladığımızı ve kullandığımızı bu yazıda paylaşacağız.
Analytics Altyapısı
1- Firebase Analytics Entegrasyonu
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,
},
);
}
}
Kullanıcı Segmentasyonu
1- Oyuncu Profili Analizi
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 Analizi
dart
class CohortAnalysis {
Future
Performans Metrikleri
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- Ağ Performansı İzleme
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 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 İmplementasyonu
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();
}
},
);
},
);
}
}
Veri Görselleştirme
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 Entegrasyonu
1- Zorluk Seviyesi Önerisi
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;
}
}
Sonuç
Analytics verilerinin doğru analizi ve kullanımı sayesinde:
1. Kullanıcı deneyimini sürekli iyileştirebiliyoruz
2. Oyun zorluğunu kullanıcılara göre optimize edebiliyoruz
3. Performans sorunlarını proaktif olarak tespit edebiliyoruz
4. A/B testleri ile yeni özellikleri güvenle test edebiliyoruz
5. Veri odaklı kararlar alabiliyoruz
İleriye Dönük Planlar
– Gerçek zamanlı analytics
– Gelişmiş anomali tespiti
– Kişiselleştirilmiş oyun deneyimi
– Churn prediction modeli
– Sosyal özelliklerin analizi





