Lancer une app Unity depuis android avec gestion des paramètres

Voulant intégrer mon jeu sous Unity dans un application Android, j’ai trouvé assez peu de source fiable pour m’aider à le faire. Après être tombé dans la plus part des pièges je vous propose de les mettre en avant et d’intégrer votre jeu.

Le but de cet article est d’expliquer comment créer un projet dans Android Studio, d’y inclure un projet Unity. On pourra appeler le projet en passant des paramètres. Puis on verra comment renvoyer des paramètres depuis Unity vers Android.

Création du projet Unity

L’objectif est de transformer le projet Unity en une librairie, qu’on pourra intégrer aisément dans un projet Android.

Déjà dans un premier temps, créer un projet le plus simple possible. On aura besoin de 2 scripts pour le rendre compatible avec un téléphone Android, le 2nd permettra de récupérer et de gérer le paramètre envoyé depuis Android.

 

Pour être compatible avec un téléphone Android, il faut gérer le cas du bouton retour arrière ou retour à l’activité précédente.

using UnityEngine;

public class SupportBackScript : MonoBehaviour {
	void FixedUpdate(){
		if (Application.platform == RuntimePlatform.Android)
		{
			// Le boutton échappe est le bouton quitter sous Android
			if (Input.GetKey(KeyCode.Escape))
			{
				// On quitte l'application unity, attention à ne pas être en Debug quand vous lancez l'application Android, sinon elle se fermera en même temps que Unity
				Application.Quit();
			}
		}
	}
}

Vous pouvez ajouter ce script à votre scène au niveau que vous souhaitez. En général je vais l’associer à un élément qui ne bouge pas, ou à un GameObject Empty spécifiquement pour gérer ce genre de cas.

Le second script va nous permettre de lire le paramètre qu’Android nous aura passé. Pour ce faire, on va utiliser les classes Java et récupérer le paramètre.

using UnityEngine;
using UnityEngine.UI;
using System;

public class Main : MonoBehaviour {    
	public Text LogText;

	void Start ()
	{
		// On réinitialise le composant ui text passé en paramètre au cas où il y aurait déjà quelque chose
		LogText.text = string.Empty;

		// On récupère les données uniquement si on est sous android 
		if (Application.platform == RuntimePlatform.Android) {
			try {
				string arguments = "";

				// On vient récupérer la classe android qui fait tourner l'application
				AndroidJavaClass UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); 
				// On récupère la scène courante
				AndroidJavaObject currentActivity = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

				// L'Intent ici est un container ayant les différents paramètres d'entrées de l'application
				AndroidJavaObject intent = currentActivity.Call<AndroidJavaObject>("getIntent");

				// On vient tester ici si dans notre Intent, on a bien un Extra (un paramètre) dont la clef est "ParamCallKey"
				bool hasExtra = intent.Call<bool> ("hasExtra", "ParamCallKey");

				// On a bien un paramètre
				if (hasExtra) {
					// On récupère le pramètre
					AndroidJavaObject extras = intent.Call<AndroidJavaObject> ("getExtras");
					// Et la valeur qu'on cast en string
					arguments = extras.Call<string> ("getString", "ParamCallKey");

					// Enfin on affiche le résultat dans le Text passé en paramètre
					LogText.text = "Result: " + arguments;
				}

			} catch (Exception e) {
				LogText.text = "Error: " + e.Message;
			}
		}
	}
}

Exporter le projet

Votre projet Unity est prêt ! Il nous reste à l’exporter. A ce jour il y a un problème lors de l’export sous Windows en version 2017.1.0f3, pour le résoudre, il suffit de générer un APK.

Ce n’est pas utile pour ce build si vous avez le problème mais pour la suite je vous conseils de changer la compression des textures. Le build Système est à changer aussi pour la suite. Gradle est un outils de génération qui va nous permettre de générer sous la forme d’une librairie. Pour que l’export fonctionne, il va nous falloir encore 2 étapes.

Spécifier le nom du package pour la génération du code java à votre guise.

Ainsi que signer l’application étant un build en mode Release et non Debug

 

Voilà on est prêt à cocher dans le build le mode « Export Project » et enfin le buton « Export« .

Unity va vous demander un dossier ou exporter les éléments, il va créer un dossier du nom du projet dans le dossier sélectionné. Donc évitez de sélectionner le dossier parent de votre projet, les deux projets risqueraient de fusionner.

Générer la librairie

Avec Android Studio, ouvrez le projet qui a été généré. Il est fort possible qu’une popup vous demande si le projet doit être transformé en projet Gradle, acceptez.

Si c’est la première fois que vous lancer Android Studio, il est possible qu’il cherche à télécharger tout les SDK utlie pour votre application, cela peut prendre du temps selon votre connexion et votre disque dur.

Actuellement ce n’est qu’un projet, on va devoir changer quelques éléments pour le transformer en librairie.

 

Le premier élément à changer est le manifest.xml lié à votre appliction, il va falloir commenter ou supprimer les lignes comme suit :

<!--<intent-filter>-->
  <!--<action android:name="android.intent.action.MAIN" />-->
  <!--<category android:name="android.intent.category.LAUNCHER"/>-->
  <!--<category android:name="android.intent.category.LEANBACK_LAUNCHER" />-->
<!--</intent-filter>-->

Ensuite vous devez ouvrir le fichier Gradle de l’application et changer :

apply plugin: 'com.android.application'
En
apply plugin: ‘com.android.library

Ainsi que supprimer l’applicationId.

 

Une fois effectué, vous n’avez plus qu’à resynchroniser votre Gradle et vous devriez avoir votre fichier AAR (pour Android Archive, à l’instar des fichiers JAR) dans le dossier DOSSIER_PROJET\build\outputs\aar

 

Intégrer dans votre application Android

Je suppose que vous avez déjà ou que vous savez faire un projet Android. Pour intégrer la librairie fraîchement créée, il va nous falloir créer un nouveau module à partir du fichier AAR.

A noter cependant, que j’ai eut des problèmes si mon package avait un nom différent de mon application Unity. Si vous êtes dans le même cas que moi, le package de unity est com.example.androidcaller.unity et le package d’Android est com.example.androidcaller. Je n’ai plus de problème depuis que j’utilise ce sous package.

Vérifiez qu’il y a bien un fichier settings.gradle du projet

include ':app', ':NomDuModule'

Pour intégrer le module au reste de l’application

dependencies {
    compile project(":NomDuModule")
    compile fileTree(dir: 'libs', include: ['*.jar'])
    ...
}

A ce moment là vous pouvez resyncrhoniser gradle.

 

S’il y a des problèmes, ce sera sans doute un Manifest merger failed. Le problème est lorsqu’on vient écraser le manifest de la librairie dans celle de l’application, comme l’icone ou le thème qui peut poser problème. Il suffit d’ajouter les lignes en gras, pour que votre librairie prenne le dessus.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
    package="com.yourpackage.name">
<application
        android:allowBackup="true"
        tools:replace="android:icon,android:theme"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        ...
    </application>
</manifest>

Appeler Unity

Enfin la partie concrète ! Si vous savez appeler des activités Android, ce n’est pas plus compliqué, il suffit d’appeler votre activité unity qui se nomme UnityPlayerActivity dans votre librairie.

private void openUnity(String parameter) {
	Intent intent = new Intent(this, com.example.androidcaller.unity.UnityPlayerActivity.class);
	intent.putExtra("ParamCallKey", parameter);

	startActivity(intent);
}

Attention cependant, en debug mode, si vous revenez en arrière, il y a de forte chance que l’application se ferme. Je vous conseil de lancer l’application en mode Release, le retour en arrière depuis Unity se passe bien à ce moment.

 

Le retour

Cette partie est rarement utile, mais j’ai voulu pousser un peu.

 

Côte java il faut privilégier la fonction startActivityForResult(string, int) à startActivity(string).  Au retour d’Unity on pourra donc utiliser une méthode onActivityResult(int, int, Intent) pour récupérer le retour s’il y en a un.

private void openUnity(String parameter) {
	// On prépare un container pour appeler notre application unity
	Intent intent = new Intent(this, com.example.androidcaller.unity.UnityPlayerActivity.class);
	// On ajoute le paramètre avec la clef "ParamCallKey" à notre container
	intent.putExtra("ParamCallKey", parameter);
	// On lance l'application en spécifiant qu'on veut un résultat, et on passe un id d'application pour la différencier au retour
	startActivityForResult(intent, 12345);
}

/* Called when the second activity's finished
requestCode =&gt; The code used to call the application
resultCode =&gt; Application result 
data =&gt; Intent from the application, with the extras if there is parameters, could be NULL when resultCode = RESULT_CANCEL
*/
protected void onActivityResult(int requestCode, int resultCode, Intent data) {	

	switch(requestCode) {
		// Dans le cas de l'id de notre application Unity
		case 12345:
			// Dans le cas où l'application a réussi
			if (resultCode == RESULT_OK) {
				// On récupère les paramètres
				Bundle res = data.getExtras();
				// On récupère la valeur
				String result = res.getString("ParamReturnKey");
				// On log pour pouvoir le récupérer
				Log.d("ACTIVITY_RESULT", "result: "+result);
			}
			else {
				Log.d("ACTIVITY_RESULT", "Cancel, no result");
			}
			break;
		default:
			Log.d("ACTIVITY_RESULT", "Unknown activity");
	}
}

 

Côté C#, je vous propose un exemple très simple, mais n’impactant en rien de coder dans l’export Java.

using System;
using UnityEngine;

public class SupportBackScript : MonoBehaviour {
	void FixedUpdate(){
		if (Application.platform == RuntimePlatform.Android)
		{
			if (Input.GetKey(KeyCode.Escape))
			{
				try
				{
					AndroidJavaClass player = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
					AndroidJavaObject activity = player.GetStatic&lt;AndroidJavaObject&gt;("currentActivity");

					// On créer un container
					var intent = new AndroidJavaObject("android.content.Intent");
					// Au lieu de récupérer, on va utiliser la méthode "putExtra" de l'intent pour y injecter un paramètre clef "ParamReturnKey" avec comme valeur "NiceParameter"
					intent.Call&lt;AndroidJavaObject&gt;("putExtra", "ParamReturnKey", "NiceParameter");
					// On injecte notre Intent en tant que retour de notre application
					// -1 == RESULT_OK
					activity.Call("setResult", -1, intent);
				}
				catch (Exception ex)
				{
					Debug.Log(ex.ToString());
				}

				// Enfin on quitte l'application, attention il ne faut pas avoir lancé l'application android en Debug, dans ce cas, l'application android sera quité en même temps que l'application unity
				Application.Quit();
			}
		}
	}
}

 

Attention cependant, vous allez devoir reexporter votre projet Unity. En exportant, vous devrez refaire les actions pour transformer l’application en librairie. Mais une fois fait vous pouvez simplement remplacer le AAR dans le dossier du projet Android rien de plus !

 

Pour aller plus loin, si vous n’avez pas d’idée je vous conseils de jeter un coups d’œil aux deux liste de mini jeu que vous pourriez intégrer à vos applications android. Personnellement je ne les ais pas acheté, mais ça permet d’avoir quelques idées.

 

Si vous voulez allez plus loin avec Android, je vous conseils ce plugin qui vous permet d’accéder à tout les outils disponibles sur le téléphone. Notification, fichier, caméra, … le plugin permet même d’utiliser les service de google play.

4 commentaires

    1. Merci, c’est justement parce que j’ai galéré sur les tutos que j’ai pu trouver sur le sujet que j’ai résumé le tout ici.

  1. Bonjour,

    Après avoir suivis votre tuto, je bloque sur la partie ou l’on appelle unity via l’Intent. Le ‘UnityPlayerActivity » de « com.example.androidcaller.unity.UnityPlayerActivity.class » n’étant pas trouvé.
    (les noms des packages sont les mêmes)

    Auriez vous une solution ?

    1. Le problème vient du fait que la librairie AAR n’est pas chargé. La cause peut être à plusieurs niveaux

      1) Vérifier la taille de ce fichier « .aar », s’il ne fait que quelques mo seulement (ou moins) le problème vient en amont, puisqu’il est sensé contenir le moteur d’unity. (une 20ène de mo environ)

      1a)Si ce n’est pas le cas vérifiez bien que vous avez cocher l' »export project » lors de l’export depuis Unity

      1b) Sinon c’est peut être parce que vous n’avez pas générer réellement générer une librairie « apply plugin: ‘com.android.library’ » dans le graddle

      2) J’ai eut plusieurs problèmes à cause des namespaces mais si vous avez suivi les mêmes que moi il ne devrait pas arriver, il y a un paragraphe ou j’explique qu’il faut impérativement que votre « package name » dans Unity, soit un enfant de celui de votre projet Java
      Dans mon cas le projet java => « com.example.androidcaller »
      Le projet android => « com.example.androidcaller.unity »

      3) Vous avez une erreur lors de la compilation de graddle, si c’est cela, il y a un problème avec le librairie ou plus vraisemblablement avec la configuration à charger. Peut être un mauvais nommage, ou le manifest qui bloque. Si ce que j’ai noté n’explique pas ce cas, c’est que je ne l’ai pas rencontré. Mais n’hésitez pas à copier les logs qu’on puisse essayer de comprendre.

      Si ce n’est aucun de ces trois problèmes, est-ce que votre IDE reconnait au moins le package de la librairie ? ( « com.example.androidcaller.unity » ) Via autocompletion ?

      Si ce n’est pas le cas, soit la librairie n’est jamais chargé, soit elle ne charge rien. Le second cas revenant au point 1)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *