vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

【Unity5】【Android】プラグインから画像のパスを投げてMaterialにセットする

前回の続きです。

やったこと

  • ギャラリーで選択して取得した画像のパスをUnity側に渡す
  • 取得したパスから画像をロードして、CubeのMaterialとしてセットする

NativeプラグインのデータをUnityに渡す

ざっと調べてみたところでは、Nativeプラグインで持っているデータをUnity側に渡すには2つの方法がありました。

  1. 「UnityPlayer.UnitySendMessage」を使ってプラグインからUnity側のメソッドを呼び出す
  2. Unity側でAndroidJavaObjectを使って該当データを取得する

今回はプラグイン側での処理が終了したタイミング(ギャラリーで画像を選択して、Intentが閉じたタイミング)でString型のデータを渡したいので、1の方法を使うことにしました。

呼び出し側

PluginConnector.java

〜省略〜
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if(resultCode != RESULT_OK)
  {
    return;
  }
  switch (requestCode)
  {
  case REQUEST_GALLERY_JELLYBEAN_BELOW:
    // 選択した画像のパスを取得する.
    String[] strColumns = {MediaStore.Images.Media.DATA };
    Cursor crsCursor = getContentResolver().query(data.getData(), strColumns, null, null, null);
    if(crsCursor.moveToFirst())
    {
      this.sendImagePath(crsCursor.getString(0));
    }
    crsCursor.close();
    break;
  case REQUEST_GALLERY_KITKAT_ABOVE:
    this.GetSelectedItemPath(data);
    break;
  }
}
@TargetApi(SDKVER_KITKAT)
private void GetSelectedItemPath(Intent data)
{
  // 選択した画像のパスを取得する.
  String strDocId = DocumentsContract.getDocumentId(data.getData());
  String[] strSplittedDocId = strDocId.split(":");
  String strId = strSplittedDocId[strSplittedDocId.length - 1];

  Cursor crsCursor = getContentResolver().query(
      MediaStore.Images.Media.EXTERNAL_CONTENT_URI
      , new String[]{MediaStore.MediaColumns.DATA}
      , "_id=?"
      , new String[]{strId}
      , null);

  if (crsCursor.moveToFirst()) {
    this.sendImagePath(crsCursor.getString(0));
  }
  crsCursor.close();
}
private void sendImagePath(String strSendPath)
{
  // 取得した画像のパスをUnity側に送信する.
  UnityPlayer.UnitySendMessage("CtrlPlugins", "OnCallback", strSendPath);
}
〜省略〜
  • 「UnityPlayer.UnitySendMessage」の引数は 1.呼び出し対象のGameObject名、2.呼び出し対象のメソッド名、3.渡すデータ(画像パス)です。
  • 1は呼び出すスクリプトファイル名ではなく、Scene内にあるGameObjectの名前のため、注意が必要です。
  • 1や2が間違っている場合でも特にエラーは発生しないため、こちらも注意が必要です。

呼び出される側

CtrlPlugin.cs

using UnityEngine;
using System.Collections;

public class CtrlPlugins : MonoBehaviour
{
    readonly string PLUGIN_CLASS_PATH = "jp.plugincontroller.PluginConnector";

    public GameObject _gmoSetTexture;
    CtrlSetTexture _ctrSetTexture;

    void Start()
    {
        _ctrSetTexture = _gmoSetTexture.GetComponent();
    }
〜省略〜
    public void OnCallback(string strGotData)
    {
        // 画像が選択されたら、取得したパスを元に画像をロードする.
        _ctrSetTexture.SetNewTexture(strGotData);
    }
}
  • 画像パスを受け取った後の処理は、新規で追加した「CtrlSetTexture.cs」で行うため、「OnCallback」が呼ばれたらそのまま「CtrlSetTexture」にデータを渡しています。

パスから画像をロードしてTexture2Dにセットする

準備

  1. 3DオブジェクトのMaterialをコントロールするための「CtrlSetTexture.cs」を作成します。
  2. 画像をセットするための「mtrCube.mat(Material)」を作成して、Shaderを「Unlit/Texture」に変更します。
  3. Sceneに任意の3Dオブジェクト(今回はCubeにしました)を作成し、2のMaterialをアサインします。
  4. 3の3Dオブジェクトまたは空のオブジェクトを追加して、1をアサインします。

画像をロードしてMaterialとしてセットする

CtrlSetTexture.cs

using UnityEngine;
using System.Collections;
using System.IO;

public class CtrlSetTexture : MonoBehaviour
{
    public Material _mtrCube;
    public void SetNewTexture(string strPath)
    {
        if (File.Exists(strPath))
        {
            // 取得したパスから画像を読み込んでマテリアルとして設定する.
            _mtrCube.mainTexture = ReadTexture(strPath, 1920, 1080);
        }
    }
    Texture2D ReadTexture(string strPath, int intWidth, int intHeight)
    {
    // バイト配列でファイルを読み込み、Texture2Dとしてセットする.
    byte[] bytReadBinary = File.ReadAllBytes(strPath);
    Texture2D txtNewImage = new Texture2D(intWidth, intHeight);
        txtNewImage.LoadImage(bytReadBinary);
    return txtNewImage;
    }
}
  • 準備の2のMaterialを「public Material _mtrCube」としてアサインしておき、取得したパスから画像をロード・セットすれば準備3の3Dオブジェクトに反映されます。
  • ReadTextureで作成するTexture2DのサイズをフルHDとしていますが、実際にはオブジェクトのサイズなどに合わせて調整した方が良いかと思います。

終わりに

エラー処理などかなり不足しているところはあるものの、とりあえずようやく端末内の画像を3Dオブジェクトにセットできるようになりました。
次は今不足している処理を追加していくのと、スクリーンショットについて挑戦してみようかな、と思います(保存した画像が即時反映されないというような話を目にしたので…)。

参考

プラグインからUnityスクリプトの呼び出し

ファイルのパスから画像を読み込む