20210124のUnityに関する記事は6件です。

Unity: VuforiaのImageTargetにオブジェクトを動的に追加する方法

How To Dynamically Add Content to Targets in Unity(https://library.vuforia.com/articles/Solution/Working-with-Vuforia-and-Unity.html)
を参考にしたがPrefabを表示出来なかったので、こうやったらOKだった。

環境

OS macOS 10.15.7
Unity 2019.4.2f1
Addressable 1.16.15

手順(上記マニュアルGoogleで直訳)

  1. Unityプロジェクトを開くか作成し、ImageTarget GameObjectを追加します(メニュー:GameObject> Vuforia> Image)

  2. 実行時に、カスタム3Dモデルを画像ターゲットに動的にアタッチするとします。

  3. プロジェクトビューの[アセット]フォルダーの下に、Prefabというサブフォルダーを作成します(まだサブフォルダーがない場合)。

  4. PrefabオブジェクトをPrefabフォルダーに追加します。 3Dオブジェクトを表すカスタムPrefabを作成する方法はたくさんあります。たとえば、次のことができます。

  • シーンビューで単純なCubeゲームオブジェクトを作成し、それをシーンビューからプロジェクトビューのPrefabsフォルダーにドラッグします。 または
  • Unityでサポートされている形式(FBX、OBJ、DAE、3DSなど)で3Dモデルをインポートします。さまざまなファイル形式の3Dモデルからプレハブを作成する方法の詳細については、UnityのWebサイトを参照してください。
  1. たとえば、C#スクリプトを作成し、それをLoaderと呼び、それをイメージターゲットオブジェクトにアタッチします。

次のコードをスクリプトに挿入して、スクリプトを保存します。

using UnityEngine;
using Vuforia;
using System.Collections;

public class loader : MonoBehaviour, ITrackableEventHandler {
    private TrackableBehaviour mTrackableBehaviour;

    public GameObject model;

    // Use this for initialization
    void Start ()
    {
        mTrackableBehaviour = GetComponent<TrackableBehaviour>();

        if (mTrackableBehaviour)
        {
            mTrackableBehaviour.RegisterTrackableEventHandler(this);
        }
    }               

    // Update is called once per frame
    void Update ()
    {
    }

    public void OnTrackableStateChanged(
              TrackableBehaviour.Status previousStatus,
              TrackableBehaviour.Status newStatus)
    {
        if (newStatus == TrackableBehaviour.Status.DETECTED ||
            newStatus == TrackableBehaviour.Status.TRACKED)
        {
            OnTrackingFound();
        }
    }
    private void OnTrackingFound()
    {
        if(model!=null){
            GameObject obj=GameObject.Instantiate(model);
            obj.transform.parent = mTrackableBehaviour.transform;
            obj.transform.localPosition= new Vector3(0f, 0f, 0f);
            obj.transform.localRotation = Quaternion.identity;
            obj.active=true;
        }
     }
}

スクリーンショット 2021-01-24 20.06.19.png


なんでマニュアルではpublic Transform myModelPrefab;ってしたんだろう。こうするとPrefabを登録できない。(マニュアルにはUIにPrefabを登録すると書いているのに)。公式マニュアルに掲載されているのでこれが正しいはずなのに。

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

UniRxのUIにまつわる処理3選

様々な使い方のあるUnityのライブラリ「UniRx」の中でも、
UIにまつわる処理を紹介します。

その1. ReactiveProperty
その2. BindToButtonOnClick
その3. BindToOnClick

その1. ReactiveProperty

Sample1
using UnityEngine;
using UniRx;

public class Sample1 : MonoBehaviour
{
    //int level; とほぼ同じ使い方ができる。
    ReactiveProperty<int> level = new ReactiveProperty<int>();

    void Start()
    {
        level.Subscribe(Talk);
        //level.Subscribe(arg => Talk(arg)); のようなラムダ式でも可
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            level.Value++;  //.Valueで中身にアクセスする
        }
    }

    void Talk(int level)
    {
        Debug.Log("level up!" + level);
    }
}

Spaceを押すとlevelが上がり、levelが上がると当時にlevel up!と表示される。

ReactivePropertyは、ざっくり言うと、値の変更を通知してくれる変数のようなものです。
new ReactiveProperty()で作成し、Valueから値を取得・設定します。
(UniRxのReactivePropertyについて より引用。詳しい使い方も書かれています。)

Subscribe関数から、値の変更時に呼ばれて欲しい関数を登録することができます。
値と常に同期して欲しいことの多いUIの変更に便利です。

その2. BindToButtonOnClick

Sample2
using UnityEngine;
using UnityEngine.UI;
using UniRx;

public class UniRxSample2 : MonoBehaviour
{
    [SerializeField] Button button;

    //bool can_press; とほぼ同じ使い方ができる。
    ReactiveProperty<bool> can_press = new ReactiveProperty<bool>();

    // Start is called before the first frame update
    void Start()
    {
        can_press.BindToButtonOnClick(button, _ => Click());
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            can_press.Value = true;
        }

        if (Input.GetKeyDown(KeyCode.F))
        {
            can_press.Value = false;
        }
    }

    void Click() { Debug.Log("Click"); }
}

can_pressというReactivePropertyの値が、Tキーを押すとtrueに、Fキーを押すとfalseに変更される。can_pressの値が変更されるとbuttonのinteractableも変更される。

BindToButtonOnClickは、ボタンに"押下された際の処理"と"押せるかどうか"、つまりonClickとinteractableを同時に登録するような関数です。
bool型のReactivePropertyから呼び出すことができます。

ちなみに、can_press.SubscribeToInteractableでinteractableのみ登録ができます。

ButtonのOnClickAsObservable()関数は、onClickをIObservableに変換する関数です。
Where関数で、bool型の関数を入力することができます。そのbool型の関数の結果がtrueの場合のみしか登録した関数を呼びださない、という処理が可能になります。

その3. BindToOnClick

Sample3
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UniRx;

public class UniRxSample3 : MonoBehaviour
{
    [SerializeField] private Button button;

    void Start()
    {
        button.BindToOnClick(_ => Observable.FromCoroutine(LevelingUp));
    }

    IEnumerator LevelingUp()
    {
        int level = 0;
        for (int i = 0; i < 100; i++)
        {
            level++;
            yield return null;
        }
        Debug.Log("level up!");
    }
}

buttonを押すと、コルーチンが呼ばれ、完了するまでButtonのinteractableがfalseとなる。

BindToOnClickはButtonにIObservableを返す関数を登録することができる関数です。
入力したIObservableが完了するまで、interactableがfalseとなります。
アニメーションを伴う処理や、サーバーと通信する処理などをする間、Buttonを押せなくするのに便利です。
CoroutineはFromCoroutine関数でIObservableに変換することができます。

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

Unity ML-Agents 導入 macOS Big Sur

1.前書き

Unityの機械学習、ML-Agentsをいろんな記事をフラフラ彷徨いながらやっと導入できました。
目次載せておきますんで「途中でエラー出て詰まってるよー」って方はビュンビュンスキップしてもらってOKです!

2.必要なツール

Unity バージョン2018以降
Anaconda-Navigator
Python 3.7(Macは3.7じゃないとバグるので3.7は厳守)

これらがインストールできたら次の項目へ〜。

3.仮想環境の作成とML-Agentsのインストール

ターミナルを開き以下のコマンドを順番に打ち込んでください。

[ Python仮想環境の作成 ]

1行目で仮想環境を作成し、2行目で仮想環境を起動します。
※既に同じ名前の仮想環境が作成されている場合はエラーが出ます。作り直しましょう。

conda create -n ml-agents python3.7
​conda activate ml-agents

[ ML-Agentsのインストール ]

※既に「ml-agents」という名前のファイルが開いているディレクトリ内に存在する場合エラーが出ます。

git clone https://github.com/Unity-Technologies/ml-agents.git

[ Pythonパッケージのインストール ]

cdコマンドで先ほどインストールしたml-agentsファイルの中に移動します。
そして、その中にあるPythonパッケージをインストールします。

cd ml-agents
pip install -e ./ml-agents-envs
pip install -e ./ml-agents

[ 完了! ]

これでターミナルはOKです!
Let‘s go to Unity!!

4.Unityで学習環境を作成

今回は、ボールが立方体に向かって転がるように訓練する学習環境を作ります。
2018以降のUnityでプロジェクトを作成します。
まずメニューバーのWindow > Package Managerを開いて下さい。
下の画像のように右上の検索欄に「ML Agnets」と入力すると出てくるので右下のinstallボタンからインストールして下さい。ProjectウィンドウのPackaegフォルダ内に「ML Agents」とあればインストール完了です!

スクリーンショット 2021-01-24 10.34.20.png

次に、PlaneとCubeとSphereを作成してそれぞれ順番に「Floor」「Target」「RollerAgent」という名前にして下さい。
それぞれにMaterialをつけるとこんな感じです↓

スクリーンショット 2021-01-24 10.02.41.png

そして、「RollerAgent」という名前でスクリプトを作成して下記のコードをコピペして下さい。

using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
// RollerAgent
public class RollerAgent : Agent
{
   public Transform target;
   Rigidbody rBody;
   // 初期化時に呼ばれる
   public override void Initialize()
   {
       rBody = GetComponent<Rigidbody>();
   }
   // エピソード開始時に呼ばれる
   public override void OnEpisodeBegin()
   {
       // RollerAgentが床から落下している時
       if (this.transform.localPosition.y < 0)
       {
           // RollerAgentの位置と速度をリセット
           this.rBody.angularVelocity = Vector3.zero;
           this.rBody.velocity = Vector3.zero;
           this.transform.localPosition = new Vector3(0.0f, 0.5f, 0.0f);
       }
       // Targetの位置のリセット
       target.localPosition = new Vector3(
           Random.value * 8 - 4, 0.5f, Random.value * 8 - 4);
   }
   // 観察取得時に呼ばれる
   public override void CollectObservations(VectorSensor sensor)
   {
       sensor.AddObservation(target.localPosition); //TargetのXYZ座標
       sensor.AddObservation(this.transform.localPosition); //RollerAgentのXYZ座標
       sensor.AddObservation(rBody.velocity.x); // RollerAgentのX速度
       sensor.AddObservation(rBody.velocity.z); // RollerAgentのZ速度
   }
   // 行動実行時に呼ばれる
   public override void OnActionReceived(float[] vectorAction)
   {
       // RollerAgentに力を加える
       Vector3 controlSignal = Vector3.zero;
       controlSignal.x = vectorAction[0];
       controlSignal.z = vectorAction[1];
       rBody.AddForce(controlSignal * 10);
       // RollerAgentがTargetの位置に到着した時
       float distanceToTarget = Vector3.Distance(
           this.transform.localPosition, target.localPosition);
       if (distanceToTarget < 1.42f)
       {
           AddReward(1.0f);
           EndEpisode();
       }
       // RollerAgentが床から落下した時
       if (this.transform.localPosition.y < 0)
       {
           EndEpisode();
       }
   }
}

スクリプトが作成できたらRollerAgent(オブジェクト)にAddComponentでRIgidBody、Behaviour Parameters、RollerAgent、DecisionRequesterを順番に追加し、下の画像のようにパラメータを変更して下さい。

スクリーンショット 2021-01-24 10.24.48.png

これで学習環境の設定は完了です!

5.Pythonスクリプトで学習

[ 訓練設定ファイルの作成 ]

ターミナルコマンドでダウンロードしたml-agents > configフォルダ内にsampleという名前でフォルダを作りその中にyamlファイルを作成します。
config内の他のフォルダにもyamlファイルがあるのでそれを複製して書き換えるのでもいいでしょう。
では、作ったyamlファイルを「RollerBall」という名前に設定し以下のコードをコピペして下さい。

RollerBall:
  summary_freq: 1000
  batch_size: 10
  buffer_size: 100
  normalize: true



behaviors:
 RollerBall:
   trainer_type: ppo
   hyperparameters:
     batch_size: 10
     buffer_size: 100
     learning_rate: 0.0003
     beta: 0.005
     epsilon: 0.2
     lambd: 0.95
     num_epoch: 3
     learning_rate_schedule: linear
   network_settings:
     normalize: true
     hidden_units: 128
     num_layers: 2
     vis_encode_type: simple
   reward_signals:
     extrinsic:
       gamma: 0.99
       strength: 1.0
   keep_checkpoints: 5
   checkpoint_interval: 500000
   max_steps: 500000
   time_horizon: 64
   summary_freq: 1000
   threaded: true

[ Terminalで学習開始 ]

では、ターミナルをもう一度開いて次の2つのコマンドを入力!

cd ml-agents
mlagents-learn ./config/sample/RollerBall.yaml --run-id=RollerBall-ppo-1 --force

これで下のような画面が出たらUnityの[▶︎]を押して学習スタートです!

                        ▄▄▄▓▓▓▓
                  ╓▓▓▓▓▓▓█▓▓▓▓▓
             ,▄▄▄m▀▀▀'  ,▓▓▓▀▓▓▄                           ▓▓▓  ▓▓▌
           ▄▓▓▓▀'      ▄▓▓▀  ▓▓▓      ▄▄     ▄▄ ,▄▄ ▄▄▄▄   ,▄▄ ▄▓▓▌▄ ▄▄▄    ,▄▄
         ▄▓▓▓▀        ▄▓▓▀   ▐▓▓▌     ▓▓▌   ▐▓▓ ▐▓▓▓▀▀▀▓▓▌ ▓▓▓ ▀▓▓▌▀ ^▓▓▌  ╒▓▓▌
       ▄▓▓▓▓▓▄▄▄▄▄▄▄▄▓▓▓      ▓▀      ▓▓▌   ▐▓▓ ▐▓▓    ▓▓▓ ▓▓▓  ▓▓▌   ▐▓▓▄ ▓▓▌
       ▀▓▓▓▓▀▀▀▀▀▀▀▀▀▀▓▓▄     ▓▓      ▓▓▌   ▐▓▓ ▐▓▓    ▓▓▓ ▓▓▓  ▓▓▌    ▐▓▓▐▓▓
         ^█▓▓▓        ▀▓▓▄   ▐▓▓▌     ▓▓▓▓▄▓▓▓▓ ▐▓▓    ▓▓▓ ▓▓▓  ▓▓▓▄    ▓▓▓▓`
           '▀▓▓▓▄      ^▓▓▓  ▓▓▓       └▀▀▀▀ ▀▀ ^▀▀    `▀▀ `▀▀   '▀▀    ▐▓▓▌
              ▀▀▀▀▓▄▄▄   ▓▓▓▓▓▓,                                      ▓▓▓▓▀
                  `▀█▓▓▓▓▓▓▓▓▓▌
                       ¬`▀▀▀█▓


Version information:
 ml-agents: 0.24.0.dev0,
 ml-agents-envs: 0.24.0.dev0,
 Communicator API: 1.3.0,
 PyTorch: 1.7.1
2021-01-24 12:05:49 INFO [learn.py:269] run_seed set to 223
2021-01-24 12:05:49 INFO [environment.py:205] Listening on port 5004. Start training by pressing the Play button in the Unity Editor.

[▶︎]を押したらReward(報酬)が記録されていきます。
他にStepという値があるはずなのでそのStepが50000を超えたら学習をストップ(Unityエディタの[▶︎]を押す)してください。
これで学習完了です!

[ 推論モデルを実行 ]

ml-agents内のresultというフォルダの中を調べていくとRollerBall.onnxというものがあるはずです。それが推論モデルになります。それをUnityにインポートしてRollerBall(オブジェクト)のBehaviour ParametersのModelsにドラッグ&ドロップしてください。そのまま[▶︎]を押せば学習した内容の通りに動きます!

後書き

これで一人でも「嗚呼分からない。嗚呼。」という方を減らせれば幸いです!

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

Unityを始める - Unityの画面構成 -

はじめに

以前、Unityで開発を始める場合に必要な事をまとめました。

Unityでゲーム開発を進める中で、知っておく必要があるUnityの画面構成についてまとめます。
また、一般的な基本操作もまとめます。

参考になる本

参考になるサイト

準備

Unityのプロジェクトの作成

  • UnityHubの起動
  • 新しいプロジェクトの作成

詳細は「Unityを始める - Unityインストールからプロジェクト作成 -」にまとめました。

Unityの各画面説明

スクリーンショット 2021-01-22 16.43.21.png

Sceneビュー

Sceneビュー は、作成しているゲーム世界に相互作用できるビューです。シーンビューを使って風景、キャラクター、カメラ、ライト、その他のすべての種類の ゲームオブジェクト を選択し配置します。

Gameビュー

Gameビュー はゲーム内のカメラから見た絵をレンダリングしています。それは最終的にパブリッシュしようとしているゲーム画面です。プレイヤーがゲームをしている時に、実際に見ていものを制御するには 1つ以上の Camera (カメラ) を使う必要があります。

Hierarchyビュー

Hierarchy (ヒエラルキー) ウィンドウには現在のシーンにおける各ゲームオブジェクトのリストが表示されます。これらのいくつかは Asset ファイル (3D モデルなど) のインスタンスそのもので、その他は、プレハブのインスタンスで、ゲームのほとんどを構成するカスタムオブジェクトです。オブジェクトはシーンの中で加えたり、削除したりするので、Hierarchy ウィンドウでも、表われたり、消えたりします。

デフォルトでは、オブジェクトは作成された順に Hierarchy ウィンドウにリストされます。オブジェクトをドラッグして上下に順番を替えたり、「子」や「親」のオブジェクトにしたりできます。

Inspectorビュー

Inspector を使用して、ゲームオブジェクト、アセット、マテリアルなどの物理的なゲームアイテムや、Unity エディター内の設定や環境設定など、ほぼすべてのプロパティーと設定を確認および編集できます。

Unityエディタ画面の構成

各ビューのタブをドラッグして、好きなところに持っていくことで、レイアウトを変更できます。

構成のカスタマイズと保存

画面上部の「Window⇒Layouts」にて始めから登録されているレイアウトを使用する事ができます。

まとめ

今回はUnityの画面構成についてまとめました。

これらの使い方を知っておく事で、今後のUnityによるゲーム開発のスピードや精度を一気に高める事ができます。また、他の人が作成したUnityのプロジェクトの内容も理解しやすくなります。

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

[Unity]DOTS(主にECS)の素晴らしい記事リンク集(随時更新中)

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

MessagePack for C#で、Genericな変数をIL2CPP環境で使いたいときのメモ

前提

このライブラリの応用?的な使い方の話をしています。
現時点の最新版、2.2.85で動作確認済み。
https://github.com/neuecc/MessagePack-CSharp

Unityエディタでは動いてたのに、IL2CPP対応(iOSとかWebGLビルドだと強制)で
FormatterNotRegisteredException 出てキッツい人向けお役立ち情報。

やりたいこと

変数部分をGenericにして、要処で使いわけたいパターンがあるとする。
テストコードとしてはこういう感じ。

public class TestClass
{
    [MessagePackObject(keyAsPropertyName: true)]
    public class TargetClass<T>
    {
        public T param = default;
        public TargetClass(T param)
        {
            this.param = param;
        }
    }

    // Tには [MessagePackObject] が付いた別クラスのインスタンスが来る想定
    public void ExecuteTest<T>(T parameter)
    {
        var bytes = MessagePackSerializer.Serialize(new TargetClass<T>(parameter));
        MessagePackSerializer.Deserialize<TargetClass<T>>(bytes);
    }
}

Unityで通常実行すると普通に通るんだけども、何も考えずIL2CPP設定でビルドすると通らない。
IL2CPP環境では動的コード生成が行えないので、事前にGeneratedResolverを生成しておく必要がある。
事前生成のやり方は、公式ReadMeのAOT Code Generation (support for Unity/Xamarin)の項目で説明されている。
https://github.com/neuecc/MessagePack-CSharp#aot-code-generation-support-for-unityxamarin

問題は、AOTコード生成をやっても上のコードはまだ通らない。
なので、もうひと手間を加える必要があるぞ、というのが今回の記事。

解決方法

    [MessagePackObject(keyAsPropertyName: true)]
    [MessagePack.Union(0, typeof(TargetClass<GenericClass_0>))]
    [MessagePack.Union(1, typeof(TargetClass<GenericClass_1>))]
    [MessagePack.Union(2, typeof(TargetClass<GenericClass_2>))]// 対応させる必要クラス分、Unionをこの要領で追加すること
    public class TargetClass<T>
    {
        public T param = default;
        public TargetClass(T param)
        {
            this.param = param;
        }
    }

Union機能を使うことで解決ができます。
https://github.com/neuecc/MessagePack-CSharp#union
Unionを追加した後に、先述のAOT Code Generationの作業を(再)実行する必要があるので注意してください。
AOT Code Generationで生成したコード内を見ると、対応できてそうかは目視確認できます。

    // 前略
    internal static class GeneratedResolverGetFormatterHelper
    {
        private static readonly global::System.Collections.Generic.Dictionary<Type, int> lookup;

        static GeneratedResolverGetFormatterHelper()
        {
            lookup = new global::System.Collections.Generic.Dictionary<Type, int>(193)
            {
                // 中略
                { typeof(global::TestClass.TargetClass<global::GenericClass_0>), 30 },
                { typeof(global::TestClass.TargetClass<global::GenericClass_1>), 31 },
                { typeof(global::TestClass.TargetClass<global::GenericClass_2>), 32 },
                // 後略

こういう感じで、TargetClass<T> 項目の反映がなされていれば多分OKです。

追記(2021/01/26)

一応ですが、これはバッドノウハウに相当する可能性があります。

Unionを追加した状態でAOT Code Generation手順を忘れると
UnityエディタでDynamicUnionResolver(非IL2CPP環境で、対応コードを自動生成してくれるやつ)が
Union can only be interface or abstract class.というエラーを吐きます。
エラー表示通り、本来Unionはinterfaceとabstractクラスだけを対応するつもりのものっぽいですね。
後々の仕様変更で死ぬ可能性はあるので、その際はまた別途解決策を模索したほうがよさそうです。

Unityエディタで暫定回避をするための対応としては、下記みたいな方法があるんじゃないでしょうか。
- Union部分を#if ENABLE_IL2CPPとか#if !UNITY_EDITORで括って、発生を回避する
- 逆に何もせず、エラーを都度出して見落とさないようにし、AOTコード生成を都度行う運用でカバー

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