カテゴリー: Android

うにのトゲは刺さると痛い(´・ω・`)-40 : Javaプラグインの利用 (Java側)

きっと他のことをやって全てを忘れてしまう未来の俺氏へのUnityで自作Javaプラグインを利用する時のJava側手順。

何故か他の人のコードを実行しようとすると上手く行かなかったので、Unity側から文字列を渡して、それにJava側が文字列を付加して返すという簡単なものにした(´・ω・`)

面倒だったのでAndroid10決め打ちにした。
日本語化が上手く行ってないのか、メニュー等がなんか中途半端な状態になっているw

参考サイトというか目を通したり試したエントリはたくさんあるのだが、ブックマークに残っていたのは下の3つくらいだった(ノ∀`)マタヤラカシタ
Unityのプラグインを作るAndroid/iOS
[Android]Unityへ組み込むAndroidライブラリビルド手順解説
突然仕事でUnity向けのSDKやPluginを作ることになった人向け資料


他の人のエントリを色々読み、理解せずにやっていたから気が付かなかったが、手順8~11のUnityのClass.jarのコピーにまつわる作業は、Java側でUnityPlayerActivity等をいじったりしない場合は必要ない気がする…( ・´ω・`)トバシテイイ


  1. Android Studioを起動し、[Create New Project]をクリック。
  2. [No Activity]を選択し<次へ>をクリック。
    ※目的はモジュール作成の為、実際にはActivityは何を選んでも問題はない筈。

  3. 任意のプロジェクト名を指定し、[Minimum SDK]で"API 29 Android 10.0"を選択し、<終了>をクリック。
    ※うちの実機はAndroid10なのでこの選択をしているが、必ずしも10でなければならないわけではない。但し、他のバージョンでは動作確認をしていない。
  4. プロジェクトの作成が終わったら、[ファイル]-[New]-[New Module]の順に選択。
  5. [Android Library]を選択し、<次へ>をクリック。
  6. [Module Name]に任意の名前を指定、[Minimum SDK]で"API 29 Android 10.0"を選択し、<終了>をクリック。
    ※この例では[Module Name]を"UniAndLib"に指定。
  7. <プロジェクト>プルダウンをクリックし、"プロジェクト"をクリック。
  8. 使用する予定のバージョンのUnityのフォルダで"Class.jar"をコピー。
    この例ではUnity Hubを使用し、バージョン 2020.2.4f1 を使用。かつmonoではなくil2cppでビルド。
    "C:\Program Files\Unity\Hub\Editor\2020.2.4f1\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes"

    カスタムインストール時は当然のことながら、Class.jarの在り処は変わってくる。

  9. Android Studioのプロジェクトの新しく追加したモジュール配下のlibsフォルダに貼り付け。
  10. ダイアログが表示されたら、<リファクタリング>をクリック。
  11. 新しく追加したモジュール配下のbuild.gradleをダブルクリック。
  12. build.gradleが表示されたら、dependenciesの箇所に
    compileOnly fileTree(dir: 'libs', include: ['*.jar'])
    compile 'androidx.appcompat:appcompat:1.1.0'
    

    を追加。

    これを追加しておかないと、"Execution failed for task ':launcher:checkDebugDuplicateClasses'"とかいうエラーが出る筈。Android Studioで作ったライブラリ側とUnity側にUnityのClass.jarが存在しちゃって、その辺がいけないらしい…

    ※compileのところに打ち消し線が出るのでDeprecatedかなにかなのかもしれないがキニシナイ( ゚ 3 ゚)
    もしかしたらそもそも必要ないのかな? よくよく見ると下の行で"appcompat:1.2.0"をimplementationしてるから要らないか?

  13. 新しく追加したモジュール配下の[src]-[main]--[パッケージ名]のフォルダを右クリックし、コンテキストメニューが表示されたら、[新規]-[クラス]の順に選択。

  14. 「New Java Class」ダイアログが表示されたら、クラス名を指定し、"Class"をクリック。
    ※この例ではクラス名に"ReturnString"を指定。
  15. 作成したクラスがエディタに表示されたら、以下の内容を入力して保存。
    package com.example.uniandlib;
    
    public class ReturnString {
        public String returnResult(String fromUnity) {
            return "Via Java Lib:\n" + fromUnity;
        }
    }
    

    (パッケージ名やクラス名を任意の値を指定している場合は適宜書き換える。)

  16. メニューで[ビルド]-[Rebuild Project]の順に選択。
  17. ビルドが終了するまで待つ。
    (ウィンドウ下のステータスバーで以下のようなの表示がされている間はビルド実行中。)

以上がUnityで使用できるJavaプラグインの作成方法。

画像を入れたら長くなりすぎたので、Unity側は次のエントリで…(ヽ'ω`)


CommandInvokationFailure: Gradle build failed.

このエラーが出た場合は、一回エクスポートして、Android Studio側でインポートしてGradleをアップグレードしないといけないとかどうとか。

参考:
Gradle build failedが出る(Unity2019から)

久し振りにAndroid SDKマネージャーを起動して更新してみたら…(´・ω・`)

大きく分けて二つほどエラーが出るようになった(´・ω・`)

一つは"'d:skin'で始まる無効なコンテンツが見つかりました。ここでは子要素を使用できません。"みたいなエラメッセージのDialogが出るやつ。

これは

  • Android Wear ARM EABI v7a System Image
  • Android Wear Intel x86 Atom System Image

をSDKマネージャーでdeleteしてしまえば良いらしい。

'd:skin'で始まる無効なコンテンツが見つかりました。ここでは子要素を使用できません。
android - Error Loading the sdk when eclipse is started

ちなみにこの回答はAPI22の時の話だが、API23に関しても同じことをしないと駄目だった。
うちの環境ではAndroid TVかなんかでもエラーが出てたので、それらも削除したような…


もう一つはGoogle Play Serviceのライブラリ絡みで、“No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version')”とかいうエラーが出るやつ。

Eclipse 用の Google Play Services Library のありか
android - Missing "/extras/google/google_play_services/libproject" folder after update to revision 30
などを眺めていると、「Looks like Google just broke down Google Play Services into multiple libraries. (GoogleはGoogle Play Servicesのライブラリを複数のライブラリに分解したみたい)」みたいなことが書かれていたので、そういうことなんだろうか。

まあ、上のリンクの方の結論である、https://dl-ssl.google.com/android/repository/google_play_services_8298000_r28.zipを落として来て解凍し、"libproject"フォルダを\extras\google\google_play_servicesにペーストして、eclipse上でインポートし直したら、エラーが消えた。

これが本当に正しい対処方法なのかはよくわからないw
他にもaarを解凍して必要なライブラリを取ってくる?とかUnity用のパッケージをインポートしてなんか色々やると一応は解決するらしい。admobだかなんかのライブラリか?


原因はよくわからないが、取り敢えず解決したから良しとする(・∀・)

Animatorの無限ループってどうやってやんのよ(´・ω・`)?

本来のAnimatorの使い方をしていないので、アニメのループをどうすべきかわからない。
setDuration()でとんでもなく大きい数値を指定するのもなぁ(´・ω・`) ダサイ
その場合でも停止状態で時間が過ぎてしまえば、いずれはアニメーションは停止してしまうし。

Animator.AnimatorListenerとか言うのがあって、その中にonAnimationEnd()みたいなのが
あったのでぐぐる。

ヒットしたページを見てみたがよくわからない(´・ω・`)
つーかこれJavaScript用?
AndroidでAnimatorSetをRepeatするときの注意点

試しに何となくでコーディングしてみたが上手く行かない(´・ω・`)
しょうがないので他の方法を探る。


setDuration()で引数に-1を設定したらどうだろうかとソースを見たら、

if (duration < 0) {
            throw new IllegalArgumentException("Animators cannot have negative duration: " +
                    duration);
        }

ということらしいので駄目(´・ω・`)

更にぐぐってINFINITEなんてのを見かける(・∀・) オ?
Techniques.RollOut

この回答に評価が全くなかったので、駄目なんじゃろうかと更にぐぐる。
INFINITE

example3辺りにObjectAnimator.INFINITEとあったのでValueAnimator.INFINITEでもいいんかなと
setRepeatCount()の引数に指定してみると、上手く行った模様。

まあ定数の中身としてはAnimation.INFINITEと一緒なのかな?


取り敢えずアニメの無限ループはするようになったけれど、再生停止中のアニメーションを
停止させるべきか否か。androidの設計思想というかプログラムの思想としては余計な処理は
止めておけよってことだから、停止させるべきなのかな(´・ω・`) メドイナ

Vertical SeekbarでthumbをsetThumb()を使ってアニメーションをさせようとすると上手く行かない件(´・ω・`)

ようやく重い腰を上げてアニメーションthumb付きのSeekbarをプログラムに組み込む。
特に問題なく動いた(・∀・)

Vertical Seekbarにもアニメーションthumbのコードを追加した。
特に問題なく動いた(・∀・)  …かのように見えた。


次のステップに進もうして眺めているうちに、Vertical Seekbarでのアニメーションが
おかしいことに気づく。アニメーションはしているが、アニメーション中はthumbが移動しないのだ(´・ω・`)

今実装しているアニメーションは決め打ちのアニメパターン(6枚)をsetThumb()で差し替えるものであったが、
追加時に面倒くさくて4枚しかdrawableフォルダにコピーしてなかったので発覚したのであった(ノ∀`)
この状態だとアニメパターンはループせずに4枚目を表示した後に停止し、setThumb()を呼び出さず、
thumbの移動が開始する。試しに残りの2枚も追加するとVertical Seekbarではthumbが全く移動せずに
ひたすらアニメパターンが繰り返し描画された。

おおまかな原因を突き止めている現在にこういう風に書くと、誰が見てもおかしいのはsetThumb()にあるのは
すぐにわかるが、それを突き止める為に数日間を無駄にした…(ヽ'ω`)

まあ専従でバグを突き止めようとしていたのではなく、dmm見放題ライトのavを流しつつ、kindle等で無料
配信されていた漫画を読んだり、テレビを横目に見つつだったり、PS2のゲームをやりつつ、ちょろっと
思いついたことを試したりしていたので余計に時間がかかったのかもしれない(・∀・)


一番最初に疑ったのはVertical Seekbarのコード。これは覚え書き 主にレイアウトの背景色の時に
参考にしたものだが、完全には理解してない(ノ∀`)

というわけで、このコードの部分を変更したり削ったりしてみたが、状況は変わらず。

次に疑ったのはアニメーション用に追加したValueAnimatorだったが、これはよくよく考えてみたら
横方向のCustomSeekbarにも同じように追加しているので犯人でないw

ますますもってわからなくなり、最後にsetThumb()をコメントアウトし、その状態で実行すると
問題なくthumbが動くことから(ValueAnimator追加前に戻したとも言える)、setThumb()を疑うことに。


なんかバグじゃろかとぐぐったりしたが、特にヒットすることもなく。
悩んだ挙句に親クラスを追っていった。

すぐ上のSeekbarはsetThumb()を持ってない。2つ上のprogress barはthumbを持たないはず。
ということはAbsSeekbarか( ・´ω・`)!!

でまぁ、中を適当に見たがよくわからない。しばらくこれとVertical Seekbarの挙動を眺めた末に
setThumbPos()に対して渡す引数がgetWidth()とか横向きのSeekbarしか想定されていないのが
原因なんじゃないかなぁと推測した。setThumb()する度にここで横向きの位置にセットされてしまい、
縦方向は移動せず、その幅から横方向にも移動せず、その場に止まっているように見えるのでは
なかろうかと。

で、じゃあsetThumbPos()をオーバーライド出来ないかと思ったけど無理(´・ω・`)
setRotation()を使う形にしてdrawThumb()をオーバーライドしてどうにか出来ないかと思ったが無理(´・ω・`)
とにもかくにもAbsSeekbarを触るのは駄目らしい。


ここで考えられる対応策は

  1. Progressbarを継承してAbsSeekbarの実装を移して、更にSeekbarの実装を移す。
  2. 最低限の単純な機能しか持たない独自の嘘んこSeekbarを作る。
  3. Vertical Seekbarでのアニメーションthumbを諦める。

くらい。

でもまあ1番目の策はめどい(´・ω・`)
そしてそこまでの能力はない。故にパス。

2番目の策もめどい。でも最悪これもありか?

3番目の策はアリといえばアリだが、この先も出てくるであろう妥協しなければならない点を
踏まえるとここで妥協カードを早速使ってしまっていいのだろうかと悩むw

どうしようかと思いつつ色々と試す。
そして上手くいくかどうかはともかく思いつく( ゚Д゚)ピコーンッ

「setThumb()しなければ、位置は動く。drawThumb()は変えられないけど、SeekbarのonDraw()の中で
thumbの位置に絵を描いちゃえばいいんじゃね(・∀・)?」

			Drawable d = getResources().getDrawable(i);
			c.save();
			c.translate(super.getPaddingLeft()-super.getThumbOffset(), super.getPaddingTop());
			d.setBounds(getThumb().getBounds().left,
						getThumb().getBounds().top,
						getThumb().getBounds().left + d.getIntrinsicWidth(),
						getThumb().getBounds().top+d.getIntrinsicHeight());
			d.draw(c);
			c.restore();

みたいな感じ。
c.save()とc.restore()ってやる必要があるかどうかわかってないw

まあこれでValueAnimatorのonAnimationUpdate()でカウント用の変数をインクリさせて、onDraw()で
アニメパターンを変更しているけど、これでいいのかどうかはわからないw

何はともあれ、これでthumbをアニメーション化出来るみたいだ(・∀・)
まあ今回は元の絵(アイコン)とアニメパターン(元の絵にちょっと追加しただけのもの)は基本的な部分が
同じなので問題はないが、実際の実装時にはアニメーション時のデフォルトthumbは何も描かれていない
画像をセットしなくちゃいけないかな?


中々先へ進まない(´・ω・`)