Доброго времени суток.
Мы с товарищем занимаемся разработкой под Android уже несколько лет. Создавали игрушки на чистом Android, OpenGL, а также Unity3d. Главной проблемой первых двух технологий является непереносимость на разные мобильные платформы. Собственно, именно поэтому мы стали использовать Unity3d.
Это довольно интересная штука. Она сочетает в себе огромные возможности программирования при помощи мышки, а также программирования на скриптовом языке, смахивающем на C#. Так как мы Java разработчики, то хотелось написать что-то кроссплатформенное на Java. Выбор пал на LibGDX. Открыв мануал и скачав пару специальных сборщиков скелета проекта на LibGDX, начали работать.
В результате большую часть времени мы потратили не на программирование, но на настройку проекта, настройку gradle, а также добавление Google Play Services. В результате, набив шишки, решили написать эту статью.
Подготовка среды разработки
Для начала вам нужно скачать JDK 1.7. В своем проекте мы использовали Intellij Idea 14+ Community Edition. Ну и для мобильной разработки Android SDK. Также следует отметить, что рекомендуется использовать SDK Android 4.4W.2 (API 20). Далее нужно скачать сборщик проекта libGDX gdx-setup.jar.
Развертывание проекта
Для того, чтобы создать проект, разработчики libGDX предоставили очень удобную утилиту, в которой можно выбрать набор библиотек, которые вы будете использовать в своем проекте. В общем, запускаем gdx-setup.jar и заполняем все поля:
После нажатия generate создастся готовый тестовый проект на libGDX. Через Intellij Idea вызываем импорт и выбираем gradle.build в нашей директории проекта. Дальше через gradle таски можно запускать Android, Desktop, а также веб-приложения. Но вот только для дальнейшей разработки этот проект он не совсем пригоден. По каким-то непонятным причинам Android модуль в Idea остался без dependences и, соответственно, писать какой-то код в этом модуле не совсем удобно. Во-первых, если что-то писать на Android, то Idea не видит зависимостей и ничего не подсказывает, а во-вторых, если компилировать не через gradle, то посыпятся ошибки отсутствия собственно этих самых зависимостей. Мы долго копались и в итоге нашли правильную последовательность действий, чтобы собрать проект как надо.
Сборка проекта
Во-первых, создаем проект, как описывалось раннее. Затем в директории проекта запускаем команду
gradlew idea
Данная команда создаст правильные файлы проекта. Затем открываем проект *.ipr и смотрим на список модулей проекта. Индикатором того, что все зависимости применены, будет служить жирное выделение каждого модуля:
В принципе, уже можно работать с проектом. Для того, чтобы запустить Android приложение, надо добавить Debug / Run Configuration Andoid application со следующими настройками:
Аналогично создаем конфигурацию для Desktop приложения:
Для старта web приложения запускаем команду
gradlew html:superDev
после чего следуем инструкциям в логе.
Всё было бы хорошо, если бы мы не захотели добавить новых модулей в gradle проект. Не хотелось руками добавлять зависимости в настройки проекта, gradle ведь должен это делать сам. В общем, с этим вопросом мы также долго возились, потому что gradle долго отказывался видеть библиотеки Google Play Services.
Установка и настройка Google Play Services
Первым делом мы стали следовать инструкции Setting Up Google Play Services, в которой сказано следующее:
Добавить в конфигурацию build.gradle модуля Android:
dependencies {
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.google.android.gms:play-services:6.5.87'
}
А также в AndroidManifest.xml следующее:
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
В результате после синхронизации Gradle мы наблюдаем вот такую ошибку:
Could not find any version that matches com.google.android.gms:play-services:6+.
Searched in the following locations:
https://repo1.maven.org/maven2/com/google/android/gms/play-services/maven-metadata.xml
https://repo1.maven.org/maven2/com/google/android/gms/play-services/
https://oss.sonatype.org/content/repositories/snapshots/com/google/android/gms/play-services/maven-metadata.xml
https://oss.sonatype.org/content/repositories/snapshots/com/google/android/gms/play-services/
https://oss.sonatype.org/content/repositories/releases/com/google/android/gms/play-services/maven-metadata.xml
https://oss.sonatype.org/content/repositories/releases/com/google/android/gms/play-services/
Получается, что данная библиотека не была найдена ни в maven репозитории, ни в других. Для того, чтобы исправить ситуацию, нужно в Android SDK Manager установить дополнительные библиотеки из пакета extras:
После установки всех библиотек проект собрался.
Далее в инструкции по установке были указаны ссылки на проект BaseGameUtils, в котором реализованы Helper классы для работы с Google Play Services. Тут всё довольно просто. Добавляем весь пакет в Android модуль, а в классе AndroidLauncher.java добавляем инициализацию GameHelper и наследуем его от интерфейса GameHelperListener.
public class AndroidLauncher extends AndroidApplication implements GameHelper.GameHelperListener {
private GameHelper gameHelper;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameHelper = new GameHelper(this, GameHelper.CLIENT_ALL);
gameHelper.setup(this);
}
...
Здесь появляется еще одна проблема. Инстанциировали GameHelper мы в модуле Android, а вся разработка на libGDX проходит в модуле Core. Соответственно, надо придумать механизм, с помощью которого можно было бы вызывать методы GameHelper в Core. Для этого был придуман интерфейс IServices, который описывает всю работу с Google Play Service вызовами.
public interface IServices {
public void signIn();
public void showAchievements();
public void showLeaderboard();
public void addScore(final String scoreName, final Integer value);
public Integer getScore(final String scoreName);
public void unlockAchievement(final String achievementID);
public boolean isAchievementUnlocked(final String achievementID);
}
Наследуем мы от этого интерфейса наш класс AndroidLauncher, реализуем все методы, а также сохраняем ссылку на него в статическом менеджере, доступ к которому есть во всем проекте.
public class AndroidLauncher extends AndroidApplication implements IServices, GameHelper.GameHelperListener {
private GameHelper gameHelper;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameHelper = new GameHelper(this, GameHelper.CLIENT_ALL);
AchievementsManager.setPlayService(this);
gameHelper.setup(this);
}
@Override
public final void signIn() {
try {
runOnUiThread(new Runnable() {
public final void run() {
gameHelper.beginUserInitiatedSignIn();
}
});
} catch (final Exception ignored) {}
}
@Override
public final void showAchievements() {
try {
runOnUiThread(new Runnable() {
public final void run() {
startActivityForResult(Games.Achievements.getAchievementsIntent(gameHelper.mGoogleApiClient), REQUEST_ACHIEVEMENTS);
}
});
} catch (final Exception ignored) {}
}
@Override
public final void showLeaderboard() {
try {
runOnUiThread(new Runnable() {
public final void run() {
startActivityForResult(Games.Leaderboards.getLeaderboardIntent(gameHelper.mGoogleApiClient, AchievementsManager.c_ScoreID), REQUEST_ACHIEVEMENTS);
}
});
} catch (final Exception ignored) {}
}
@Override
public final void addScore(final String scoreName, final Integer value) {
try {
Games.Leaderboards.submitScore(gameHelper.getApiClient(), scoreName, value);
} catch (final Exception e) {
e.printStackTrace();
}
}
@Override
public final void unlockAchievement(final String achievementID) {
try {
Games.Achievements.unlock(gameHelper.getApiClient(), achievementID);
} catch (final Exception e) {
e.printStackTrace();
}
}
@Override
protected final void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
gameHelper.onActivityResult(requestCode, resultCode, intent);
}
}
Следующий этап — это создание Google Play Services для вашего проекта в Google Developer Console. Всё, что нам нужно, это создать проект сервисов, в нем создать leaderboards и achievments, после чего добавить в константы проекта их id. Id самих сервисов нужно добавить в AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.habrahabr.test"
android:versionCode="5"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="20" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/GdxTheme" >
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="ID ваших сервисов" />
<meta-data android:name="com.google.android.gms.appstate.APP_ID" android:value="ID ваших сервисов" />
<activity
android:name="com.gesoftware.figures.android.AndroidLauncher"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Далее следует не забыть подписать проект ключом и выложить альфа релиз в Developer Console.
После этого сервисы всё равно не заработали. Причиной тому было отсутствие разрешений на API в консоле. Причем Google Play Game Management, Play Game Services и Google+ API были включены, но их было недостаточно. Пришлось добавлять еще Drive API, Drive SDK и Google Cloud Storage JSON API.
В результате мы смогли получить скелет проекта на LibGDX с встроенными в него Google Play Services. В следующей статье хотелось бы рассказать о самом проекте и способах построения архитектуры приложения с использованием библиотек libGDX
Автор: rannovr