- 投稿日:2021-08-06T20:46:12+09:00
(C#)System.RandomとUnityEngine.Randomの違い
はじめに unityでC#を使っている大学生です。スクリプトを書いていて躓いたことを備忘録として残します。 同じ疑問をお持ちになった方是非お役立てください。 今回は、乱数についてです。 Randomクラス使用時の落とし穴 差異はどこにあるか 同時に System名前空間のRandomクラスを用いた疑似乱数生成の記述方法 UnityEngine名前空間のRandomクラスを用いた疑似乱数生成の記述方法 についても触れていきます。 落とし穴 まず無知な私は、UnityEngine名前空間を使っていたので、愚直にRandomクラスを使おうとしてエラーを吐きました。 // エラー 'Random' は、'UnityEngine.Random' と 'System.Random' 間のあいまいな参照です int a = Random.Range(0,10); RandomクラスがSystem名前空間とUnityEngine名前空間の両方に存在するので、どちらのクラスを使用しているのか分からないのが問題なようです。 解決法 クラスを使う際に名前空間を指定すると簡単に解決できます。 // UnityEngine名前空間のクラスであることを明示(0以上10未満の乱数が返る) int RUNDOM_NUMBER_1 = UnityEngine.Random.Range(0,10); // System名前空間のクラスであることを明示(0以上10未満の乱数が返る) System.Random r = new System.Random(); int RANDOM_NUMBER_2 = r.Next(10); 使い分け 大きな差はインスタンスを生成するか否かのようです。 System名前空間ではインスタンスを生成する為、いくつも乱数が必要な場合に有用性を発揮します。 対してUnityEngine名前空間ではそのようなステップを踏まないので、乱数の取得が簡単です。 またSystem名前空間の方は乱数の返り値が整数ですが、UnityEngine名前空間のほうでは浮動小数点数です。 パフォーマンスでも差があるようで、UnityEngine.Randomのメソッドのほうが高速だそうです。 乱数を複数個扱わないのであればこちらを使うに越したことはなさそうですね。 (参考) Unity公式ドキュメント Versus System.Random This class has the same name as the .NET Framework class System.Random and serves a similar purpose, but differs in some key ways: Static vs instanced UnityEngine.Random is a static class, and so its state is globally shared. Getting random numbers is easy, because there is no need to new an instance and manage its storage. However, static state is problematic when working with threads or jobs (the generator will error if used outside the main thread), or if multiple independent random number generators are required. In those cases, managing instances of System.Random would be a better option. Performance Methods in UnityEngine.Random have been measured to be between 20% and 40% faster than their equivalents in System.Random. (和訳) System.Randomとの違い このクラスは、.NET Framework のクラス System.Random と同じ名前で、同じような目的を持っていますが、いくつかの重要な点で異なります。 静的 vs インスタンス UnityEngine.Randomは静的なクラスであり、その状態はグローバルに共有されます。インスタンスを生成したり、ストレージを管理したりする必要がないので、乱数の取得が容易です。しかし、スレッドやジョブを使用する場合や、複数の独立した乱数生成器が必要な場合には、静的な状態は問題となります。このような場合には、System.Randomのインスタンスを管理する方が良いでしょう。 パフォーマンス UnityEngine.Randomのメソッドは、System.Randomのメソッドに比べて20%から40%高速であることが測定されています。 記述方法 System.Randomを使う // シード値(1000)を使用して初期化 // シード値が変わらなければ毎回同じ乱数を返す System.Random r = new System.Random(1000); // 0以上10未満の乱数を整数で返す int RANDOM_NUMBER_1 = r.Next(10); // -10以上10未満の乱数を整数で返す int RANDOM_NUMBER_2 = r.Next(-10,10); // 0以上Int32.MaxValue(32bit符号付き整数の最大有効値:2147483647)未満の乱数を整数で返す int RANDOM_NUMBER_3 = r.Next(); Random.Next メソッド Next(Int32) :指定した最大値より小さい 0 以上のランダムな整数を返します。 Next(Int32, Int32) :指定した範囲内のランダムな整数を返します。 Next() :0 以上のランダムな整数を返します。 またMicrosoft公式ドキュメントに プロセス開始時に一度だけジェネレーターにシードが与えられ,その後は完全にスクリプトの制御下に置かれることになります。 との記載があるのでシード値を乱数(可変のもの)にすれば毎度違う乱数を返すようにもできます。 UnityEngine.Randomを使う // 0以上10未満の乱数を浮動小数点数で返す int RANDOM_NUMBER = UnityEngine.Random.Range(0,10); Random.Range public static float Range(float minInclusive, float maxInclusive); Returns a random float within minInclusive..maxInclusive. (訳:minInclusive..maxInclusive内のランダムな浮動小数点数を返します。) 終わりに はじめは同じに見えましたがよくよく調べるとしっかりと違いがありました。 目的に合わせて使い分けていきたいです。 そしてどれも指定した最大値"未満"の乱数を返すという点に注意が必要だと思いました。 参考記事 [https://docs.microsoft.com/ja-jp/dotnet/api/system.random.next?view=net-5.0] [https://docs.unity3d.com/ScriptReference/Random.html]
- 投稿日:2021-08-06T19:45:52+09:00
【Unity(C#)】UniWebView4使ってUnityでWebView表示
はじめに UniWebView4とはアプリ内でWEBサイトを表示したい場面で役に立つアセットです。 初めて使用する際にいろいろとわからないことが多かったので メモを残しておきます。 バージョン情報 諸々名前 バージョン Unity 2020.3.4f1 UniWebView4 4.9.0 UniRx 7.1.0 デモ フルスクリーンで半透明のWebViewを表示し、WebView上のボタン押下でCubeを動かすサンプルです。 コード まずはHTMLです。 デモなので超簡易です。作成したらStreamingAssetsに保存します。 <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=yes" /> <head> <title>Sample</title> </head> <head> <a href="web-view-demo://close"> <button style="position: fixed; top: 10px; right: 10px; padding: 6px 40px;">閉じる</button> </a> </body> <li> <a href="web-view-demo://up"> <button>↑</button> </a> </li> <li> <a href="web-view-demo://down"> <button>↓</button> </a> </li> <li> <a href="web-view-demo://right"> <button>→</button> </a> </li> <li> <a href="web-view-demo://left"> <button>←</button> </a> </li> フルスクリーン表示する際はHTML側でレスポンシブ対応しておかないとios端末で正しく表示されませんでした。 <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=yes" /> 次にC#側のWebView生成コードです。 using UniRx; using UniRx.Triggers; using UnityEngine; /// <summary> /// WebViewクラス /// </summary> public class WebViewDemo { /// <summary> /// UniWebView /// </summary> private UniWebView _uniWebView; /// <summary> /// WebViewを生成 /// </summary> /// <param name="player">動かすオブジェクト</param> /// <param name="backGroundColor">背景色</param> /// <param name="url">開きたいURL</param> public void ShowWebViewPlayerController(string url, Transform player, Color? backGroundColor = null) { //既に存在している場合は開くだけ if (_uniWebView != null) { _uniWebView.Show(); return; } var webViewObject = new GameObject("WebViewObject"); //キャッシュのクリア webViewObject.OnDestroyAsObservable().Subscribe(_ => _uniWebView.CleanCache()).AddTo(webViewObject); _uniWebView = webViewObject.AddComponent<UniWebView>(); //スキームを追加 _uniWebView.AddUrlScheme("web-view-demo"); //メッセージ受け取りイベント _uniWebView.OnMessageReceived += (view, message) => { switch (message.Path) { //閉じるボタン押下 case "close": //WebViewを非表示にする if (_uniWebView != null) _uniWebView.Hide(true); break; //↑ボタン押下 case "up": player.Translate(Vector3.forward); break; //↓ボタン押下 case "down": player.Translate(Vector3.down); break; //→ボタン押下 case "right": player.Translate(Vector3.right); break; //←ボタン押下 case "left": player.Translate(Vector3.left); break; } }; //画面の向きが変わるたびに解像度を変更 _uniWebView.OnOrientationChanged += (view, orientation) => { _uniWebView.Frame = new Rect(0, 0, Screen.width, Screen.height); }; //画面サイズを設定 _uniWebView.Frame = new Rect(0, 0, Screen.width, Screen.height); //背景色 var color = Color.white; color.a = 0.5f; _uniWebView.BackgroundColor = backGroundColor ?? color; //ツールバー非表示 _uniWebView.SetShowToolbar(false); //横スクロールバー非表示 _uniWebView.SetHorizontalScrollBarEnabled(false); //インジケータ _uniWebView.SetShowSpinnerWhileLoading(true); _uniWebView.SetSpinnerText("ロード中"); //内部保持してるサイトを読み込み var exchangeUrl = UniWebViewHelper.StreamingAssetURLForPath(url); _uniWebView.Load(exchangeUrl); //画面表示 _uniWebView.Show(); } } OnMessageReceivedで受け取ったメッセージに応じた処理を行っています。 当然ながら、HTML側のスキーム等、C#側と合わせないと動きません。 構造体をデフォルト引数に 小ネタですが、Colorをデフォルト引数として設定するのは構造体の都合上難しいようでした。 【参考リンク】:Unity3d c# - Vector3 as default parameter ですので、下記のようにNull許容型として定義し、関数内で値の有無を見てデフォルト値を設定しています。 public void Hoge(Color? c = null) { var defaultColor = Color.white; hogehoge.Color = c ?? defaultColor ; } ??はNull合体演算子と呼ばれるもので、??の左側が評価されてnullを返した場合、??の右側を評価するという書き方です。 【参考リンク】:??(null合体演算子) 最後に使う側のサンプルです。 ボタン押下で指定したHTMLのWebViewを開きます。 using UnityEngine; using UnityEngine.UI; public class Test : MonoBehaviour { [SerializeField] private Button _testButton; [SerializeField] private Transform _player; void Start() { _testButton.onClick.AddListener(ShowWebView); } private void OnDestroy() { _testButton.onClick.RemoveListener(ShowWebView); } private void ShowWebView() { var webView = new WebViewDemo(); webView.ShowWebViewPlayerController("SampleHTML.html",_player); } } 最後に 超簡単かつドキュメントがしっかりしていて、有料の価値のある非常に良いアセットだと個人的には思いました。 一点だけ下記直せずです。 iosだと問題なく動いたのですが、手元のAndroid端末(ver11)だと Hideの呼び出し時に一瞬カクついてWebViewが閉じるバグがありました。 何か知ってる人いたら助けてください。 参考リンク UniWebView もう逃げない。HTMLのviewportをちゃんと理解する
- 投稿日:2021-08-06T17:09:32+09:00
Prism.WpfのDialogServiceからのDialogでRegionを使いたい
内容 Prism.WpfでDialogServiceから呼び出したDialogの中にRegionを加えてRequestNavigateしたけれども、 ちょっと手を加えないと遷移しなかったため、その方法について記載します。 環境 Prism.Unity Version="8.1.97" 試したサンプル 結論 DialogServiceを継承したクラスを用意して(ソース) protected virtual void ConfigureDialogWindowContent(string dialogName, IDialogWindow window, IDialogParameters parameters) をオーバーライドして dialogservice protected override void ConfigureDialogWindowContent(string dialogName, IDialogWindow window, IDialogParameters parameters) { var rm = _regionManager.CreateRegionManager(); RegionManager.SetRegionManager(window as Window, rm); RegionManager.UpdateRegions(); parameters.Add("rm", rm); base.ConfigureDialogWindowContent(dialogName, window, parameters); } こんな感じでRegionManagerを新規に作成し、Dialogとして表示されるWindowに割り当てます。 ViewModelにRegionManagerを伝えるためにIDialogParametersにインスタンスを追加します。 ViewModel側でRegionManagerを受け取ってRequestNavigateします。(ソース) viewmodel public void OnDialogOpened(IDialogParameters parameters) { if (parameters.TryGetValue<IRegionManager>("rm", out IRegionManager rm)) CustomDialogARegionManager = rm; if (parameters.TryGetValue<string>("ViewName", out string v)) CustomDialogARegionManager.RequestNavigate("CustomDialogARegion", v); else CustomDialogARegionManager.RequestNavigate("CustomDialogARegion", "CustomDialogAAView"); } Region間でのRequestNavigateはIRegionNavigationServiceを使用すれば意識する必要がなくなります。(参考) 発端 <ContentControl prism:RegionManager.RegionName="MyRegion" prism:RegionManager.RegionManager="{Binding MyRegionManager}" /> こんな感じでRegionManager添付プロパティを指定すれば出来ると思って実装したのですが出来ませんでした。 調べてみるといくつか言及している質問がありました。 RegionManager unable to find region inside of module Cannot find Region in RegionManager (using PRISM) WPF, Prism v2, Region in a modal dialog, add region in code behind いくつか抜粋してグーグル翻訳してみると 問題は、リージョンがビジュアルツリーでRegionManagerのアタッチされたプロパティを検索し、そのマネージャーに登録することです。メインウィンドウでは問題ありませんが、子ウィンドウではこれは発生しません。 プレゼンタークラスのコンストラクターにリージョンマネージャーを登録する必要がありました。 そうすれば、新しいウィンドウのリージョンでグローバルリージョンマネージャーを見つけることができます。 DialogServiceによって作成されたダイアログを含むサブウィンドウに自動的に継承されることはありません。したがって、ダイアログでリージョンを使用する場合は、リージョンマネージャーを手動でアタッチする必要があります。 等々。 メイン画面ではPrismさんが良い感じにアタッチしてくれてるみたいなのですが、新規に作成したWindowに関しては自分でアタッチしないといけないみたいです。 修正案 Dialogで表示させるViewで個々にアタッチする実装を行う 一律Dialogに新規のRegionManagerをアタッチする実装を行う DialogではRegionを使用しない etc とりあえず2.の方向で検討し、上記のような実装になりました。 デメリットですが、IDialogParametersを経由してRegionManagerの受け渡しを行うため、 それのキー(今回だと"rm")が予約語的になってしまうところです。 開発者間でわかっていれば問題ないことですが暗黙知になりそうです。 最後に Prism.Wpfの7から8の変化として Bootstrapperの復活 Dialog用のカスタムウィンドウを使い分けられるようになった Region初期表示用のメソッド(RegisterViewWithRegion)が使いやすくなった 等がありました。他にもあると思います。