Unity3D — написание плагинов для Android

    Предисловие


    Здравствуйте!
    Тут находятся инструкции, как расширить возможности Unity3D в работе с Android-приложениями. Другими словами, мы можем в Eclipse виде библиотеки на языке программирования Java написать нужный функционал для Android, экспортировать его в .jar и использовать в Unity3D. В основном пишут то, что отсутствует в возможностях Unity3D — такие вещи, как включение/выключение фонарика или длительная вибрация

    image

    Что нам понадобится


    • Установленные Android SDK и Eclipse (либо другая IDE на ваш выбор и усмотрение)
    • Минимальные познания в Java и C#
    • Немного терпения


    Исходники


    Unity3D проект
    Eclipse проект

    Android



    Написание библиотеки Android для Unity3D ничем не отличается от написания обычной библиотеки Android для чего бы то ни было. Только для начала работы надо взять еще библиотеку специально для этого от Unity — для этого идем в папку, где у нас она установлена (обычно C:/ProgramFiles), далее идем по такому пути Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar, и копируем библиотеку в удобное для вас место.

    Создаем проект в Eclipse: жмем «File» — «Project» — «Android Application Project» — «Next», после чего вводим название объекта. Допустим, нам надо написать проект, который может включать и выключать лампочку Android-устройства (такой функции в Unity3D изначально нет). Тогда называем проект UnityTorch. Package Name у проекта пусть будет com.izaron.habr.unitytorch. После чего несколько раз нажимаем «Next», пока не дойдем до имени нашего activity. Называем его UnityTorchActivity (от этого зависит имя главного класса), после чего жмем «Finish»

    У нас есть созданный проект. Теперь перемещаем в папку libs библиотеку classes.jar, о которой говорил выше

    Открываем UnityTorchActivity.java в папке src, он выглядит так
    package com.izaron.habr.unitytorch;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.view.Menu;
    
    public class UnityTorchActivity extends Activity {
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_unity_torch);
    	}
    
    	@Override
    	public boolean onCreateOptionsMenu(Menu menu) {
    		// Inflate the menu; this adds items to the action bar if it is present.
    		getMenuInflater().inflate(R.menu.unity_torch, menu);
    		return true;
    	}
    
    }
    


    Для того, чтобы наш плагин мог работать с Unity3D, наш activity должен наследоваться от UnityPlayerActivity
    Изменяем файл до такого состояния
    package com.izaron.habr.unitytorch;
    
    import com.unity3d.player.UnityPlayerActivity;
    
    import android.os.Bundle;
    
    public class UnityTorchActivity extends UnityPlayerActivity {
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    	}
    
    }
    


    Как мы видим, здесь ничего не происходит. Теперь нам надо написать класс Torch.java, который отвечает за фонарик и содержит функции, которые его включают/выключают. Немного гугла, и у нас готов такой класс
    Скрытый текст
    package com.izaron.habr.unitytorch;
    
    import com.unity3d.player.UnityPlayerActivity;
    
    import android.content.pm.PackageManager;
    import android.hardware.Camera;
    import android.hardware.Camera.Parameters;
    
    public class Torch {
    
    	private Camera cam;		// Объект камеры
    	public UnityPlayerActivity activity;	// Нужен для определения возможностей устройства
    	public boolean isEnabled;	// Включенный-выключенный фонарик
    	
    	public Torch(UnityPlayerActivity activity) {
    		this.activity = activity;
    		
    		isEnabled = false;
    		cam = Camera.open();	// Берем заднюю камеру у устройства
    	}
    	
    	 public boolean hasTorch() { 
    		 // Если мы не можем включить фонарик, то возвращаем false
    		 return (activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH));
    	 }
    
    	 public void turnLightOn() {
    		 
    		 if(hasTorch()) {
    			 
    			 isEnabled = true;
    		 
    			 cam = Camera.open();     
    			 
    			 Parameters p = cam.getParameters();
    			 p.setFlashMode(Parameters.FLASH_MODE_TORCH);	// Свет
    			 
    			 cam.setParameters(p);
    			 cam.startPreview();
    		 }
    	 }
    	 
    	 public void turnLightOff() {
    		 
    		 if(hasTorch()) {
    
    			 isEnabled = false;
    			 
    			 cam.stopPreview();
    			 cam.release();
    		 }
    	 }
    	 
    	 public boolean enabled() {
    		 return isEnabled;
    	 }
    }
    



    Внимание! Может возникнуть ошибка, связанная с activity.getPackageManager(). Для ее исправления в файле AndroidManifest.xml можно изменить строчку «android:minSdkVersion=»8"" на «android:minSdkVersion=»9"", и все будет ОК

    Финальная часть — делаем в главном классе экземпляр Torch
    package com.izaron.habr.unitytorch;
    
    import com.unity3d.player.UnityPlayerActivity;
    
    import android.os.Bundle;
    
    public class UnityTorchActivity extends UnityPlayerActivity {
    	
    	public Torch torch = new Torch(this);
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    	}
    
    }
    


    Unity3D


    Создаем проект в Unity3D
    Все плагины для Android обязаны храниться в папке Assets/Plugins/Android, иначе юнити «не увидит» плагин — создаем эту папку. Туда можно будет кидать AndroidManifest.xml, jar-файлы библиотек, папку res для хранения ресурсов

    Теперь возвращаемся обратно в проект в Eclipse, жмем: правой кнопкой мыши по проекту в разделе слева — «Export» — «JAR file» — «Next» — пишем путь до Assets/Plugins/Android нашего Unity-проекта — «Finish»

    Создаем прямо в Unity в той же папке AndroidManifest.xml — не прямо в редакторе, а в проводнике, либо в IDE (Visual Studio, MonoDevelop)

    Пишем в этом файле

    <?xml version="1.0" encoding="utf-8"?>
    
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.izaron.habr.unitytorch"
          android:versionCode="1"
          android:versionName="1.0">
      <uses-sdk android:minSdkVersion="9" />
      <application android:label="@string/app_name"
                   android:icon="@drawable/app_icon">
        <activity android:name="com.izaron.habr.unitytorch.UnityTorchActivity"
                  android:configChanges="keyboardHidden|orientation"
                  android:label="@string/app_name">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
      </application>
    
      <uses-permission android:name="android.permission.CAMERA"/>
      <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
      <uses-feature android:name="android.hardware.camera" />
      <uses-feature android:name="android.hardware.camera.autofocus" />
      
    </manifest>
    

    То, что надо будет изменять вам — это название package, android:name у activity и uses-permisson'ы

    Манифест из Eclipse-проекта не копируем, он нам не нужен

    Теперь надо создать небольшую «обертку» над библиотекой — создаем два файла: TorchActivity.cs и Torch.cs

    TorchActivity.cs (не забываем про package name)
    using UnityEngine;
    using System.Collections;
    
    public static class TorchActivity
    {
    #if UNITY_ANDROID && !UNITY_EDITOR
        public static AndroidJavaClass activityClass = new AndroidJavaClass("com.izaron.habr.unitytorch.UnityTorchActivity");
        public static AndroidJavaClass unityActivityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        public static AndroidJavaObject activityObj = unityActivityClass.GetStatic<AndroidJavaObject>("currentActivity");
    #else
        public static AndroidJavaClass activityClass;
        public static AndroidJavaClass unityActivityClass;
        public static AndroidJavaObject activityObj;
    #endif
    }
    


    Torch.cs
    using UnityEngine;
    using System.Collections;
    
    public static class Torch
    {
    #if UNITY_ANDROID && !UNITY_EDITOR
        private static AndroidJavaObject torchObj = TorchActivity.activityObj.Get<AndroidJavaObject>("torch");
    #else
        private static AndroidJavaObject torchObj;
    #endif
    
        public static bool HasTorch()
        {
            if (Application.platform == RuntimePlatform.Android)
                return torchObj.Call<bool>("hasTorch");
            else
                return false;
        }
    
        public static bool Enabled()
        {
            if (Application.platform == RuntimePlatform.Android)
                return torchObj.Call<bool>("enabled");
            else
                return false;
        }
    
        public static void TurnLightOn()
        {
            if (Application.platform == RuntimePlatform.Android)
                torchObj.Call("turnLightOn");
        }
    
        public static void TurnLightOff()
        {
            if (Application.platform == RuntimePlatform.Android)
                torchObj.Call("turnLightOff");
        }
    }
    


    Зачем нам два поля activityClass и activityObj? Через activityObj мы можем вызывать методы UnityTorchActivity, а статические функции можно вызывать только через activityClass. Такой небольшой (и подлый для новичков) нюанс
    В остальном все довольно прозрачно — запуск функций вне Android платформы ни к чему хорошему не приводит

    Выводы — написание плагинов под Android для Unity3D является не особо широкой практикой, из-за чего статей про это направление почти нет. Надеюсь, что привнес ясность в этот вопрос. Спасибо за внимание!

    Исходники


    Unity3D проект
    Eclipse проект
    Метки:
    • +18
    • 30,9k
    • 8
    Поделиться публикацией
    Похожие публикации
    Комментарии 8
    • 0
      Спасибо за статью
      Еще бы исходники выложить на на github.com или bitbucket.com
      то было бы просто ОгонЪ!
      • 0
        Это все хорошо. Но что, если другому плагину также необходимо перегрузить UnityPlayerActivity? Как их заставить работать одновременно?
        • 0
          Решение здесь в третьем сообщении. По всем правилам разработки на андроиде мы запускаем через Intent новый Activity через главный Activity, которой только один на все приложение
        • 0
          Как раз недавно пытался разобраться с плагинами под Android. В официальной документации Unity почти ничего не нашёл.
          Большое спасибо за статью.
          • 0
            Когда я изучал манифест плагинов с просторов Интернета, там было это:
                <application
                    android:icon="@drawable/app_icon"
                    android:label="@string/app_name"
                    android:debuggable="true">
                    <activity android:name="com.unity3d.player.UnityPlayerProxyActivity"
                              android:label="@string/app_name">
                        <intent-filter>
                            <action android:name="android.intent.action.MAIN" />
                            <category android:name="android.intent.category.LAUNCHER" />
                        </intent-filter>
                    </activity>
                    <activity android:name="com.unity3d.player.UnityPlayerActivity"
                              android:label="@string/app_name">
                    </activity>
                    <activity android:name="com.unity3d.player.UnityPlayerNativeActivity"
                              android:label="@string/app_name">
                        <meta-data android:name="android.app.lib_name" android:value="unity" />
                        <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
                    </activity>
                    <activity android:name="com.unity3d.player.VideoPlayer"
                              android:label="@string/app_name">
                    </activity>
                    <activity android:name="com.google.ads.AdActivity" />
                </application>
            

            Что это за 4 непонятных активити из com.unity3d.player.*?
            • 0
              Я так понимаю, что это стандартные файлы, которые нужны для нормальной работы Unity3d. Они по идее есть в classes.jar и когда мы билдим в eclipse нашу библиотеку, то там эти UnityPlayerProxyActivity, UnityPlayerActivity, UnityPlayerNativeActivity будут и они подтянутся в наш юнити-проект

              Конечно, я тут сам не совсем понимаю тоже. Хотелось бы чтоб кто-то пояснил детальней.
            • 0
              А если в манифесте уже объявлено
                    <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                    </intent-filter>
              

              у другого активити, как быть? как загрузить этот из плагина, чтобы работал?
              • 0
                Как переключаться, если ты сделал два плагина с активити?

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.