- 投稿日:2019-08-04T13:27:50+09:00
[Unity]FindOfjectsOfType<T> と SceneのRootGameObjectsに対してGetComponents<T>するのではどちらが早いのか?
結果
rootからさらった方が早い
Editor
メソッド 平均処理時間(ms) FindObjectOfType 10.0 FindObjectsOfType 11 .0 RootGameObject 3.5 RootGameObject(Listに追加) 3.0 standaloneでdevelopmentbuild + Autoconnect profiler
メソッド 平均処理時間(ms) FindObjectOfType 6.5 FindObjectsOfType 6.9 RootGameObject 2.7 RootGameObject(Listに追加) 2.7 大雑把な環境
windows7
Unity 2018.4.5f1
.net 4.x
.net standard 2.0
mono計測コード
profile.csusing System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Profiling; public class NewBehaviourScript : MonoBehaviour { const string ROOT_GAMEOBJECT_NO_BUFFERED = "rootGameObjects no buffered"; const string ROOT_GAMEOBJECT_BUFFERED = "root game object buffered"; const string FIND_OBJECTS_TYPE = "find object's of type"; const string FIND_OBJECT_TYPE = "find object of type"; // Start is called before the first frame update void Start() { for (int i = 0; i < 1000; i++) { var go = new GameObject($"number {i}"); for (int y = 0; y < 20; y++) { go.AddComponent<Type1>(); go.AddComponent<Type2>(); } } } private List<Type1> _Type1s = new List<Type1>(); private List<GameObject> GameObjects = new List<GameObject>(); private List<Type1> _Type1sBuffer = new List<Type1>(); // Update is called once per frame void Update() { GC.Collect(); { Profiler.BeginSample(ROOT_GAMEOBJECT_NO_BUFFERED); var rootGameObjects = gameObject.scene.GetRootGameObjects(); Type1[][] type1s = new Type1[rootGameObjects.Length][]; for (int i = 0; i < rootGameObjects.Length; i++) { type1s[i] = rootGameObjects[i].GetComponentsInChildren<Type1>(); } Profiler.EndSample(); } GC.Collect(); { Profiler.BeginSample(ROOT_GAMEOBJECT_BUFFERED); gameObject.scene.GetRootGameObjects(GameObjects); for (int i = 0; i < GameObjects.Count; i++) { GameObjects[i].GetComponentsInChildren(_Type1sBuffer); _Type1s.AddRange(_Type1sBuffer); } Profiler.EndSample(); _Type1sBuffer.Clear(); _Type1s.Clear(); GameObjects.Clear(); } GC.Collect(); { Profiler.BeginSample(FIND_OBJECTS_TYPE); var type1s = GameObject.FindObjectsOfType<Type1>(); Profiler.EndSample(); } GC.Collect(); { Profiler.BeginSample(FIND_OBJECT_TYPE); var type1 = GameObject.FindObjectOfType<Type1>(); Profiler.EndSample(); } } } //別ファイルで定義 public class Type1 : MonoBehaviour { } public class Type2 : MonoBehaviour { }感想
最初typoで単体のコンポーネントを拾ってくるFindObjectOfType()をつかってて
それですらRootのGameObjectを全部さらってGetComponentsInChildren()したやつより遅かったので
両方計測した結果FindObjectOfType()とFindObjectsOfType()が速度的に大差ないという驚きの結果を偶然得ることができました
FindObjectsOfType()を処理した結果の先頭を返してるっぽい挙動重いと言うのは知ってましたが
単体の方ですら初期化でも使うのを躊躇うほどの処理負荷がかかるとは思ってませんでしたSceneからGameObject全部とってきてGetComponentsする方が一見すると重そうなのに
中でどういう実装が行われているのか知りませんが
似たような速度になるならともかく、どうしてこんなに差がでるほど遅いのか本当に謎ですね
- 投稿日:2019-08-04T13:11:33+09:00
UnityでRhinoInsideを使ってVRアプリを作ってみる
はじめに
この記事はRhino7(2019/7現在WIP版)に向けて開発されている、RhinoInside(公式サイトはこちら )をつかって、UnityからRhinoの機能を呼び出してVRの中で形をいじることができるアプリの作り方について説明していきます。
そもそもとはRhinoとは何かというと、フリーフォームNURBSモデリングに特化した商用の製造業向け3次元CADソフトウェア(3Dサーフェスモデラー)で、その中のジオメトリの計算機能をSDKから呼び出してRhino以外のソフトからでも使えるようになったのがRhinoInsideです。完成品
RhinoInsideを使ったVRアプリできました~
— hiron@バGH使い (@hiron_rgkr) July 24, 2019
exeファイルを起動してUnityのロゴが出た後、一瞬でる画面が裏のRhinoを起動している瞬間です。
機能はこれまでと同じで球で曲面を動かすだけです…
RhinoWIPとSteamVRが使用できる環境にある数少ない人たちしか使えない誰得アプリ#rhinoinside #Unity pic.twitter.com/a0w6ZVYpCQRhinoInside Unity のサンプルさわるまでのやり方
まずRhinoInsideをUnityで使えるようにします。やり方は公式のgithubに掲載の通りですが、READMEの内容をざっくり説明します。
必要なもの
公式githubを参考にやり方
公式githubのREADMEではUnityのバージョンは 2018.3ですが、私のUnityの環境は後述のsteamVRの使い方がよくわかなかったので、2017.4です。
RhinoInsideはgithubにあるので、コマンドラインから
git clone --recursive https://github.com/mcneel/rhino.inside.git rhino.inside
をして、githubのものをクローンするか、以下のようにgithubからそのままZIPファイルでダウンロードして下さい。その後、Unityからダウンロードしたフォルダの中のRhino.Inside/Unity/Sample1 のプロジェクトを開きます。サンプルの操作は、githubのREADMEの説明が動画になっているいてわかりやすいと思いますのでそちらをどうぞ。
UnityのmenubarのSample1からCreateLoftSurfaceをやるとUnityのSceneの画面にLoftされたSurfaceが表示されます。このLoftSurfaceの作成にRhinoInsideが使われています。VRでアバターを動かす環境整理
次にVR側の話です。こちらはあきら(@sh_akira)さんのQiita、UniVRM+SteamVR+Final IKで始めるを参考にやりました。詳細はそちらを見てください。
必要なもの
1. HTC Vive
2. VR Ready PC
3. UniVRM
4. SteamVR plugin 2.0.1(リンク先のあきらさんの記事にあるように私の環境でもうまくいかなかったので、ver2.0.1を使っています)
5. Final IK (こちらは有料なので、注意)
6. OVRLipSync(リップシンクはうまくいかなかったので、使ってないです)
7. AniLipSync-VRM(リップシンクはうまくいかなかったので、使ってないです)
8. VRMモデル(今回はアリシアソリッドちゃんを使ってます。)RhinoInsideをVRでいじれるようにしていく
まず現状のUnityの状態の確認です。上記に2つをやると以下の画像のような感じのSceneになっているはずです。左下があきらさんの記事をもとにやったVRの環境、右上がRhinoInsideのSample1をやって、LoftSurfaceを作ったものになっています。(LoftSurfaceは自分でデバックしやすいように場所を少しうつしています。)
後はあきらさんの記事から変えた点について説明していきます。基本的にはそのままで、リップシンクだけエラーがでてうまくいかなかったので、使っていないです。
VRで制御点をいじれるようにする
ここからVRでインタラクションするための設定についてです。SteamVR plugin にはインタラクションするための機能がついています。例えば物をつかむとか、対象先にテレポートするとかです。
今回はRhinoのLoftSurfaceの制御点(画面中青い球)をVRからインタラクトすることを考えます。やり方は簡単で、対象とするsphereにAdd Component で Throwable を追加します。Throwableを追加すると必要なコンポーネントも同時に追加されます。
Rigidbodyの中で、UseGravityの項目(画像の赤線部)がありますがfalseにしています。Trueにしておくと物理演算で重力適用されるので、Playモードにするとそのまま球が落ちていきます。
次につかんで離した後の挙動ですが、Throwableの名前の通りデフォルトでは投げる(はなした時の手の速度で飛んでいく)設定になっています。(青下線部)これを選択で「NoChange」にすると話した点で止まるようになります。
これらの設定をすれば、VR内で設定した球がつかめるようになります。親子関係の維持
Sphereをつかめるようになりましたが、このままだとRhinoInsideでうまくジオメトリの計算をしてくれません。
このSample1で作成されるLoftSurfaceは、親(オブジェクト名LoftSurface)の、子になっているオブジェクト(ここではSphere)を制御点として形状をコントロールしています。しかし今の設定のままでは、Playモード中に物をつかむとこの親子関係が崩れてしまい、つかんだものが親から離れてしまいます。
そこで、親子関係を維持するコンポーネントを追加します。Assetを右クリックしてCreate→C#Scriptで新たにC#のスクリプトを作成します。作成したC#スクリプトは以下です。Unityで作ったC#スクリプトのUpdataのところに以下を追加してください。ただ毎フレームごとに Loft Surface というオブジェクトの子にしているだけです。
Parent.cspublic class Parent : MonoBehaviour { void Update () { transform.parent = GameObject.Find ("Loft Surface").transform; } }これを対象のSphereに追加すれば、つかんで親子関係が外れても、すぐにLoftSurfaceの子に戻ります。とりあえず動くものにしているだけなので、このままだと実は問題がありますが許してください。ちなみに問題点は以下です。
- 親に対して子として一番下に追加されるだけなので、子の中の順番が維持されない。(制御点の順番にも意味があるのでこの順番がずれると形がおかしくなる)
- 1フレームごとに呼び出される void Update() の部分にそのまま書いているので、つかんでも1フレームごとに手から離れてしまう。
解決策はわかっていて、1つ目であれば、親子関係の順番を記録してその順番で子にすればよく、2つ目であればつかんでいる状態を判定してそれがTrueなら親子関係をもどすスクリプトを動かさなければよいだけです。そのうち作ります…
記事の一番の動画で私が操作しているものは、親子関係で最初から一番下のもの投げているだけなので、サーフェスの形状が崩れず一見手に追従して形状が変化しているように見えてます。VRアプリとして出力する
次にアプリとして出力する方法についてです。初めに、以下の作業をする前に、これより上の作業を完了しておいてください。出力用にいくつかのC#のスクリプトをいじったりするので、上の操作が終わっていないとうまく動かなくなる可能性があります。
とりあえずBuildしてみる
UnityのFileメニューからBuild Settings...を選びBuildを行います。ですがこのままだとエラーで出力がされません。Buildするものの中で、UnityのエディタのUIそのものをいじるものがあるとエラーになります。(出力するアプリはUnityではないので、UnityのUIをいじるものがあるのはおかしい)
UnityEditor関連をなくしていく
そこで、UnityEditorに関係するものを修正してい行きます。まずは Standard Assets/RhinoInside のUnityのファイルを以下のように Using UnityEditor と [InitializeOnLoad] の二つをコメントアウトします。
Unity.csusing System; using System.Reflection; using System.IO; using UnityEngine; // using UnityEditor; using Rhino; using Rhino.Runtime.InProcess; namespace RhinoInside.Unity { // [InitializeOnLoad] static class Startup同じく Standard Assets/RhinoInside の中にある UI ファイルを Assets の下にEditorというフォルダを作ってそちらに移します。直下の Editor のフォルダにあるものはBuildの際に読み込まれない個所になっているそうです。ちなみにこのUIはUnityのmenubarにGrasshopperを追加したりしているだけで本当にUIを操作しているだけのものです。
次に実際にLoftSurfaceを操作しているLoftSurfaces.csファイルをいじります。変更点は以下
- UnityEditorにかかわるものをコメントアウト
- void Start() 内にRhinoを起動させ、ウインドウを最小化させる部分を追加
変更した個所を抜粋したC#スクリプトは以下です。
LoftSurface.cs(変更箇所その1)using Rhino; using Rhino.Runtime.InProcess; using System; using System.Reflection; using System.IO; using System.Collections; using System.Collections.Generic; using UnityEngine; // using UnityEditor; namespace RhinoInside.Unity.Sample1 { // [InitializeOnLoad] // [ExecuteInEditMode] public class LoftSurface : MonoBehaviour { // [MenuItem("RhinoInside/Create Loft Surface")] public static void Create() { var surface = new GameObject("Loft Surface"); surface.AddComponent<LoftSurface>() ; }LoftSurface.cs(変更箇所その2)const int VCount = 3; void Start() { //ここを追加 --------------------- string RhinoSystemDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Rhino WIP", "System"); bool isLoaded = Environment.GetEnvironmentVariable("PATH").Contains(RhinoSystemDir); var PATH = Environment.GetEnvironmentVariable("PATH"); Environment.SetEnvironmentVariable("PATH", PATH + ";" + RhinoSystemDir); GC.SuppressFinalize(new RhinoCore(new string[] { "/scheme=Unity", "/nosplash" }, WindowStyle.Minimized)); //ここを追加 --------------------- gameObject.AddComponent<MeshFilter>(); var material = new Material(Shader.Find("Standard")) { color = new Color(1.0f, 0.0f, 0.0f, 1f) }; gameObject.AddComponent<MeshRenderer>().material = material;これでBuildすれば UnityEditor に関するエラーが出なくなるはずです。
無事Buildが完了して出力先の.exeファイルを起動すればRhinoInsideを使ったVRアプリが起動するはずです。完成!
冒頭の完成品を再掲です。
今後は、中でもうちょっと動けるようにしたりだとか、上記であったつかんだ際の親子関係の問題とかを直していければと思ってます。
ライセンス
この記事内でいじっているRhinoInside関係のコードはMITライセンスです。プラグインについては各ライセンスに従ってください。
- 投稿日:2019-08-04T09:54:45+09:00
Solitaire(1)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Solitaire : MonoBehaviour { public static string[] suits = new string[] { "C", "D", "H", "S" }; public static string[] values = new string[] { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}; public List<string> deck; // Start is called before the first frame update void Start() { PlayCards(); } // Update is called once per frame void Update() { } public void PlayCards() { deck = GenerateDeck(); Shuffle(deck); //test the cards in the deck: foreach (string card in deck) { print(card); } } public static List<string> GenerateDeck() { List<string> newDeck = new List<string>(); foreach (string s in suits) { foreach (string v in values) { newDeck.Add(s + v); } } return newDeck; } void Shuffle<T>(List<T> list) { { System.Random random = new System.Random(); int n = list.Count; while (n > 1) { int k = random.Next(n); n--; T temp = list[k]; list[k] = list[n]; list[n] = temp; } } } }


