Google Play Game Services в Libgdx

image

Введение


Многие разработчики игр хотят использовать сервис Google Play Game Services в своих играх. Я не был исключением, но знаний и навыков как быстро добавить поддержку GPGS в свою libgdx игру не было. В данной статье опишу процесс подключения таблицы рекордов и достижений. Исходные данные: Eclipse, настроенная консоль разработчика для работы с игровыми сервисами, android-проект, корневой проект.

Настройка


Не буду рассказывать, как настроить консоль разработчика для работы с GPGS, скажу лишь, что оттуда нам понадобятся ресурсы вида:
games-ids.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
Google Play game services IDs.
Save this file as res/values/games-ids.xml in your project.
-->
<resources>
  <string name="app_id">310266082735</string>
  <string name="achievement_take_10_levels">HgkIr62KmoQJEAIQBg</string>
  <string name="achievement_take_30_levels">HgkIr62KmoQJEAIQBw</string>
  <string name="achievement_take_50_levels">HgkIr62KmoQJEAIQCA</string>
  <string name="achievement_take_70_levels">HgkIr62KmoQJEAIQCg</string>
  <string name="achievement_take_100_levels">HgkIr62KmoQJEAIQCQ</string>
  <string name="achievement_beginner_cutter">HgkIr62KmoQJEAIQDw</string>
  <string name="achievement_advanced_cutter">HgkIr62KmoQJEAIQEA</string>
  <string name="achievement_master_cutter">HgkIr62KmoQJEAIQEQ</string>
  <string name="achievement_lucky">HgkIr62KmoQJEAIQEg</string>
  <string name="achievement_cheerful">HgkIr62KmoQJEAIQEw</string>
  <string name="achievement_exceptional_joyous">HgkIr62KmoQJEAIQFA</string>
  <string name="achievement_thrust">HgkIr62KmoQJEAIQFQ</string>
  <string name="achievement_very_persistent">HgkIr62KmoQJEAIQFg</string>
  <string name="achievement_the_most_resistant">HgkIr62KmoQJEAIQFw</string>
  <string name="achievement_1_000_000">HgkIr62KmoQJEAIQGA</string>
  <string name="achievement_2_000_000">HgkIr62KmoQJEAIQGQ</string>
  <string name="achievement_3_000_000">HgkIr62KmoQJEAIQGg</string>
  <string name="achievement_unlucky">HgkIr62KmoQJEAIQGw</string>
  <string name="achievement_loyal">HgkIr62KmoQJEAIQHA</string>
  <string name="achievement_fan">HgkIr62KmoQJEAIQHQ</string>
  <string name="achievement_leader_of_mars">HgkIr62KmoQJEAIQHg</string>
  <string name="achievement_leader_of_neptune">HgkIr62KmoQJEAIQHw</string>
  <string name="achievement_unhurried">HgkIr62KmoQJEAIQIA</string>
  <string name="achievement_final_push">HgkIr62KmoQJEAIQIQ</string>
  <string name="leaderboard_leaderboard">HgkIr62KmoQJEAIQAQ</string>
  <string name="leaderboard_pack_1">HgkIr62KmoQJEAIQCw</string>
  <string name="leaderboard_pack_2">HgkIr62KmoQJEAIQDA</string>
</resources>
, где app_id – идентификатор вашего приложения, строки вида leaderboard_xxx и achievement_xxx указывают на конкретную таблицу рекордов и достижение соответственно. Следует создать ресурсный xml-файл с именем games-ids.xml в android-проекте своей игры и поместить туда ресурсы выше.

image

Для работы GPGS нужен проект-библиотека google-play-services_lib. Его путь:
<android-sdk>\extras\google\google_play_services\libproject\google-play-services_lib
По умолчанию игровые сервисы не устанавливаются вместе с Android SDK, поэтому по этому пути ничего не будет. Чтобы это исправить нужно в Android SDK Manager установить два пакета: Google Repository и Google Play services.

image

Проект-библиотеку google-play-services_lib нужно импортировать в Eclipse. После чего установить в свойствах импортированного проекта галочку «Is library».

image

Теперь нужно связать между собой ваш android-проект и библиотеку google-play-services_lib. Для этого добавляем библиотеку в раздел Required projects on the build path. (Properties -> Java Build Path -> Projects).

image

На данном этапе можно было бы закончить настройку, но официальная документация Google настоятельно рекомендует использовать класс-помощник GameHelper. Этот класс сильно облегчает жизнь при работе с игровыми сервисами. Его установка аналогична установке google-play-services_lib (импортировать проект как библиотеку, связать проект-библиотеку с android-проектом). Скачать BaseGameUtils.

В AndroidManifest.xml добавляем два разрешения и мета-данные:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Мета-данные добавляются внутри тега application:

<meta-data
            android:name="com.google.android.gms.games.APP_ID"
            android:value="@string/app_id" />
<meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

Настройка закончена.

Код


В главной Activity реализуем два интерфейса GameHelperListener, ActionResolver:

public class MainActivity extends AndroidApplication implements GameHelperListener, ActionResolver

Интерфейс GameHelperListener выглядит следующим образом:

public interface GameHelperListener {
		/**
		 * Вызывается при неудачной попытке входа. В этом методе можно показать
		 * пользователю кнопку «Sign-in» для ручного входа
		 */
		void onSignInFailed();

		/** Вызывается при удачной попытке входа */
		void onSignInSucceeded();
	}

Интерфейс ActionResolver создаем сами. Он нужен для вызова платформо-зависимого кода. Эта техника описана в официальной wiki libgdx. Пример интерфейса:

public interface ActionResolver {
		/** Узнать статус входа пользователя */
		public boolean getSignedInGPGS();

		/** Вход */
		public void loginGPGS();

		/** Отправить результат в таблицу рекордов */
		public void submitScoreGPGS(int score);

		/**
		 * Разблокировать достижение
		 * 
		 * @param achievementId
		 *            ID достижения. Берется из файла games-ids.xml
		 */
		public void unlockAchievementGPGS(String achievementId);

		/** Показать Activity с таблицей рекордов */
		public void getLeaderboardGPGS();

		/** Показать Activity с достижениями */
		public void getAchievementsGPGS();

	}

Пример кода главной Activity:

public class MainActivity extends AndroidApplication implements
		GameHelperListener, ActionResolver {
	// помощник для работы с игровыми сервисами
	private GameHelper gameHelper;
	// класс нашей игры
	private TestGame game;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// CLIENT_ALL указывет на использование API всех клиентов
		gameHelper = new GameHelper(this, GameHelper.CLIENT_ALL);
		// выключить автоматический вход при запуске игры
		gameHelper.setConnectOnStart(false);
		gameHelper.enableDebugLog(true);
		// запретить отключение экрана без использования дополнительных
		// разрешений (меньше разрешений – больше доверие к приложению)
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		// Входной параметр this является ActionResolver. Позволяет вызывать из
		// игрового цикла платформо-зависимые методы GPGS
		game = new TestGame(this);
		AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
		initialize(game, config);
		gameHelper.setup(this);

	}

	// методы gameHelper’а: onStart(), onStop() вызываются для корректной работы
	// GPGS в жизненном цикле android-приложения

	@Override
	protected void onStart() {
		super.onStart();
		gameHelper.onStart(this);
	}

	@Override
	protected void onStop() {
		super.onStop();
		gameHelper.onStop();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();

	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		// здесь gameHelper принимает решение о подключении, переподключении или
		// отключении от игровых сервисов, в зависимости от кода результата
		// Activity
		gameHelper.onActivityResult(requestCode, resultCode, data);
	}

	@Override
	public boolean getSignedInGPGS() {
		// статус подключения
		return gameHelper.isSignedIn();
	}

	@Override
	public void loginGPGS() {
		try {
			runOnUiThread(new Runnable() {

				@Override
				public void run() {
					// инициировать вход пользователя. Может быть вызван диалог
					// входа. Выполняется в UI-потоке
					gameHelper.beginUserInitiatedSignIn();

				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@Override
	public void submitScoreGPGS(int score) {
		// отправить игровые очки в конкретную таблицу рекордов с ID
		// “HgkIr62KmoQJEAIQAQ”
		Games.Leaderboards.submitScore(gameHelper.getApiClient(),
				"HgkIr62KmoQJEAIQAQ", score);

	}

	@Override
	public void unlockAchievementGPGS(String achievementId) {
		// открыть достижение с ID achievementId
		Games.Achievements.unlock(gameHelper.getApiClient(), achievementId);

	}

	@Override
	public void getLeaderboardGPGS() {
		// вызвать Activity для всех зарегистрированных таблиц рекордов. Так же
		// можно вызывать Activity под конкретную таблицу
		startActivityForResult(
				Games.Leaderboards.getAllLeaderboardsIntent(gameHelper
						.getApiClient()), 100);

	}

	@Override
	public void getAchievementsGPGS() {
		// вызвать Activity с достижениями
		startActivityForResult(
				Games.Achievements.getAchievementsIntent(gameHelper
						.getApiClient()), 101);

	}

	@Override
	public void onSignInSucceeded() {
	}

	@Override
	public void onSignInFailed() {
	}

}

Допустим, у нас в игре предусмотрено достижение – набрать 1 млн. игровых очков, то код реализующий это будет выглядеть так:

public static void checkTotalPoints(int points) {
		ActionResolver res = getActionResolver();
		if (!res.getSignedInGPGS()) {
			return;
		}
		if (points >= 1000000) {
			res.unlockAchievementGPGS("HgkIr62KmoQJEAIQGA");
		}
		
	}

То же самое и с таблицей рекордов, только еще легче: нужно просто вызвать метод submitScoreGPGS(int score) интерфейса ActionResolver.

P. S. Для тестирования игровых сервисов нужно экспортировать apk-файл с тем же сертификатом, что и в консоли разработчика. Так же нужно добавить аккаунты тестировщиков в консоли. Изменения в игровых сервисах вступают в силу через некоторое время.

Использованные источники


developers.google.com/games/services/android/quickstart
developer.android.com/intl/ru/google/play-services/setup.html
github.com/libgdx/libgdx/wiki
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 10

    –2
    Использование google play services — это очень плохая идея, потому что приложение работает только на андроиде с гугловыми сервисами, получается намертво вендор лок для создателя приложения и неудобства для пользователей за пределами стокового андроида.
      0
      В целом соглашусь, что неудобства есть, но ведь можно сервисы Google установить и на кастомный андроид. Поэтому вендор лок только условный.
        –1
        На каких-то кастомах специально выпиливают гуглосервисы, т.к. кастомы и так не от хорошей жизни ставят :)
        +1
        Использование устройства на Android без Google Play Services — вот где истинно очень плохая идея. Все устройства от крупных производителей поставляются с Google Play Services, без них, как правило, только откровенная китайщина или какой-нибудь Nokia X. А надо ли такое поддерживать — тут уж каждый решает для себя сам.
          0
          Fire-серия у Амазона, Nook'и, сотни китайских и не очень брендов, часть майкрософтовских телефонов…

          А если мы учтём порты рантайма под хром (а так же проект по выпиливанию хрома и вытягиванию рантайма в нативный вид под разные ОС), то гугловые сервисы — лишь частный случай.
            0
            Давайте подсчитаем какую часть, в процентом соотношении, составляют девайсы с Google Play Services (на момент Google I/O 2014 было больше 1 миллиарда устройств, активных за последний месяц), а какую — Fire (телефон — 35 тысяч, планшеты — 23 миллиона), модели Nokia (16 миллионов) и китайские бренды (вот тут посчитать проблематично, но будем честными, как часто стоит задача их поддержки?). Не слишком уж большое соотношение получается, как для частного случая?

            Осознанно не включил в выборку возможность собрать приложение как расширение для хрома и возможность поставить Android на десктоп/лаптоп. Последнее — не официальный порт и юзер сам несёт ответственность за свои действия. А возможность собрать приложение как расширение для Chrome официально появилась 22 сентября, всего 7 дней назад. Рано говорить о целесообразности поддержки данной формы приложений.

            К чему я, собственно, собрал эту статистику. Во-первых, доказать что Google Play Services использовать можно и нужно, а во-вторых — отметить, что с гораздо большей вероятностью за пределами Google-enabled андроида будет непредсказуемо работать именно libgdx, а не Google Play Services. :)

            P.S. Кстати, Nook HD и Nook HD+ с середины 2013 года обладают всем пакетом Google Play Services.
        0
        А что делать если игра на C++, NDK и SDL2? :)
          0
          Сервисы Google поддерживают множество платформ: Android, iOS, Web (REST), C++
          –2
          полезная статья, спасибо!
            0
            Если б еще эти Google Play Game Services вылетали поменьше, а то после релиза игрушки с этой библиотекой Crash репорты участились раз так в 5-10, и все со ссылками на всякие NPE и java.lang.NoClassDefFoundError в диковинных тредах com.google.android.gms.*. И все бы ничего, но количество крашев на ранкинг в поисковой выдаче влияет.

            Хуже всего что они в ту же либу и admob засунули, а stand-alone admob библиотека теперь deprecated, и новые приложения с нею на play store не принимаются. Ну и весит эта либа даже после всех оптимизаций больше мегабайта, apk все жирнеет и жирнеет.

            Only users with full accounts can post comments. Log in, please.