Среда
- Flutter
- Flame
- games_services: ^4.1.1
- Android и iOS собирает
- Игровые сервисы Google Play (новый API v2)
В pubspec.yaml:
Код: Выделить всё
dependencies:
games_services: ^4.1.1
Код: Выделить всё
dependencies {
implementation "com.google.android.gms:play-services-games-v2:+"
implementation "com.google.android.gms:play-services-auth:21.3.0"
implementation "com.google.android.play:integrity:1.6.0"
}
- Включены игровые сервисы Play
- Таблица лидеров создана и опубликована
- Связан проект Google Cloud
- Учетные данные OAuth настроены
- Приложение подписано и загружено правильно
- Внутреннее/производственное тестирование работает
Код: Выделить всё
import 'dart:async';
import 'package:games_services/games_services.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'game_data.dart'; // Import your GameData class
class GamesServicesController {
// Your leaderboard IDs
static const String iosLeaderboardId = [iosLeaderboardId];
static const String androidLeaderboardId = [androidLeaderboardId];
// Migration tracking key
static const String _migrationCompleteKey = 'score_migration_complete';
static final GamesServicesController _instance = GamesServicesController._internal();
factory GamesServicesController() => _instance;
GamesServicesController._internal();
bool _signedIn = false;
bool _isInitialized = false;
// Public getters
bool get signedIn => _signedIn;
bool get isInitialized => _isInitialized;
/// Initialize games services - call this once at app startup
Future initialize() async {
if (_isInitialized) return;
try {
debugPrint('Initializing Games Services...');
await GamesServices.signIn();
_signedIn = await GamesServices.isSignedIn;
debugPrint('Games Services signed in: $_signedIn');
// If signed in successfully, sync cloud score and migrate if needed
if (_signedIn) {
await syncCloudScoreToLocal();
await _migrateOfflineScoreIfNeeded();
}
} on Exception catch (e) {
debugPrint('Games Services initialization failed: $e');
_signedIn = false;
}
_isInitialized = true;
}
/// Sync cloud score down to local storage
Future syncCloudScoreToLocal() async {
if (!_signedIn) {
debugPrint('Cannot sync cloud score - not signed in');
return;
}
try {
// Get the player's score from the cloud
int? cloudScore = await GamesServices.getPlayerScore(
iOSLeaderboardID: iosLeaderboardId,
androidLeaderboardID: androidLeaderboardId,
);
if (cloudScore != null && cloudScore > 0) {
// Get local score
final localScore = await GameData.getBestScore();
// If cloud score is higher, update local score
if (cloudScore > localScore) {
debugPrint('Syncing cloud score ($cloudScore) to local storage (was $localScore)');
await GameData.saveBestScore(cloudScore);
}
}
} on Exception catch (e) {
debugPrint('Failed to sync cloud score to local: $e');
}
}
/// Migrate offline score to online leaderboard (one-time operation)
Future _migrateOfflineScoreIfNeeded() async {
try {
final prefs = await SharedPreferences.getInstance();
final migrationComplete = prefs.getBool(_migrationCompleteKey) ?? false;
if (migrationComplete) {
debugPrint('Score migration already completed');
return;
}
final offlineBestScore = await GameData.getBestScore();
if (offlineBestScore > 0) {
debugPrint('Migrating offline score: $offlineBestScore');
// Submit the offline score to the leaderboard
await GamesServices.submitScore(
score: Score(
iOSLeaderboardID: iosLeaderboardId,
androidLeaderboardID: androidLeaderboardId,
value: offlineBestScore,
),
);
debugPrint('Offline score migrated successfully');
}
// Mark migration as complete
await prefs.setBool(_migrationCompleteKey, true);
} on Exception catch (e) {
debugPrint('Failed to migrate offline score: $e');
// Don't mark as complete if migration failed
}
}
/// Submit score to leaderboard (enhanced version)
Future submitScore(int score) async {
// Only submit if signed in
if (_signedIn) {
try {
int? onlinePlayerScore = await GamesServices.getPlayerScore(
iOSLeaderboardID: iosLeaderboardId,
androidLeaderboardID: androidLeaderboardId,
);
if (onlinePlayerScore == null || score > onlinePlayerScore) {
await GamesServices.submitScore(
score: Score(
iOSLeaderboardID: iosLeaderboardId,
androidLeaderboardID: androidLeaderboardId,
value: score,
),
);
}
} on Exception catch (e) {
debugPrint('Failed to submit score online: $e');
}
}
}
/// Show the platform's leaderboard UI
Future showLeaderboard() async {
if (!_signedIn) {
debugPrint('Cannot show leaderboard - not signed in to Games Services');
return;
}
try {
// Sync with a timeout to avoid long delays
await forceSyncOfflineScore().timeout(
const Duration(seconds: 2),
onTimeout: () {
debugPrint('Score sync timed out, showing leaderboard anyway');
},
);
await GamesServices.showLeaderboards(
iOSLeaderboardID: iosLeaderboardId,
androidLeaderboardID: androidLeaderboardId,
);
} on Exception catch (e) {
debugPrint('Failed to show leaderboard: $e');
}
}
/// Sign in manually (if auto sign-in failed)
Future signIn() async {
try {
await GamesServices.signIn();
_signedIn = await GamesServices.isSignedIn;
// If sign-in successful, sync cloud score and try migration
if (_signedIn) {
await syncCloudScoreToLocal();
await _migrateOfflineScoreIfNeeded();
}
return _signedIn;
} on Exception catch (e) {
debugPrint('Manual sign-in failed: $e');
return false;
}
}
/// Force sync offline scores (useful for debugging or manual sync)
Future forceSyncOfflineScore() async {
if (!_signedIn) {
debugPrint('Cannot sync - not signed in');
return;
}
final offlineBestScore = await GameData.getBestScore();
if (offlineBestScore > 0) {
await submitScore(offlineBestScore);
}
}
/// Reset migration flag (for testing purposes)
Future resetMigrationFlag() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_migrationCompleteKey);
debugPrint('Migration flag reset');
}
}
- Отправлять результаты
- Правильно обновлять свой лучший результат
- В таблице лидеров появляются как новые, так и существующие игроки
- Результаты загружаются правильно
Только на Android:
Новые игроки (которые никогда раньше не были в таблице лидеров):
- Могут играть в игру
- Могут набирать высокие баллы (даже выше, чем существующие игроки)
Существующие игроки Android продолжают нормально обновлять свои результаты.
Я действительно запутался, потому что, если бы это была проблема с кодом, существующие проигрыватели Android также должны были выйти из строя, или iOS должна показать ту же проблему.
Если интеграция Google Cloud / Play Games была нарушена, результаты не должны загружаться ни для кого.
Но вместо этого это касается только новых пользователей Android.
Что может привести к тому, что таблицы лидеров Google Play Games на Android будут приняты оценивать материалы существующих игроков и молча игнорировать или не отображать результаты новых игроков?
Будем очень признательны за любые идеи или советы по отладке.
Подробнее здесь: https://stackoverflow.com/questions/798 ... flutter-fl
Мобильная версия