20190804のUnityに関する記事は3件です。

[Unity]FindOfjectsOfType<T> と SceneのRootGameObjectsに対してGetComponents<T>するのではどちらが早いのか?

結果 

rootからさらった方が早い

Editor

メソッド 平均処理時間(ms)
FindObjectOfType 10.0
FindObjectsOfType 11 .0
RootGameObject 3.5
RootGameObject(Listに追加) 3.0

findobject vs rootgameobject editor.JPG

standaloneでdevelopmentbuild + Autoconnect profiler

メソッド 平均処理時間(ms)
FindObjectOfType 6.5
FindObjectsOfType 6.9
RootGameObject 2.7
RootGameObject(Listに追加) 2.7

findobject vs rootgameobject mono.JPG

大雑把な環境

windows7
Unity 2018.4.5f1
.net 4.x
.net standard 2.0
mono

計測コード

profile.cs
using 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する方が一見すると重そうなのに
中でどういう実装が行われているのか知りませんが
似たような速度になるならともかく、どうしてこんなに差がでるほど遅いのか本当に謎ですね

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでRhinoInsideを使ってVRアプリを作ってみる

はじめに

 この記事はRhino7(2019/7現在WIP版)に向けて開発されている、RhinoInside(公式サイトはこちら )をつかって、UnityからRhinoの機能を呼び出してVRの中で形をいじることができるアプリの作り方について説明していきます。
 そもそもとはRhinoとは何かというと、フリーフォームNURBSモデリングに特化した商用の製造業向け3次元CADソフトウェア(3Dサーフェスモデラー)で、その中のジオメトリの計算機能をSDKから呼び出してRhino以外のソフトからでも使えるようになったのがRhinoInsideです。

完成品

RhinoInside Unity のサンプルさわるまでのやり方

 まずRhinoInsideをUnityで使えるようにします。やり方は公式のgithubに掲載の通りですが、READMEの内容をざっくり説明します。

必要なもの

  1. GITのクライアント(ダウンロード)
  2. RhinoWIP(ダウンロード)(Rhinoのライセンスを持っていないと使えません)
  3. Unity(ダウンロード)

公式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ファイルでダウンロードして下さい。

 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 を追加

 Throwableを追加すると必要なコンポーネントも同時に追加されます。

 必要なコンポーネントも同時に追加

 Rigidbodyの中で、UseGravityの項目(画像の赤線部)がありますがfalseにしています。Trueにしておくと物理演算で重力適用されるので、Playモードにするとそのまま球が落ちていきます。
 次につかんで離した後の挙動ですが、Throwableの名前の通りデフォルトでは投げる(はなした時の手の速度で飛んでいく)設定になっています。(青下線部)これを選択で「NoChange」にすると話した点で止まるようになります。
 これらの設定をすれば、VR内で設定した球がつかめるようになります。

親子関係の維持

 Sphereをつかめるようになりましたが、このままだとRhinoInsideでうまくジオメトリの計算をしてくれません。
 このSample1で作成されるLoftSurfaceは、親(オブジェクト名LoftSurface)の、子になっているオブジェクト(ここではSphere)を制御点として形状をコントロールしています。しかし今の設定のままでは、Playモード中に物をつかむとこの親子関係が崩れてしまい、つかんだものが親から離れてしまいます。
 そこで、親子関係を維持するコンポーネントを追加します。Assetを右クリックしてCreate→C#Scriptで新たにC#のスクリプトを作成します。

  Create→C#Script

 作成したC#スクリプトは以下です。Unityで作ったC#スクリプトのUpdataのところに以下を追加してください。ただ毎フレームごとに Loft Surface というオブジェクトの子にしているだけです。

Parent.cs
public 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] の二つをコメントアウトします。

Assets/RhinoInside/Unity

Unity.cs
using 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を操作しているだけのものです。

 UIファイル移動

 次に実際にLoftSurfaceを操作しているLoftSurfaces.csファイルをいじります。変更点は以下

  1. UnityEditorにかかわるものをコメントアウト
  2. 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アプリが起動するはずです。

完成!

 冒頭の完成品を再掲です。

  RIUApp.gif

 今後は、中でもうちょっと動けるようにしたりだとか、上記であったつかんだ際の親子関係の問題とかを直していければと思ってます。

ライセンス

この記事内でいじっているRhinoInside関係のコードはMITライセンスです。プラグインについては各ライセンスに従ってください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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;
            }
        }
    }

}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む