FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »Unityライブラリ
このページの記事一覧

【Unity】タブ切り替えの UI をコードを書かないで作る  


 今回はスクリプト無しでタブ切り替え(TabStrip, Tabs Widget)を作る方法を紹介しよう。と言っても私が考えたわけではなく、内容的には海外記事の翻訳したものを、自分なりにアレンジしたものだったりする(笑)。

Unity UI: Easy Tabs (no scripting)



 タブの UI の実装に関しては、大抵スクリプトで画像を切替える方法がオーソドックスな気がするが、今回のように Toggle を上手く利用する方法もなるほどと感じたので、日本語記事にしておくと初心者にも役に立つんじゃないかと考えた(ググってもなぜかタブ実装の詳しい日本語記事が見つからなかったので)。それに何にしてもコードを書かずに済ませられるなら、それだけ労力を減らせるので、そういった方法は積極的に導入していくと良いと思う(アプリが大きくなるにつれ、大量のコードの管理・デバッグに時間を取られてしまうため)。


(※) Unity 2018.4.12f1 / Windows10(x64) で確認



■タブの backgroud 画像を用意する



 とりあえず、参考記事を真似して、もっと簡単な画像を用意したので、必要ならダウンロードして使って欲しい。もちろんダウンロードした素材は自由に加工して使って貰っても構わない(zip には psd と完成例の unitypackage も付けておく)。

>>サンプルの素材をダウンロード
(Google Drive を利用。画面右上にあるダウンロードアイコンを押す)


 プロジェクトビューで適当なフォルダを作ったら、zip に入っている「Tab_altas.png」をドラッグドロップなどして、インポートして欲しい(.unitypackage はこの記事の完成例となっている。プレファブ化して利用するのも良いだろう)。

 「Tab_altas.png」をインポートしたら、まずはインスペクタで「Texture Type」を「Sprite (2D and UI)」に、「Sprite Mode」を「Multiple」にしよう。これは1つの画像を複数のスプライトとして利用する設定だ。



 次にそのままインスペクタから「Sprite Editor」を開いて、上部の [Slice]を押して、「Type」で「Grid By Cell Size」で「Pixel Size」を 64x64 にして、[Slice] して欲しい。これでグリッド状に 64x64 でスプライトが等分割される。



 分割したら、各スプライトをクリックすると、右下に「Sprite」の設定が出るので、Name(名前)や Border(引き伸ばしの領域)を設定しておこう。名前は左から「Tab_panel」「Tab_item」(わかり易い名前なら何でも良い)と付けておき、Border は各スプライトの LRTB を全て 18 にしておいて欲しい。完了したら、右上の [Apply] を押しておく。画像の引き伸ばし領域の設定に関しては以下の記事がわかり易いだろう。

UnityのSpriteを引き伸ばす際に綺麗に引き伸ばす方法 SpriteEditor






■タブの UI を作る

 スプライトの設定ができたら、本題のタブの UI を作っていこう。まずは背景のベースとなる画像(Image)を1つ置く。場所はどこでも良いが、とりあえず中央に置いておきたいので RectTransform の Pos を全て (0, 0, 0) にしておく。



 また、ついでに Image を置いたときに自動で追加された Canvas の「Canvas Scaler」を適当に設定しておく。ここでは「UI Scale Mode」を「Scale With Screen Size」に、「Reference Resolution」を「1920 x 1080」にしておいた。この辺りは自分の環境によって変えて欲しい。



 先ほど置いた Image も「TabPanel」と名前を変えておき、サイズも適当に決めておこう。ここでは 600 x 500 にしておいた。そして Image に「Tab_altas」の分割スプライトの「Tab_panel」を設定しておく



 次にタブの画像を並べるレイアウト用の空オブジェクトを作ろう。「TabPanel」をヒエラルキーで選択して、右クリックで「Create Empty」して、名前を「TabContainer」としておく。そして、[Add Component] から「Toggle Group」と「Horizontal Layout Group」を追加しておく。設定は自分の環境に合わせればよいが、「Child Control Size」と「Child Force Expand」をオンにしておけば、自動でレイアウトしてくれる。



また「TabContainer」の RectTransform は Pivot や引き伸ばしなどの設定をしておこう。これも画像などのサイズによるので、自由に設定しても構わない。設定例はキャプチャを参考にして欲しい。


 TabContainer を適当に設定したら、次に各タブを作っていこう。TabContainer を右クリックして、Toggle を置く。初期位置がおかしくなってたりしたら、一旦 Toggle をオフ→オンにすると自動レイアウトになっている場合、再配置される(セーブなど何らかの操作をすると再計算される)。

 置いた Toggle はわかり易いように「Tab 1」とでも名前を変えておき、「Toggle」コンポーネントの「Group」に、先ほど「TabContainer」にアタッチした「Toggle Group」を登録しておく。



 そして「Tab 1」を展開し、「Bacground」の Image にスプライト「Tab_atlas」の「Tab_item」を設定する。Color も A(アルファ)を下げておく。RectTransform は適当で良いが、上下ストレッチを設定しておこう。



 また、更に下の階層の「Checkmark」にも「Tab_item」を設定し、同じように RectTransform をストレッチ設定にしておく。あとはテキストとなる「Label」も適当に設定しておくと良いだろう。



 タブが1つできたら、「Tab 1」を [Ctrl + D] で適当に複製しよう。名前は適当に付けて欲しい。また複製したタブの「Toggle」コンポーネントの「IsOn」は全てオフにしておく(これも設定したら、一旦オブジェクトをオフ→オンすると再描画できる)。



 タブを並べたら、次に切り替える内容を作ろう。これも自分の環境によって何でも構わないが、「TabPanel」以下に適当に同じサイズの空オブジェクトをタブと同じ数だけ作り、内容を各オブジェクトに入れておく、ここでは「PageContainer」を作って「Page 1~3」を作ってテキストを入れておいただけだ。好きにやって貰って構わない。これらも「Page 1」以外は非表示にしておく(タブのオン/オフの初期状態に合わせておく)。



 Page 1~3 を作ったら、各対応タブに戻り、「Toggle」コンポーネントの「On Value Changed (Boolean)」にコールバックを設定しよう。これは Toggle.IsOn のオン/オフ状態が変化したときに、実行されるメソッドなどを登録するものだ。ここに各対応する「Page」の表示/非表示を設定する。具体的には「Tab 1」には「Page 1」の「GameObject.SetActive (Dynamic)」をセットし、同様に「Tab 2」には「Page 2」を、「Tab 3」には「Page 3」をセットする。ここでは表示の切り替えだけなのでコードを書く必要はないが、色々コールバックに仕込みたいなら以下の記事を参考にして欲しい。

【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録



 もし、クリックしたときのトランジション(色の変化)がいらないなら、各 Toggle の Transition の Target Graphic を None(元の画像を外す)にしておけば良い。



 これで完成だ。シーンを保存して動作を確かめてみよう。


VRM Live Viewer では、これをそのまま使っている。
色や文字を太字に変化するようにすれば、見た目も結構良くなる。
何よりタブ切り替えのコードを書かなくて済むので実装が楽だ(笑)。






(関連記事)
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】制限付きでテキストのサイズに合わせて他のオブジェクトのサイズも変化させる
【Unity】【C#】RectTransform の矩形の実座標を取得する
【Unity】【C#】uGUI ドロップダウンの要素をコードで設定と取得、外観のカスタマイズなど
【Unity】【C#】インスペクタの表示項目を動的に変更する
【Unity】【C#】独自のギズモ(Gizmo)を表示する


関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  uGUI  サンプル 
tb: 0   cm: --

【Unity】【C#】制限付きでテキストのサイズに合わせて他のオブジェクトのサイズも変化させる  


 ContentSizeFitter を使えば近い感じにもなるが、最小~最大みたいになると、レイアウトの入れ子になったり、オブジェクトを階層化しなくてはならなかったりと複雑になる気がするので、もっと簡単にできるものないかとググってみたら、やはり同じ様なものがあった

TextSizeAdjuster



 ただ試してみたら、ちょっと私が欲しい挙動と違ったので、元のスクリプトを参考に改造してみた。簡単に言えば「テキストの幅が最小~最大サイズまでは伸縮し、最大を超えたら折り返す」みたいにできるようにスクリプトに書き換えた。

 まぁ、自動レイアウトを駆使すれば同じことはできるかもしれないが、実は私も自動レイアウトは苦手なので、今回のスクリプト1つで簡単に実現できるのなら、それもアリかと(笑)。

【Unity】uGUIの自動レイアウトが分かりにくいと評判なので解説してみる

(※) Unity 2018.4.8f1 / Windows10(x64) で確認



■テキストのサイズに合わせて他のオブジェクトのサイズも変化させる(サイズ制限付き)

●TextSizeSync.cs
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif

/// <summary>
/// https://gist.github.com/iwashihead/db6e88e7de74f43c43fb9ebbd6769516
/// を改造
/// Text の preferredWidth / preferredHeight に合わせて
/// 他の RectTransform のサイズも変更する.
/// </summary>
[ExecuteInEditMode]
//[ExecuteAlways]
public class TextSizeSync : MonoBehaviour
{
//サイズを調整する方向
public enum AdjustMode
{
Width, Height, Both,
}
public AdjustMode adjustMode = AdjustMode.Width;

public bool alwaysAdjust = false; //Update でテキスト変化を検出

public Text referenceText; //サイズを参照するテキスト

public float minWidth = -1; //調整する最小幅 (※負で無視)
public float maxWidth = -1; //調整する最大幅 (※負で無視)
public float minHeight = -1; //調整する最小高 (※負で無視)
public float maxHeight = -1; //調整する最大高 (※負で無視)

//同期させるオブジェクトの余白(テキストサイズ+余白になる)
public RectOffset padding = new RectOffset();

//サイズを同期させるオブジェクト
public RectTransform[] syncRectTransforms = new RectTransform[0];


//Editor only
private void Reset()
{
if (referenceText == null)
referenceText = GetComponent<Text>();
}

//Editor only
private void OnValidate()
{
if (referenceText != null)
Adjust();
}

private void OnEnable()
{
if (referenceText != null)
Adjust();
}

// Use this for initialization
private void Start()
{
Adjust();
}

// Update is called once per frame
private void LateUpdate()
{
if (alwaysAdjust && IsTextChanged)
Adjust();
}

string _oldText;

//テキストの内容が変化したか?
public bool IsTextChanged {
get {
if (_oldText == null || _oldText != referenceText.text)
{
_oldText = referenceText.text;
return true;
}
return false;
}
}

//サイズの調整
public void Adjust()
{
switch (adjustMode)
{
case AdjustMode.Width:
AdjustWidth();
break;
case AdjustMode.Height:
AdjustHeight();
break;
case AdjustMode.Both:
AdjustBoth();
break;
}

AdjustSyncRectTransforms();
}


Vector2 _size = Vector2.zero;

//幅を調整
void AdjustWidth()
{
var w = ClampWidth(referenceText.preferredWidth);
var h = referenceText.rectTransform.sizeDelta.y;
_size.Set(w, h);
referenceText.rectTransform.sizeDelta = _size;
}

//高さを調整
void AdjustHeight()
{
var w = referenceText.rectTransform.sizeDelta.x;
var h = ClampHeight(referenceText.preferredHeight);
_size.Set(w, h);
referenceText.rectTransform.sizeDelta = _size;
}

//幅・高さの両方を調整
void AdjustBoth()
{
var w = ClampWidth(referenceText.preferredWidth);
var h = ClampHeight(referenceText.preferredHeight);
_size.Set(w, h);
referenceText.rectTransform.sizeDelta = _size;
}

//幅の制限
float ClampWidth(float w)
{
if (minWidth >= 0) w = Mathf.Max(minWidth, w);
if (maxWidth >= 0) w = Mathf.Min(maxWidth, w);
return w;
}

//高さの制限
float ClampHeight(float h)
{
if (minHeight >= 0) h = Mathf.Max(minHeight, h);
if (maxHeight >= 0) h = Mathf.Min(maxHeight, h);
return h;
}

//他の RectTransform のサイズ同期(余白付き)
void AdjustSyncRectTransforms()
{
var w = ClampWidth(referenceText.preferredWidth);
var h = ClampHeight(referenceText.preferredHeight);

foreach (var rt in syncRectTransforms)
{
var x = rt.sizeDelta.x;
var y = rt.sizeDelta.y;
switch (adjustMode)
{
case AdjustMode.Width:
x = padding.left + w + padding.right;
break;
case AdjustMode.Height:
y = padding.top + h + padding.bottom;
break;
case AdjustMode.Both:
x = padding.left + w + padding.right;
y = padding.top + h + padding.bottom;
break;
}
_size.Set(x, y);
rt.sizeDelta = _size;
}
}
}


#if UNITY_EDITOR
//※エディタ用
//OnValidate() では他のコンポーネント変化が検知できないので、
//インスペクタの更新で変化をチェックする
[CustomEditor(typeof(TextSizeSync))]
public class TextSizeSyncEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();

var obj = target as TextSizeSync;
if (obj.IsTextChanged)
{
obj.Adjust();
}
}
}
#endif

 色々付け加えたので長くなった(笑)。まぁ、少し古いバージョン(.NET3.5)でも対応できるようにしてるせいもあるけどね。
"[ExecuteInEditMode]" なんかもいつの間にか "[ExecuteAlways]" になったらしいしね。

 使い方は、参照する Text と同じ GameObject にアタッチし、変化させたいオブジェクトを syncRectTransforms に登録するだけだ。あとは最小~最大サイズや余白を設定しておく。






●例えば以下のようタグを囲むのに使っている


※ただし、レイアウトの計算は同じフレーム内では上手く反映されないので、1フレーム遅らせて反映させた方が確実。



 ちなみに、gif アニメの ContetnSizeFitter と TextSizeAdjuster の設定は以下のようになる。



●ContentSizeFitter ① を使い、LayoutElement で MinWidth を設定





●ContentSizeFitter ② を使い、LayoutElement で MinWidth, PereferedWidth を設定





●TextSizeAdjuster と ContentSizeFitter を使う

TextSizeAdjuster






 まぁ、自動レイアウトを極めれば色々できるかも知れないが、とにかく組み合わせを考えるのが大変なので、何重にもレイアウトの入れ子になるくらいなら、簡単なスクリプトを作るのも1つの手かも知れない(笑)。

【Unity】uGUIの自動レイアウトが分かりにくいと評判なので解説してみる
【Unity】逆引き自動レイアウトのトレーニング。UIをLayoutGroupで並べる
逆引き、UnityのuGUIのレイアウトトレーニング(uGUI RectTransform入門その2)






(関連記事)
【Unity】タブ切り替えの UI をコードを書かないで作る
【Unity】【C#】RectTransform の矩形の実座標を取得する
【Unity】【C#】uGUI ドロップダウンの要素をコードで設定と取得、外観のカスタマイズなど
【Unity】【C#】インスペクタの表示項目を動的に変更する
【Unity】【C#】独自のギズモ(Gizmo)を表示する


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityリファレンス  uGUI 
tb: 0   cm: --

【Unity】【C#】アセットバンドルのキャッシュを調べて不要なものを消す  


 もう随分 AssetBundle Manager を触ってなかったのだが、ちょっと利用する機会があったのでメモ。

 どうやら Unity2017 以降にはキャッシュの操作もできるようになったみたいなので、キャッシュの存在確認や不要なキャッシュを簡単に削除できると良いと思った。

 この AssetBundle Manager はUnity5 時代のもので、もうアセットストアからは落とせないみたいだが、まだお世話になってる人のためにも機能追加案を書いておこう。他のツール使ってるなら必要ないかもだが、同じような考え方なら流用もできるだろう。


(※) Unity2018.3.9f1 / Windows10(x64) で確認



●マニフェストからハッシュを取得
//マニフェストからハッシュを取得
static public Hash128 GetAssetBundleHash(string assetBundleName)
{
return m_AssetBundleManifest.GetAssetBundleHash(assetBundleName); //※先にマニフェストをロードしておく必要あり
}

 この関数はレガシーな AssetBundle Manager を基にしてるので注意(スコープや書き方も合わせている)。

 これは元からある AssetBundleManifest.GetAssetBundleHash を少し簡単にしただけのものなので、あまり説明はいらないだろう。このハッシュは実際にマニフェストに書かれているもので、テキストエディタなどで「~.manifest」を開けば、それぞれの「AssetFileHash」に対応していることがわかる。もう結構古い記事になるがテラシュールさんの記事に詳しく書いてあるので、参考資料として載せておこう。

AssetFileHashとTypeTreeHashは、AssetBundleの更新判定
AssetBundleManifest.GetAssetBundleHash



●アセットバンドルがキャッシュに存在しているか?(url と hash を用いる)
//アセットバンドルがキャッシュに存在しているか?
static public bool IsExistCaching(string assetBundleName)
{
var url = BaseDownloadingURL + assetBundleName;
var hash = GetAssetBundleHash(assetBundleName);
return Caching.IsVersionCached(url, hash);
}

 この関数もレガシーな AssetBundle Manager を基にしてるので注意(スコープや書き方も合わせている)。GetAssetBundleHash は前述したものだ。

 なぜだかわからないが、この関数はわりと昔からあって(Unity5 の時代からあったらしい?)、マニュアルにも載ってるバージョンとそうでないバージョンがある。

 IsVersionCached(string url, int version) の方は Obsolute(廃止予定)だし、version の指定はどちらかというと、アセットバンドル全体のバージョンという感じで、個々のアセットバンドルの差分としては使いづらいんだよね(この version は WWW.LoadFromCacheOrDownload [obsolute] や UnityWebRequestAssetBundle.GetAssetBundle などの引数に当たる)。なので、ハッシュの方を使いたいことが多い。まぁ、url 指定なので少し使いづらいが…。

 また、ハッシュと任意の名前を用いることもできる。ここでは名前をアセットバンドル名としておくと、以下のようなものも作れる。

●アセットバンドルがキャッシュに存在しているか?(url と name を用いる)
//アセットバンドルがキャッシュに存在しているか?
static public bool IsExistCaching(string assetBundleName)
{
var hash = GetAssetBundleHash(assetBundleName);
var cab = new CachedAssetBundle()
{
hash = hash,
name = assetBundleName,
};
return Caching.IsVersionCached(url, hash);
}

 こちらの方が使いやすいかも知れない。url をキーとしてないので、移動しても使える。

 ついでに色々資料も載せておこう。

キャッシュの存在確認
Caching.IsVersionCached
WWW.LoadFromCacheOrDownload [obsolute]
UnityWebRequestAssetBundle.GetAssetBundle



●現在のハッシュ以外のキャッシュを消す
//現在のハッシュ以外のキャッシュを消す(※使うタイミングに注意)
static public bool ClearOtherCaching(string assetBundleName)
{
var hash = GetAssetBundleHash(assetBundleName);
return Caching.ClearOtherCachedVersions(assetBundleName, hash);
}

 この関数もレガシーな AssetBundle Manager を基にしてるので注意(スコープや書き方も合わせている)。GetAssetBundleHash は前述したものだ。

 これは割と最近(Unity2017.1 以降?)にできてたらしい。調べていたら出てきたので、試してみたら上手く行った。実行は同期的なのでタイミングには気をつける必要があるが(まだ古い使用中アセットがあった場合、途中破棄されるわけで)、他にも削除する方法はあるみたいなので、色々やってみるのも良いかも知れない。また、資料には「デフォルトで150日間で期限切れ」ともあるね。以下に記事も載せておこう。

[Unity 2018.2] AssetBundleのキャッシュを完全に理解する
期限切れによる削除
Caching.GetCachedVersions



 まぁ、この AssetBundle Manager 自体は旧時代のものなので、これからも使うなら少し書き直した方が良いかも知れない。WWW よりも UnityWebRequest の方がパフォーマンス良いと聞いたこともあるし、実際に書き換え例なんかもあるので、自分でやってみると良いかも(Unity2019 には Addressable Assets というものも出てくるらしいしので今更だが(笑))。

AssetBundleManagerのwwwをUnityWebRequestに変えて、さらにキャッシュの古いバージョンを削除してみる






(関連記事)
【Unity】【C#】AssetBundleManager を WebGL で使う・エラー対処法
【Unity】【C#】Addressable Assets でキー(アドレス)の存在(登録)を調べる


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityオープンソースライブラリ 
tb: 0   cm: --

【Unity】Firebase のプッシュ通知と FantomPlugin の共存(Firebase と他のプラグインの共存方法)  


 ちょっと最近 VRM Live Viewer に構いっきりで(笑)、FantomPlugin の更新の方が滞っているのだが、Android 8.0 以降では通知システムも変わってしまったので、試しに Firebase のプッシュ通知をオーバーライドできるかやってみた。結果はそれほど難しく無く可能だったので、細かい部分はカスタマイズした方が良いと思うが、導入もできると思う。あくまで簡単な例となるが、FantomPlugin と Firebase の共存の方法を提供しておこう。

 ただ、この機能は Unity2018.2.x 以降のプロジェクト内で Java をプラグインとして扱えるようになったおかげで使えるようになったものなので、必要であれば、以下の記事も参照して欲しい。

【Unity】【C#】プロジェクト内で Android(Java)プラグインをビルドする


(※) Unity2018.3.5f1 (Unity 2018.2 以降) / Firebase SDK 5.5.0 / Windows10(x64) で確認



■プラグインの導入

 大まかには Firebase SDKFantomPlugin の2つを用意することになる。といっても、ここではオーバーライドするプラグインの例に FantomPlugin を使っているというだけで、考え方さえわかれば、他のプラグインに Firebase を利用することも可能だろう。



●Firebase SDK のインポート(掲載時点 SDK ver.5.5.0)

 Firebase のプッシュ通知を利用するには、公式から配布されている Unity 用のパッケージを導入する必要がある。以下のページに「SDK をダウンロード」というボタンがあるので、クリックして zip をダウンロード→解凍して欲しい。

Unity プロジェクトに Firebase を追加する


 zip を解凍すると、「dotnet4」というフォルダがあるので、その中から「FirebaseMessaging.unitypackage」をインポートしよう。これには Firebase からプッシュ通知を受け取るアクティビティやマニフェストファイル、API などが含まれている。初回のインポートでは依存関係のライブラリを自動導入するので、結構時間かかるかも知れない(※Build Target が Android になってない場合はここでやっておくと良い)。また、「dotnet3」にも同じものが入っているが、これは .NET 3.x 時代のもので、Unity2018 ではデフォルトで .NET 4.x となっているので、ここでは不要だ。

 Resolver(依存関係の解決)が終わったら、大量のファイルが「Assets/Plugins/Android」に配置されていると思う。Firebase を既存のプロジェクトに使うだけなら、ここにある「AndroidManifest.xml」はあまりいじる必要はないが(だいたい自動でやってくれる)。他のプラグインと共存するには少し手を加える必要がある。その辺りは後で説明しよう。



●FantomPlugin のインポート

 とりあえず今回はアセットストア版の「FantomPlugin (Android Native Dialogs and Functions Plugin)」(※長いので略してるだけ(笑))を使って説明しよう。Google Drive 版を使っている人でも基本的には同じなので、パス名などを置き換えてもらえばできると思う。

 より詳しいセットアップは「AssetStore版 FantomPlugin のセットアップ」の記事にも書いてあるが、簡単な方法としてはエディタ上で [Ctrl+9] でアセットストアを開き「Android Native Dialogs and Functions Plugin」を検索すれば、すぐに出てくると思うので、とりあえずダウンロード→インポートして欲しい。





■FantomPlugin オーバーライド用 Acrivity の導入

 インポートが終わったら、もう1つプロジェクトに追加して欲しいものがある。それは先ほど保留したAndroidManifest.xml」 に関わる問題でもある。というのは Firebase SDK のインポート で作られたマニフェストファイル(AndroidManifest.xml)は Firebase 専用となっている。この辺りの説明は少し難しくなってしまうのだが、なるべく簡単に説明すると、この「Firebase 専用のものをオーバーライドすることにより、他のプラグインの機能を共存してしまおう」ということだ。そのオーバーライドするのに必要な Activity を用意した。そのうち、アセットストア版にも出すつもりだが、とりあえずは先行配布としてこれを使って欲しい。


 ダウンロードして zip を解凍すると「Firebase」フォルダに「OverrideFantomPluginOnFirebaseMessagingActivity.java」というのが入っていると思う。これは現在使われている「FantomPlugin」と同じもので、Unity2018.2 のプラグイン用の Java コードをプロジェクト内でビルドするために作ってみたものだ(内容的にはプラグイン「fantomPlugin.aar」の内部に入ってる「FullPluginOnUnityPlayerActivity」と全く同じ)。


 また、「AndroidManifest_FirebaseMessaging.xml」というのも入っているが、これは Firebase の「AdnroidManifest.xml」に「OverrideFantomPluginOnFirebaseMessagingActivity」をオーバーライドするように修正したものだ。Firebase のデフォルト設定に追加しただけのものなので、必要あれば編集した方が良いかも知れない。

 これらをプロジェクトのプラグインルート「Assets/Plugins/Android/」以下に置いて欲しい。ここに Java ファイルを置いておけば Unity が自動でビルドしてくれる(Build System が「Gradle」のときのみ[※Unity2018 はデフォルト])。

 また、元の「AndroidManifest.xml」はリネームして「AndroidManifest_old.xml」とでもしておき、「AndroidManifest_FirebaseMessaging.xml」を「AndroidManifest.xml」にリネームすると、オーバーライドが有効になる


 この辺りの内容は少し難しくなるので、簡単に説明にしておくと、「UnityPlayerActivity(Unity)継承→ MessagingUnityPlayerActivity(Firebase)継承→ OverrideFantomPluginOnFirebaseMessagingActivity(FantomPlugin)」のようにオーバーライドされていることになる(※つまり自作するときは同じように、競合しないようなオーバーライドする Activity を作れば良い)。

 必要なら参考資料を載せておこう。ダウンロードした zip の中の「Default」フォルダがあると思うが、その中にある「OverrideFantomPluginOnUnityPlayerActivity.java」はデフォルトの「UnityPlayerActivity」を継承している。Firebase のオーバーライドと同じように継承するものを変更すれば、他のプラグインもオーバーライドできるかも知れない。

UnityPlayerActivity Java コードの拡張
Activity
アプリ マニフェスト



■Firebase の準備

 アプリ方の準備はだいたい終わったので、次のプッシュ通知を送信する Firebase自体をセットアップしよう。

1.Firebase をまだ利用してない人は、まずは登録しよう。といっても Google アカウントを持っていれば、サイトに行けばすぐに新規登録できると思う。登録完了したら、いつでも画面の右上あたりにある「コンソールへ移動」があるので、プロジェクト管理へ移動できる。


2.コンソール(「Firebaseへようこそ」)へ来たら、プッシュ通知を利用するプロジェクトを作成しよう。「+プロジェクトを追加」を押して、プロジェクト名などを任意に入力し、「プロジェクトを作成」ボタンを押せば良いだけだ。完了したら、プロジェクトへ移動しよう。



3.初回の場合、プロジェクトにはアプリが追加されてないので以下の画面が出ると思う。今回は Android のプラグインをオーバーライドする実験なので、Android のアイコンを押そう。



4.「アプリの登録」の「Android パッケージ名」は Unity の Identification と合わせておく必要がある(※以下のアプリIDは例なので、自分のアプリのIDに置きかえる)。Unity の PlayerSettings(File>Build Settings...>Player Settings...>Other Settings>Identification)を開いてコピペすると良い。ここが1文字でも間違ってたら、通知は届かないので注意しよう。



●Unity 側

※FantomPlugin を使う場合、Android 4.2 以上にする必要がある。


5.これら Firebase の設定を Unity に認識させるために、設定ファイル「google-services.json」をダウンロードしておく必要がある。このファイルは Unity の Assets 直下に置くことで反映される(ID の紐づけ)



●Unity 側


6.次の「Firebase SDK の追加」は Unity の場合、自動でやってくれるので必要ない。



7.最後の「アプリを実行してインストールを確認」はスキップしてしまって良いだろう。





■アプリをビルドする

 これまでの準備が整ったらアプリをビルドしてみよう。注意すべき点は、「OverrideFantomPluginOnFirebaseMessagingActivity.java」「AndroidManifest_FirebaseMessaging.xml(AndroidManifest.xml)」「アプリのID(Android パッケージ名/Identification)」「google-services.json」だ。結構大変だが、置く場所なども含めて確認して欲しい。

 確認が取れたら、ビルドするシーンは何でも良いが、FantomPlugin のデモなら、「FantomPlugin/Demo/Scenes/」以下に置いてある。それらを「Build Setting>Scenes In Buildに追加し、「Player Settings...」で「Minimum API Level」が 4.2 以上(※これは FantomPlugin の仕様。できれば 5.0 以上の方が良いかも知れない)になってればOKだ。ビルドして一度起動してみよう。また、今回は通知を受信する実験をするだけなので、アプリはすぐに終了して構わない。







■Firebase からプッシュ通知を送る

 ここまででの手順が間違ってなければ、既に Firebase からプッシュ通知の送信ができるようになっている。Firebase のコンソール へ行き、左のメニューから「Cloud Messsaging」へ移動しよう。初回なら「Send your first message」のボタンが出るので、それを押すとメッセージを作成することができる。



1.通知の作成画面では任意にテキストを入力して行こう。気をつけて欲しいのは既にリリースされているアプリの場合、適当に入力したものがユーザーに届いてしまうことである。その辺は自己責任で(笑)。



2.一番重要なのは、このターゲットのアプリ(ID)だ。ここを Unity のアプリの Identification と同じにすることによって、そのアプリ用の通知として送られる。間違えないようにしよう(Android / iOS などでも分別できる)。



3.以降の「スケジュール設定」は「Now」(今すぐ)で、「コンバージョンイベント」はデフォルトで良いだろう。「その他のオプション」では「Android 通知チャンネル」という項目があるが、これは Android 8.0 以降の「通知チャンネル」の機能で、通知ごとに種類を分別できるようにするものだ(例えば「お知らせ」とか「更新情報」とか、別々に通知のオン・オフできるようにするため)。Android 8.0 より前の機種では分別はされない。

Android O(APIバージョン26)のPush通知



4.最後の「メッセージの再確認」では「公開」ボタンを押すと即送信されるので(スケジュールで「今すぐ送信」(Now)の場合)、間違いがないか確認してから押そう。ユーザーに飛んでいってしまうので、もう取り消せない(笑)。



5.履歴が表示されていれば、送信完了だ。通信状態・サーバー状況などによって、タイムラグが出ることもあるが、成功していればおおよそすぐに通知が来ると思う。実機で確認してみよう。





※アイコンは Unity で設定したものが表示される(空だと Android のデフォルトアイコンになる)






(関連記事)
【Unity】【C#】プロジェクト内で Android(Java)プラグインをビルドする
【Android】【Java】パーミッションの付与(許可)のチェックと要求をする
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】AssetStore版 FantomPlugin のセットアップ


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityプラグイン  Unityオープンソースライブラリ  Firebase  FantomPlugin 
tb: 0   cm: --

【Unity】【C#】Addressable Assets でキー(アドレス)の存在(登録)を調べる  


 新しいリソースの管理のシステムである Addressable Assets をちょっといじってみた。しかしなぜか、素材のアドレス(キー)が存在しないときに出る InvalidKeyException が try~catch で捕捉できないので、登録の有無の検出ができなかった…(掲載時点:Addressables System 0.5.3-preview)。


InvalidKeyException encounterd in operation UnityEngine.ResourceManagement.CompletedOperation`1[UnityEngine.Texture], result='', status='None', valid=True, location=..
UnityEngine.AsyncOperation:InvokeCompletionEvent()

InvalidKeyException: Exception of type 'UnityEngine.AddressableAssets.InvalidKeyException' was thrown., Key=hogehoge
UnityEngine.AsyncOperation:InvokeCompletionEvent()

 まだ preview 版なので、単なるバグなのかも知れないが、少し不便と感じたのでググってみたら、フォーラムにも同じ内容があった。

Can I ask Addressables if a specified key (addressable name) is valid?

 まぁそのうち、ちゃんとエラー捕捉できるようになったり、「Addressables.HasKey()」みたいなのができそうな気がするが、今使うには簡単なものを用意しておくのも良いだろう。



(※) Unity 2018.3.2f1 / Addressables System 0.5.3-preview / Windows10(x64) で確認



■Addressable Assets にアドレス(キー)が存在するか否か?を返すメソッドを定義

 フォーラムにある「AddressableResourceExists」でも判別可能ではあったが、そのすぐ下に Unity の中の人からのアドバイスが書かれていたので、試しにやってみた。

What you have above probably could work. But the thing I would actually recommend is to just do Addressables.LoadAsset<IResourceLocation>("key");

This will not load your asset, but will load the location that points to your asset. If your key is valid, then you'll get a valid location back. Otherwise you will not. You can keep and use that location (doing LoadAsset<GameObject>(myNewLocation)) or you can just discard it and continue to load with key once needed.

 Google翻訳すれば『これはアセットをロードするのではなく、アセットを指す場所をロードします。キーが有効であれば、有効な場所に戻ります』となるが、要するに「存在位置が示されればアセットは存在する」ということだろう。

●Addressable Assets にアドレス(キー)が存在するか否か?
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement;

public static class AddressablesUtil { //※クラス名は任意

// (参考) https://forum.unity.com/threads/can-i-ask-addressables-if-a-specified-key-addressable-name-is-valid.574033/
// http://fantom1x.blog130.fc2.com/blog-entry-319.html
/// <summary>
/// Addressable Assets にアドレス(キー)が存在するか否か?
/// </summary>
/// <param name="key">アドレス(キー)</param>
/// <returns>true = 存在する / false = 存在しない</returns>
public static bool Exists(string key)
{
return Addressables.LoadAsset<IResourceLocation>(key).Status == AsyncOperationStatus.Succeeded;
}
}

 やってみたら、どうやら存在しないキー(アドレス)を指定したときには「AsyncOperationStatus.Failed」になるみたいなので(まだ何もしてないときは None)、これだけで存在有無は確認できそうだ。Addressables.LoadAsset() の戻値:IAsyncOperation<IResourceLocation> は null は出ないみたいなので、チェックは省略した。

 使用例を簡単に書くと以下のようになる。

●Exists() の使用例(Addressable Assets からテクスチャを読み込んで RawImage に入れるサンプル)
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement;

public class AddressablesTest : MonoBehaviour { //※クラス名は任意

public RawImage image;

void Start () {
string key = "hogehoge"; //アセットのアドレス(キー) ※任意

if (AddressablesUtil.Exists(key))
{
Addressables.LoadAsset<Texture>(key).Completed += op => {
image.texture = op.Result;
};
}
else
{
Debug.LogWarning("Invalid key = " + key);
}
}
}




■Task と連携して非同期で読み込むメソッドを定義

 ついでに前述した「Exists」と以下の拡張メソッドを組み合わせて、動作を Task 化し、アドレスの存在検出して、有れば素材を、無ければ default(この場合は基本的に null)が返ってくる関数を定義してみよう。これを使うとかなり楽に非同期ロードができるようになる。Task 化メソッドは以下の記事(IAsyncOperationExtensions.GetAwaiter<T>)をまるっとコピーして欲しい。
 
【Unity】Addressable Asset Systemのリソース読み込みをasync/await対応する

●アドレス(キー)が存在するならロードし、無ければ default (= null) を返す
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement;
using System.Threading.Tasks; //※Task ならこっち
//using UniRx.Async; //※UniTask ならこっち

public static class AddressablesUtil { //※クラス名は任意

// (参考) https://qiita.com/toRisouP/items/e03b53e8eb6af06fbcbf (※IAsyncOperationExtensions.GetAwaiter<T> を利用)
// http://fantom1x.blog130.fc2.com/blog-entry-319.html
/// <summary>
/// アドレス(キー)が存在するならロードし、無ければ default (= null) を返す
/// </summary>
/// <typeparam name="T">素材の型</typeparam>
/// <param name="key">アドレス(キー)</param>
/// <returns>取得した素材</returns>
public static async Task<T> LoadOrDefault<T>(string key) where T : class
{
if (Exists(key)) //※前述のメソッド
return await Addressables.LoadAsset<T>(key); //※IAsyncOperationExtensions.GetAwaiter<T> が必要
return default;
}
}

 もちろん、Task を UniTask(UniRx が必要)に置き換えることも可能だ。

 簡単な使い方としては以下のようにしても良いだろう(※かなり適当なので、好きに書き換えて(笑))。

●LoadOrDefault()の使用例(Addressable Assets からテクスチャを読み込んで RawImage に入れるサンプル)
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement;
using System.Threading.Tasks; //※Task ならこっち
//using UniRx.Async; //※UniTask ならこっち

public class AddressablesTest : MonoBehaviour {

public RawImage image;

void Start () {
string key = "hogehoge"; //アセットのアドレス(キー) ※任意
LoadImage(key); //※Task なら警告が出る
//LoadImage(key).Forget(); //※UniTask なら警告を消せる
}

async Task LoadImage(string key)
{
var tex = await AddressablesUtil.LoadOrDefault<Texture>(key);
if (tex != null)
{
image.texture = tex;
}
else
{
Debug.LogWarning("Invalid key = " + key);
}
}

}

 もちろんこれも UniTask(UniRx が必要)に置き換えても良い。

 待機したいなら、async/await を付ければ良い(※この例ではあまり意味がないが(笑))。
async void Start () {
string key = "hogehoge"; //アセットのアドレス(キー) ※任意
await LoadImage(key);
}



 まだ、Addressable Assets はプレビュー版だからか、使い勝手が微妙な所もあるが、いずれは Resources の代わりに使われるようになるだろう。Addressable Assets ははじめから非同期ロードだったり、プリロード機能もあったりして便利なので、今から慣れておくのも良いかも知れない。





(関連記事)
【Unity】【C#】AssetBundleManager を WebGL で使う・エラー対処法
【Unity】【C#】アセットバンドルのキャッシュを調べて不要なものを消す


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityトラブルシューティング  C# 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR