mobile-gaming-analytics-kullanici-davranislarini-anlama

Mobile Gaming Analytics: Understanding User Behavior

Table of Contents

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<void> 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<void> 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<void> 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<void> 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<Map<String, RetentionMetrics>> analyzeRetention(
    DateTime startDate,
    DateTime endDate,
  ) async {
    final cohorts = await _groupUsersByCohort(startDate, endDate);
    final metrics = <String, RetentionMetrics>{};
    
    for (var cohort in cohorts.entries) {
      metrics[cohort.key] = await _calculateRetention(
        users: cohort.value,
        startDate: cohort.key,
      );
    }
    
    return metrics;
  }
  
  Future<RetentionMetrics> _calculateRetention({
    required List<String> users,
    required DateTime startDate,
  }) async {
    final day1 = await _getActiveUsers(users, startDate.add(Duration(days: 1)));
    final day7 = await _getActiveUsers(users, startDate.add(Duration(days: 7)));
    final day30 = await _getActiveUsers(users, startDate.add(Duration(days: 30)));
    
    return RetentionMetrics(
      totalUsers: users.length,
      day1Retention: day1.length / users.length,
      day7Retention: day7.length / users.length,
      day30Retention: day30.length / users.length,
    );
  }
}


				
			

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 = <String, List<Duration>>{};
  
  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<ExperimentVariant> getVariant(String experiment) async {
    final variantId = _config.getString('exp_${experiment}_variant');
    return ExperimentVariant.fromId(variantId);
  }
  
  Future<void> trackExposure({
    required String experiment,
    required String variant,
  }) async {
    await _analytics.logEvent(
      name: 'experiment_exposure',
      parameters: {
        'experiment': experiment,
        'variant': variant,
      },
    );
  }
  
  Future<void> 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<ABTestingService>(
      builder: (context, abTest, child) {
        return FutureBuilder<ExperimentVariant>(
          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<AnalyticsRepository>(
      builder: (context, repository, child) {
        return FutureBuilder<RetentionData>(
          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<GameDifficulty> recommendDifficulty(UserMetrics metrics) async {
    final features = _prepareFeatures(metrics);
    final prediction = await _model.predict(features);
    
    return _difficultyFromPrediction(prediction);
  }
  
  Map<String, dynamic> _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

References

Twitter
LinkedIn
Related Articles
Contact Form

Chipode Apps

loading...
Google Play Store
loading...
Apple Store