Пользователь
0,0
рейтинг
20 июня 2014 в 17:06

Разработка → 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 проект
@Izaron
карма
4,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (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
    Как переключаться, если ты сделал два плагина с активити?

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