【Unity】UniRxを覚える(3)
前回の記事。
UnityのコルーチンをIObservableに変換
Observable.FromCoroutine
コルーチンの終了タイミングをストリームで扱うことが可能
void Start() { Observable.FromCoroutine(TestCoroutine) .Subscribe( e => { Debug.Log("next"); }, () => { Debug.Log("complete"); } ).AddTo(gameObject); } IEnumerator TestCoroutine() { Debug.Log("start"); yield return new WaitForSeconds(4); Debug.Log("finish"); }
start->finish->next->Complete
コルーチンから結果を返す
Observable.FromCoroutineValue
void Start() { Observable.FromCoroutineValue<int>(ReturnValueCoroutine) .Subscribe(e => Debug.Log(e)); } IEnumerator ReturnValueCoroutine() { for(var idx = 0; idx < 10; ++idx) { yield return idx; } }
0から9まで出力
コルーチン内でOnNextを発行
void Start() { Observable.FromCoroutine<int>(observer => CallOnNextCoroutine(observer)) .Subscribe( e => { Debug.Log(e); } ).AddTo(gameObject); } IEnumerator CallOnNextCoroutine(IObserver<int> observer) { observer.OnNext(1); yield break; }
3記事に分けて学習していきました。
おそらく入門程度の知識を身についたと思います。
ほぼすべての処理を初期化で行うのでどのくらいの負荷がかかるのかとか、
チームで導入するには学習コストが高いなどの気になるところはあります。
ただその分、今まで何十行もかけて書いてた処理を数行でかけるようになったりと、
可読性もあがります。
私個人としてはとても便利だと思うので、
ここから更に理解を深めて積極的に使っていきたいです。
【Unity】UniRxを覚える(2)
前回の記事。
UniRxを覚える(1)で学んだ、
Subjectを定義して、OnNextとして、Subscribeに到達
という流れをストリームと呼びます。
IObservableのSubscribeはIDisposableというインターフェースで実装されています。
これはリソースの開放を行うためのもので、
Dispose()のみ定義されています。
SubjectでDisposeを実行すると、ストリームの購読を終了することができます。
/// <summary> /// UniRx Subject。 /// </summary> public Subject<string> subject = new Subject<string>(); void Awake() { // UniRx Subject登録。 subject .Subscribe( message => { Debug.Log(message); }, () => { Debug.Log("Completed"); } ); } void Start() { // UniRx Subject呼び出し。 subject.OnNext("UniRx Hello!"); subject.Dispose(); subject.OnNext("UniRx Hello!"); subject.OnCompleted(); }
Disposeのあとの処理は呼び出されません。
Subjectが破棄されればストリームも全て破棄されます。
Subjectが残っていればストリームは動き続けます。
ストリームは使い終わったら必ずDisposeを呼ぶ
またはOnCompletedを発行するようにする。
AddTo
AddToを実装すれば、GameObjectが破棄されたタイミングで、
自動的にDisposeを行ってくれます。
// UniRx Subject登録。 subject .Subscribe( message => { Debug.Log(message); }, () => { Debug.Log("Completed"); } ).AddTo(gameObject);
Subject
自由にイベントを発行したいとき。
ReactiveProperty
変数にSubjectの機能をくっつけたもの。
var reactiveProperty = new ReactiveProperty<string>("sample"); reactiveProperty.Value = "Hello"; reactiveProperty.Subscribe(e => Debug.Log(e)); reactiveProperty.Value = "hello";
ReactiveCollection
状態の変化を通知する機能が内蔵されたList< T >
var reactiveCollection = new ReactiveCollection<int>(); reactiveCollection .ObserveAdd() .Subscribe(x => { Debug.Log("Add Index: " + x.Index + " Value: " + x.Value); }); reactiveCollection .ObserveRemove() .Subscribe(x => { Debug.Log("Remove Index:" + x.Index + " Value: " + x.Value); }); reactiveCollection.Add(1); reactiveCollection.Add(2); reactiveCollection.Remove(2);
Observable.Create
自由に値を発行するストリームを作ることができるファクトリメソッド。
Observable.Create<int>(ob => { ob.OnNext(1); ob.OnCompleted(); return Disposable.Create(() => {}); }).Subscribe(e => Debug.Log(e));
Observable.Start
別スレッドで実行して結果を一つだけ発行するファクトリメソッド。
非同期で走らせ結果が出たら通知。
Observable.Start(() => { var count = 0; for(var idx = 0; idx < 10000; ++idx) { count += idx; } return count; }) .ObserveOnMainThread() .Subscribe(e => Debug.Log(e)); Debug.Log("mainThread");
mainThreadの文字列が先に発行される。
Observable.Timer/TimerFrame
一定時間後にメッセージを発行するファクトリメソッド。
実時間はTimer、フレーム数での指定はTimerFrame
Observable.Timer(System.TimeSpan.FromSeconds(3)) .Subscribe(e => Debug.Log("elapsed 3sec")); Observable.Timer(System.TimeSpan.FromSeconds(3), System.TimeSpan.FromSeconds(1)) .Subscribe(e => Debug.Log("elapsed 3sec and repeat 1sec")) .AddTo(gameObject);
第一引数に発行されるまでのTimeSpan、第二引数に繰り返し発行するTimeSpanを指定できる。
Observable.EveryUpdate
Update時に通知してくれるストリームソース。
停止しない限りシーンをまたいでも動き続けます。
Observable.EveryUpdate().Subscribe(e => Debug.Log("every update"));
UniRx.Triggers
UnityのコールバックイベントをUniRxのIObservableに変換してくれる。
this.FixedUpdateAsObservable() .Subscribe(e => Debug.Log("fixed update"));
uGUIイベントのストリームソース
クリックイベント
[SerializeField] Button button = null; button.OnClickAsObservable().Subscribe(e => Debug.Log("onclick"));
(3)に続きます。
sunagimo-app.hatenablog.com
【Unity】UniRxを覚える(1)
C#のライブラリのReactive ExtensionsをUnity向けに作られたライブラリ
デザインパターンの一つのObserverパターンがベースになっています。
UniRxにおいて、重要なもの。
Subjectがこれまで使ってきた、eventの役割になる。
そしてSubjectの中でも重要な2つの処理が、
SubscribeとOnNext。
Subscribe
メッセージ受取時に実行する処理を登録
OnNext
Subscribeに登録された処理に渡して処理を実行
Subject、Subscribe、OnNextの処理を今までのイベント処理で書くとこんな感じになる。
UniRx実装前
/// <summary> /// イベント。 /// </summary> /// <param name="message">メッセージ。</param> public event Action<string> OnDisplayMessage = delegate(string message){}; void Awake() { // 登録。 OnDisplayMessage += (message) => { Debug.Log(message); }; } void Start() { // 呼び出し。 OnDisplayMessage("Hello!"); }
UniRxを使うとこのように書き直せる。
UniRx実装後
/// <summary> /// UniRx Subject。 /// </summary> public Subject<string> subject = new Subject<string>(); void Awake() { // UniRx Subjectへ登録。 subject.Subscribe(message => { Debug.Log(message); }); } void Start() { // UniRx Subject呼び出し。 subject.OnNext("UniRx Hello!"); }
SubjectはIObserverとIObservableの2つのインターフェースを実装しています。
IObserver
イベントメッセージの発行を担う
OnCompleted
OnError
OnNext
の3つのメソッドが定義されている。
IObservable
イベントメッセージを購読を担う
Subscribe
のメソッドが定義されている。
先程の処理に、OnNext、OnError、OnCompletedを追加するとこのように定義できる。
/// <summary> /// UniRx Subject。 /// </summary> public Subject<string> subject = new Subject<string>(); void Awake() { // UniRx Subject登録。 subject.Subscribe( message => { Debug.Log(message); }, error => { Debug.LogError("Error"); }, () => { Debug.Log("Completed"); } ); } void Start() { // UniRx Subject呼び出し。 subject.OnNext("UniRx Hello!"); }
SubjectとSubscribeの間に挟み込んでメッセージを処理する部品のこと。
LINQのWhereオペレータみたいなもの。
/// <summary> /// Linqサンプル。 /// </summary> public List<int> linqSampleList = new List<int>(); void Awake() { linqSampleList.Add(1); linqSampleList.Add(2); linqSampleList.Add(3); linqSampleList.Add(4); } void Start() { var resultList = linqSampleList.Where(e => e % 2 == 0).ToList(); for(var idx = 0; idx < resultList.Count; ++idx) { Debug.Log(resultList[idx]); } }
UniRxの代表的なオペレータ
Where:フィルタリング
Select:メッセージを変換
Distinct:重複を排除
Buffer:一定個数をまとめるまで待つ
ThrottleFirst:短時間にまとめてきた場合に先頭のみを使う
LINQライクな書き方
Whereを使用する
/// <summary> /// UniRx Subject。 /// </summary> public Subject<string> subject = new Subject<string>(); void Awake() { // UniRx Subject登録。 subject .Where( e => e == "UniRx Hello!" ) .Subscribe( message => { Debug.Log(message); }, error => { Debug.LogError("Error"); }, () => { Debug.Log("Completed"); } ); } void Start() { // UniRx Subject呼び出し。 subject.OnNext("UniRx Hello!"); subject.OnNext("UniRx Hello1!"); subject.OnNext("UniRx Hello2!"); }
"UniRx Hello!"のみ出力される
AwakeやStartへの負担が気になる…
続き
sunagimo-app.hatenablog.com
【Unity】Addresable Asset Systemを使ってみる
本記事で扱うUnityのバージョンは「Unity2018.3.7f1」です。
Addresable Asset Systemのバージョンは「Version 0.6.7 Preview」です。
今までの複雑だったAssetBundleのビルドやロードをよりシンプルにしたAsset管理システム
とにかくコードを書く量が少なく済み、環境によって書き換えたり、依存関係を気にすることがなくなる
導入編
まだPreview版なので、Unity2018.2以降のPackageManagerよりインストール可能です。
インストール後、UnityのWindow→Asset Management→Addresable Assetsを開きます。
はじめは設定が必要なので、
Create Addresables Settingを選択して、
設定ファイルを作成します。
作成後、このようなウインドウが開かれます。
ここで主にAssetを管理します。
Assetの登録
登録したいAssetをそのままAddresablesのウインドウに、ドラッグアンドドロップするか、
AssetのInspectorのAddresableにチェックを入れると登録されます。
Assetを選択してドラッグアンドドロップ
もしくはチェックマークを入れる
登録されるとこのようになります。
Addresableで使用するAsset名が長いのでシンプルにします。
対象のAssetを選択して、Simplify Entry Namesを選択。
Addresableで管理したいディレクトリを丸ごと持ってくるということは現状ではできないので、
今後追加されることを祈るか、自前で実装という感じですね。
一応これで準備は整いました。
Addresableのモード
Addresableの動作モードを説明します。
Play Mode Scriptを選択すると、各モードが選択できます。
・Fast
パッキングせずに、ファイルを直接ロード
・Virtual
パッキングせずに、実際にパッキングしたときの動作をシミュレート
・Packed
パッキングして、AssetBundleからロード
Assetの読み込み
実際にコードを書いていきます。
Addressables.LoadAsset<Sprite>(assetName) .Completed += (op) => { img.sprite = op.Result; };
非同期で読み込みます。
渡す引数としては、「Asset名(assetName)」の他に、
「AssetReference」というのも渡せます。
こちらは、Addresableの対象となっているAssetを直接選択することで、
入力する手間やミスを防ぐことができます。
※ver0.6.6以降から
AssetReferenceTypeRestrictionのアトリビュートを指定して、
アセットのタイプを指定できたのですが、
いろいろ変更があったらしく、処理自体が消えました。
ver0.6のAPIReferenceも消えているので、今後どうなるのかわかりませんが、
おそらくそれぞれの指定できるTypeのAssetReferenceを呼ぶ必要があるのかと。
[Header("Asset参照(Sprite)")] [SerializeField] private AssetReferenceSprite assetReference = null; /// <summary> /// 参照読み込み。 /// </summary> void ReferenceLoad() { Addressables.LoadAsset<Sprite>(assetReference) .Completed += (op) => { img.sprite = op.Result; }; }
こんな感じでInspectorから指定できます。
Assetの解放
Addresables.ReleaseAssetで参照したAssetの解放が行えます。
/// <summary> /// アセットリスト。 /// </summary> List<Sprite> assetList = new List<Sprite>(); [Header("Asset参照(Sprite)")] [SerializeField] private AssetReferenceSprite referenceSprite = null; /// <summary> /// 参照読み込み。 /// </summary> void ReferenceLoad() { if(referenceSprite == null) { return; } Addressables.LoadAsset<Sprite>(referenceSprite) .Completed += (op) => { img.sprite = op.Result; assetList.Add(op.Result); }; } /// <summary> /// 全ての参照を解放。 /// </summary> void AllReleaseAsset() { for(var i = 0; i < assetList.Count; ++i) { Addressables.ReleaseAsset(assetList[i]); } }
参照しているAssetの情報などはAddresable Profilerより確認できます。
実際に読み込むとグラフとして表示され、下にログも残ります。
解放すると参照が消えて、色が薄くなります。
今までのAssetBundleのシステムと比べるととてもシンプルになりました。
Profilerなども用意してあり、一々ログを仕込まなくてもよくなりました。
まだPreviewということなので、今後も変更があるとは思いますが、
よりよい変更になることを期待してます。
【Unity】Sprite Atlasの実装(Assetbundleにも対応)
本記事で扱うUnityのバージョンは「Unity2018.3.7f1」です。
Spriteをパッキングする機能。
複数枚の画像を一つの画像にする。
処理が軽くなります。
描画回数(Set Pass Calls)を節約できます。
こんな感じに
画像4枚をそのまま配置 → +4のSetPassCalls
画像4枚をSpriteAtlas化 → +1のSetPassCalls
Project → Create → Sprite Atlasを選択
spriteAtlasファイルが作られます
Sprite Altasの設定
・Type
Masterのまま
・Include in Build
AssetBundleのときはチェックを外す
・AllowRotation
uGUIで使用する際に表示崩れが起きるのでチェックは外す。本来はアトラスのサイズを抑えるためのもの。
・Tight Packing
uGUIで使用する際に表示崩れが起きるのでチェックは外す。本来はアトラスのサイズを抑えるためのもの。
・Padding
4のまま
パッキング
Objects for Packingという項目から、+ボタンでパッキングするSpriteを選択します。
選択できたらPack Previewを選択します。
こんな感じでパッキングされます。
パッキングした元のSpriteを使用します。
SpriteRendererの場合はそのまま配置、
uGUIの場合はImageにつけたりと通常のSpriteと使用方法は同じです。
SetPass calls
【実行前】
Game ViewのStatsより確認できます。
実行前はまだAtlasの効果は出ていません。
【実行後】
実行するとSetPass calls減っているのがわかります。
・カメラ +1
・Atlas +1
uGUIだと実行前から効果がでています。
なぜか。
AssetBundleでの使用方法
AssetBundleで使用する場合は、Sprite Atlasの設定のInclude in Buildのチェックを外します。
AssetBundleを読み込む際に、
SpriteAtlasManagerのatlasRequestedを使用します。
docs.unity3d.com
サンプルコード
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.U2D; public class SpriteAtlasLoad : MonoBehaviour { /// <summary> /// アセットバンドル名。 /// </summary> [Header("アセットバンドル名")] [SerializeField] string assetBundleName = null; /// <summary> /// アセット名。 /// </summary> [Header("アセット名")] [SerializeField] string assetName =null; [Header("イメージ")] [SerializeField] Image img = null; /// <summary> /// AssetBundle。 /// </summary> AssetBundle assetBundle; void Awake() { assetBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + assetBundleName); img.enabled = false; } void OnEnable() { SpriteAtlasManager.atlasRequested += OnAtlasRequested; } void OnDisable() { SpriteAtlasManager.atlasRequested -= OnAtlasRequested; } void OnAtlasRequested(string tag, System.Action<SpriteAtlas> atlasCallback) { var request = assetBundle.LoadAssetAsync<SpriteAtlas>(assetName); request.completed += (operation) => { atlasCallback.Invoke((SpriteAtlas)request.asset); img.enabled = true; }; } }
一応普通に読み込んだSpriteAtlasから参照して取れますが、
Warningが出ます。
サンプルコード
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.U2D; public class SpriteAtlasLoad : MonoBehaviour { /// <summary> /// アセットバンドル名。 /// </summary> [Header("アセットバンドル名")] [SerializeField] string assetBundleName = null; /// <summary> /// アセット名。 /// </summary> [Header("アセット名")] [SerializeField] string assetName =null; /// <summary> /// スプライト名。 /// </summary> [Header("スプライト名。")] [SerializeField] string spName = null; /// <summary> /// イメージ。 /// </summary> [Header("イメージ")] [SerializeField] Image img = null; /// <summary> /// AssetBundle。 /// </summary> AssetBundle assetBundle; void Awake() { assetBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + assetBundleName); } void Start() { var request = assetBundle.LoadAssetAsync<SpriteAtlas>(assetName); request.completed += (operation) => { var atlas = (SpriteAtlas)request.asset; var atlasSp = atlas.GetSprite(spName); img.sprite = atlasSp; }; } }
atlasRequestedを使えと!
モバイルで作る際にはどうしても必要な機能かと思います。
AssetBundleで使う際は少し癖はあるかと思いますが、
使い方さえ間違わなければとても強い味方。
kan-kikuchi.hatenablog.com
tsubakit1.hateblo.jp
tsubakit1.hateblo.jp
【Unity】Webviewの実装(Android、iOS対応)
本記事で扱うUnityのバージョンは「Unity2018.3.7f1」です。
UnityでのWebviewのライブラリというと、
greeさんの「unity-webview」
github.com
もしくは、AssetStoreで配信されている「UniWebView」を使用されていると思います。
https://www.assetstore.unity3d.com/jp/?stay#!/content/92605
今回はgreeさんの「unity-webview」を使用して、
サンプルを作成したので使い方や解説を行っていきます。
基本はサンプル通りに処理を書いています。
ただ少し違うのが、WebView内で選択したリンクをそのままUnity側に送って、
Unity側で処理するようにしています。
Android
Android PluginのCWebViewPlugin.javaのshouldOverrideUrlLoading内の処理を一部修正しています。
@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { canGoBack = webView.canGoBack(); canGoForward = webView.canGoForward(); // if (url.startsWith("http://") || url.startsWith("https://") // || url.startsWith("file://") || url.startsWith("javascript:")) { // // Let webview handle the URL // return false; // } else if (url.startsWith("unity:")) { // String message = url.substring(6); // mWebViewPlugin.call("CallFromJS", message); // return true; // } // すべてUnity側へ飛ばすように修正。 if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://") || url.startsWith("javascript:") || url.startsWith("unity:")) { mWebViewPlugin.call("CallFromJS", url); return true; } Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); PackageManager pm = a.getPackageManager(); List<ResolveInfo> apps = pm.queryIntentActivities(intent, 0); if (apps.size() > 0) { view.getContext().startActivity(intent); } return true; }
iOS
WebView.mmのdecidePolicyForNavigationActionの処理の一部を修正しています。
- (void)webView:(WKWebView *)wkWebView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { if (webView == nil) { decisionHandler(WKNavigationActionPolicyCancel); return; } NSURL *nsurl = [navigationAction.request URL]; NSString *url = [nsurl absoluteString]; // URLの文字列が文字化けしてしまうためエンコード。 NSString *sendUrl = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]; if ([url rangeOfString:@"//itunes.apple.com/"].location != NSNotFound) { [[UIApplication sharedApplication] openURL:nsurl]; decisionHandler(WKNavigationActionPolicyCancel); return; } else if ([url hasPrefix:@"unity:"]) { // UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [[url substringFromIndex:6] UTF8String]); UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [sendUrl UTF8String]); decisionHandler(WKNavigationActionPolicyCancel); return; // } else if (![url hasPrefix:@"about:blank"] // for loadHTML(), cf. #365 // && ![url hasPrefix:@"file:"] // && ![url hasPrefix:@"http:"] // && ![url hasPrefix:@"https:"] // && ![url hasPrefix:@"mailto:"] // && ![url hasPrefix:@"tel:"] // && ![url hasPrefix:@"facetime:"] // && ![url hasPrefix:@"sms:"]) { // if([[UIApplication sharedApplication] canOpenURL:nsurl]) { // [[UIApplication sharedApplication] openURL:nsurl]; // } // decisionHandler(WKNavigationActionPolicyCancel); // return; } else if (navigationAction.navigationType == WKNavigationTypeLinkActivated && (!navigationAction.targetFrame || !navigationAction.targetFrame.isMainFrame)) { // cf. for target="_blank", cf. http://qiita.com/ShingoFukuyama/items/b3a1441025a36ab7659c // [webView load:navigationAction.request]; // 全てのメッセージを送ってあげるようにする。 UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [sendUrl UTF8String]); decisionHandler(WKNavigationActionPolicyCancel); return; } else { if (navigationAction.targetFrame != nil && navigationAction.targetFrame.isMainFrame) { // If the custom header is not attached, give it and make a request again. if (![self isSetupedCustomHeader:[navigationAction request]]) { NSLog(@"navi ... %@", navigationAction); [wkWebView loadRequest:[self constructionCustomHeader:navigationAction.request]]; decisionHandler(WKNavigationActionPolicyCancel); return; } } } UnitySendMessage([gameObjectName UTF8String], "CallOnStarted", [url UTF8String]); decisionHandler(WKNavigationActionPolicyAllow); }
他にもiOS側でもいくつか処理の追加を行っています。
bouncesの機能をOFFに変更
スクロールを最後まで引っ張ったときに、空白の部分まで見えてしまうやつ
画像をドラッグアンドドロップによる画像反転禁止
// bouncesをOFFに変更。 id subview = [[webView subviews] objectAtIndex:0]; if([[subview class] isSubclassOfClass:[UIScrollView class]]) { ((UIScrollView *)subview).bounces = NO; // iOS11からのドラッグアンドドロップによる画像反転禁止。 if(@available(iOS 11.0, *)) { ((UIScrollView *)subview).interactions = [[NSArray alloc] init]; } }
3DTouchの機能を無効化
// iOS10以降で3DTouchを無効にする。 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 - (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo { return false; } #endif
WebView内のテキスト選択を禁止
長押しによるメニューの表示禁止
// 読み込み完了時に呼び出される。 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { // webView内のテキスト選択禁止。 [webView evaluateJavaScript:(@"document.documentElement.style.webkitUserSelect='none';") completionHandler:^(NSString *result, NSError *error) {}]; // webView内の長押しによるメニュー表示禁止。 [webView evaluateJavaScript:(@"document.documentElement.style.webkitTouchCallout='none';") completionHandler:^(NSString *result, NSError *error) {}]; }
ここらへんの処理も追加せずに、パラメータなどで切り替えられたらいいな
もういっそUnityでAPI化してほしい!
【Unity】Package Managerにモバイル通知の機能が追加されていた件(3)
本記事で扱うUnityのバージョンは「Unity2018.3.7f1」です。
前回
sunagimo-app.hatenablog.com
今回はサンプルを作成したので紹介します。
github.com
【機能一覧】
・通知の作成(Android、iOS)
・通知の削除(Android、iOS)
・通知の有無(Android、iOS)
・繰り返し通知の作成(Android、iOS)
・バックグラウンド通知(Android)
まず、モバイル通知ですが、
アプリがバックグラウンドにいる状態で届くのが基本です。
しかし今回使用した、モバイル通知API、および他のAssetにおいても、
iOSではバックグラウンド状態で通知が届くのですが、
Androidにおいてはフォアグラウンド状態でも通知が届いてしまいます。
今回のサンプルは、それを踏まえた上で
バッググラウンド状態のみ通知が届くように実装しております。
【バッググラウンド通知の実装の流れ】
1.作成した通知をリストに追加
2.Prefsにも保存する(電源を落としたあとにも保持するため)
3.アプリをバッググラウンド状態にする
4.リストにある通知を全て送信
5.アプリをフォアグラウンドにする
6.通知を全てキャンセルする
左から、説明します。
・10秒後に通知(DateTime指定)
・10秒後に通知(TimeSpan指定)
・1分後に繰り返し通知
・スケジュールされている通知を全て削除
の4つのサンプル処理を用意しました。
NotificationController.SendNotification("通知テスト", "10秒後に通知(TimeSpan)", notificationTimeInterval, NotificationController.NotificationType.TYPE_1); NotificationController.SendNotification("通知テスト", "10秒後に通知(DateTime)", notificationDateTime, NotificationController.NotificationType.TYPE_1); NotificationController.SendNotification("通知テスト", "1分後に繰り返し通知", notificationDateTime, NotificationController.NotificationType.TYPE_1, true, repeatInterval); NotificationController.AllCancelNotification();
NotificationControllerクラスから呼べるようにしてあります。
その中でAndroid、iOSの処理を分けて呼んでいます。
また、パラメーターに通知タイプ、通知IDを指定するようにしています。
予め識別子を設けておくことで、管理が可能になります。
通知されるとこんな感じで表示されます。
なお、Androidの場合はOSのバージョンによって見た目が異なります。
(Android)
Package ManagerからMobile Notificationsをインストールすると、
Editor/com.unity.mobile.notifications/NotificationSettingsという設定ファイルが作られます。
ここでAndroidの通知アイコンの設定ができます。
smallIconについては白色の透過アイコンでないと、
四角い真っ白になってしまうので注意が必要です。
Previewで正常に表示されていれば問題ないと思います。
ここらへんを参考に。
qiita.com
(iOS)
Player SettingsのNotification Iconsで設定します。
まず、今まで外部ライブラリなどを用いて使ってきた機能が、
Unity内に含まれたことはとても素晴らしいことでした。
その上で今まで使ってきた、モバイル通知のライブラリと比較しても
代用できる十分な機能を備えていると思います。
ただ一つだけ、
この記事の最初でも書いていますが、
Androidのバックグラウンド通知をデフォルトとして設定できるようにしてほしいです。
いつか、Preview表示が消えることを祈っています。
そしてUnityさんに感謝しています。