お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

ml-agentsでRayを飛ばして観測データを得る方法(RayPerceptionSensorComponent3D)

以下のページの「Raycast Observations」の項に簡単な説明が書いてあったのでメモ。
RayPerceptionSensorはバージョン0.12.0からのもので、このドキュメントはバージョン0.15.0時点でのもの。
ベータ版のものだというのと、誤解している箇所があるかもしれないので注意。
github.com

RayPerceptionSensorComponentとは

ml-agentsにおいて、ゲーム空間上の観測データを得る方法の一つとして用意されているもの。
使用方法は簡単で、RayPerceptionSensorComponent3D(2Dの場合はRayPerceptionSensorComponent2D)をAgentオブジェクトにアタッチするだけ。
このコンポーネントはゲーム空間上にいくつかのRay(設定によってはSphere)を飛ばし、検出されたものを観測データとしてAgentに送る。

RayPerceptionSensorComponentの設定

Detectable Tags

観測対象とするオブジェクトのタグをここで列挙する。

Rays Per Direction

飛ばすRayの数を定義する。
正面にはデフォルトで1本Rayが飛ばされるようになっていて、その左右にここで定義された本数ずつRayが飛ばされる。
したがって実際に飛ばされるRayの本数は (1 + Ray Per Direction * 2) 本になる。
例えばここに3がセットされてあれば、飛ばされるRayの数は7本となる。

Max Ray Degrees

一番外側のRayと中心のRayとの角度を表す。
90度が指定されていればちょうど左右にRayが照射されることになる。

[Rayの飛び方サンプル]

Rays Per Direction : 3
Max Ray Degrees : 90
正面を0度として-90度から30度ごとに90度まで7本Rayが飛んでいる。
f:id:spi_8823:20200320192057p:plain

Sphere Cast Radius

Rayの代わりにSphereで観測データを得る場合のSphereの半径。
0がセットされている場合はRayを使用する。
多分だけど、Rayの方向にポンとボールを投げてボールに当たったものを観測するというイメージっぽい。
Rayを使ったほうが(特に複雑なシーンでは)効果的らしい。

Ray Length

Rayを飛ばす距離

Observation Stacks

スタックに保持しておく過去の観測データの数。
例えば3を指定すれば過去3回分の観測データがAgentへのインプットとして用いられるっぽい。
Behaviour ParametersのStacked Vectorsとは独立していると書いてある。

Start Vertical Offset (3Dのみ)

Rayの照射元の位置のY方向オフセットを定義。

End Vertical Offset (3Dのみ)

Rayの照射先の位置のY方向オフセットを定義

[Length・Offsetで決定されるRayの飛び方]

f:id:spi_8823:20200320195804p:plain

[Agentへのインプットとして送られるRayデータの数]

(Observation Stacks) * (1 + 2 * Rays Per Direction) * (Detectable Tags + 2) 個
それぞれのRayに対して(タグの個数+α)個のfloat値があって、検出されたタグに応じた位置のfloatにその物体までの距離が入れられてるはず(未確認)
タグの個数にプラスアルファしているのは何も当たらなかった場合などにも対応するため(未確認)

Tips

  1. 相対的な位置を気にする必要があるようなケースでRayPerceptionSensorを使うと効果的
  2. 学習の安定性やパフォーマンスのを気にするなら、タグの個数や飛ばすRayの本数は必要最小限にしたほうがよい

その他

RayPerceptionSensorを複数個アタッチすればその分だけ観測データとして使える。
Agentスクリプトと一緒にアタッチされるBehaviour ParametersUse Children SensorsがONになっていれば子オブジェクトのRayPerceptionSensorも使用することができる。
子オブジェクトのTransformをうまい具合に変更してやればAgentオブジェクトから離れた位置からRayを飛ばしたり、Rayを飛ばす方向を変えることも可能。


以上。

MLAgents(Version 0.14.0~)インストール方法

インストール方法

バージョン0.14.0からMLAgentsがUnity Packageを通してインストールするようになってた。
(サンプルプロジェクトは従来通りProject/Assets/ML-AgentsフォルダをUnityに持ってくるだけでよい)

1. 最新のソースコードのzipをダウンロードする

以下のページの一番新しいバージョンを落としてくる
github.com

2. zipを解凍する
3. Unityで「Window」→「Package Manager」を開く
4. Package Managerから「+」→「Add package from disk...」を選択
5. ファイルダイアログで解凍したソースコードの「com.unity.ml-agents」フォルダ以下にある「package.json」を開く
6. Packagesに「ML Agents」がインストールされる(パンパカパンッ!)

Unity Packagesマジで便利だなあという感想。

Unityで自動実装プロパティにSerializeFieldをつけるだけでインスペクタにまともな名前を表示させる

追記

???
Unity 2019にしたら動かなくなったのでUnity 2020でしか動かないっぽい

出典元

この記事では9割9分9厘下記サイトに書いてある通りのことをやっています。
baba-s.hatenablog.com

[field: SerializeField]をつけただけで名前をまともに表示してほしい

出典元様のソースコードが非常に便利で重宝していたのですが、これに加えてやりたいことが2つ出てきました。

  • Attributeを1つで済ませたい
  • 変数名から自動でインスペクタ上の名前も表示してほしい

以上の要件を満たすためには次のようなスクリプトを書けばよいです。

using UnityEditor;
using UnityEngine;

#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SerializeField))]
public class FieldNameDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (!property.isArray)  //arrayでダメな理由は知らん
        {
            label.text = property.displayName;
        }

        EditorGUI.PropertyField(position, property, label, true);
    }
}
#endif

SerializeFieldのCustomPropertyDrawerを直接定義しています。
こう書いておけばあとはプロパティの定義のところで以下のように書くだけでインスペクタに自然に表示されます。

public class SampleBehaviour : MonoBehaviour
{
    [field: SerializeField]
    public float SampleProperty { get; private set; }
}

f:id:spi_8823:20200314004737p:plain

SerializeField様の挙動をいじるのはかなり危なそうで気が引けますが、手元ではチラリと試してみた限りでは問題なく動いてそうに見えます。
どこで弊害が出てくるかはわからないので使用は自己責任で。

「ASP.NET Core Blazor」入門ハンドブック(を目指す)その2(Razor構文編)

前回の記事

spi8823.hatenablog.com

さて、前回はBlazor用のテンプレートプロジェクトを作成してデバッグ実行してみるというところまでやりました。
しかしこれだけでは何のありがたみも面白味もありません。
この記事では「BlazorによってどのようにHTML内にコードを埋め込むことができるのか」を説明します。

得られる知識

  • 「Razor構文」についての初歩的な知識
  • ボタンのイベントをC#によって記述する方法
  • C#の繰り返し文を利用してHTML要素を複数表示させる方法

前提知識

こういう技術的記事は前提とする知識をあらかじめ言っておくのがいいらしい。

  • C#で「for文」とか「if文」とか基本的なロジックを書くことができる
  • HTMLで文字列を表示したりボタンを表示することができる
  • JavaScriptでHTMLで表示したボタン押下時の処理を書くことができる
  • Webアプリケーションを作りかけたことがある

これくらいでしょうか。
この記事に興味がある人ならばだいたい大丈夫だと思います。

Razor構文

HTML内にC#を埋め込むための文法を「Razor構文」と呼びます。
この「Razor構文」というものはもともとかなり前からC#のWEBアプリケーションフレームワークである「ASP.NET」というものに含まれていたものです。
ただしこの時はRazor構文をあらかじめサーバー側で解釈してHTML+javascriptとしたものをブラウザに送っていたという程度のものだったようです(詳しくは知らないがまあ今となってはどうでもいいでしょう)

普通のHTML文書の中に「@」で始まるコードを書くとそれがC#のプログラムだと解釈されていろいろやってくれます。
例えば以下のように書くことができます。

@for(var i = 0;i < count;i++)
{
    <p>@i 番目の文字列</p>
}

このコードを見てもらうとだいたい雰囲気をつかんでもらえると思いますが、「@for」から始まる文がC#の繰り返し文で、その中に「<p>@i 番目の文字列</p>」といったようにHTMLを記述することができます。
さらにそのHTML要素の中でもC#の変数である「@i」という書き方ができるのもわかります。

このように、BlazorではRazor構文を使ってHTMLの中にC#コードを埋め込むことができます。
ちなみにBlazorというのも「Web」+「Razor」から来た造語らしいです。

Razorファイル

Razor構文はRazorファイルという「*.razor」という拡張子のついたファイルに記述する必要があります。
(「*.cshtml」というファイルも存在しますがこれはレガシーだという話をどこかで見た気がする)
この「*.razor」というファイルを一つのクラスとして考え、Razor構文ではそのメンバ関数やメンバ変数などを記述していくという解釈をすると考えやすいでしょう。

以下にこの記事で説明したい事項を詰め込んだRazorファイルを記述します。

例文

f:id:spi_8823:20191101010338p:plain
Sample.razor

見ての通りなんの感情も入っていないコードですが、以下ではこの例文をもとにしてRazor構文を見ていきます。

1行目「@page "/sample"」

「@page」で始まる記述のことを「pageディレクティブ」と呼んでいるようです。
このように書いて実行すると、例えば「localhost:12345/sample」というアドレスからこのRazorファイルによって記述されたページにアクセスすることができます。(12345の部分は環境に応じて変化します)
すなわち、pageディレクティブではページのルーティングを設定することができます。

2行目「@using System.Collections.Generic」

この行は通常のC#の「using」と同じことをしています。

4~13行目「@code { ~~ }」

この部分のように先頭に「@code」と記述し波かっこでくくったブロックによってメンバ変数やメンバ関数を定義することができます。

13行目で「StateHasChanged();」としていますが、これはもとからRazor構文内で利用することのできる関数で、ページ内の変数などが書き換えられたことを明示的に通知しページの再描画を促します。
これを記述せずとも自動で更新されることもありますが、どのようなタイミングで更新されるのかは把握できていないです。(おそらくボタンクリック時に更新するようになってる?)

15行目「

この部分ではHTMLのbutton要素を表示しています。
そして「@onclick = "AddText"」という部分ではボタンがクリックされた時のイベントにあらかじめ定義しておいた「AddText」という関数を設定しています。

17~20行目「@foreach { ~~ }」

この部分ではC#のforeach文を使ってメンバ変数である「Texts」の要素をそれぞれpタグによって出力しています。
「Texts」の中身はボタンクリック時に「AddText」関数で追加されていきますから、それと同時に行数が増えていきます。

実行結果

このファイルがある状態でデバッグ実行し、「~~/sample」へアクセスすると以下のようなページが表示されます。
f:id:spi_8823:20191101013322p:plain

ボタンを数回クリックすると以下のようになります。
f:id:spi_8823:20191101013056p:plain


次の記事ではRazor構文によって記述したものを別のRazorページから利用するRazorコンポーネントについて触れる予定です。

「ASP.NET Core Blazor」入門ハンドブック(を目指す)(インストール編)

Blazorとは

とにかくBlazorで「Hallo World」まで

この記事ではインストールから初めのデバッグ実行までを順を追って案内します。

Visual Studio 2019の最新版をインストール

visualstudio.microsoft.com
こことかからダウンロードしてインストールしましょう。
同じVS2019でもバージョンが古いものは対応していないので最新版に更新しましょう。
下の図のように「ASP.NETとWeb開発」の項目を含めてインストールすることに注意してください。
というかこの際「.NET Core」に関連するものは全部入れちゃったらどうよ?
f:id:spi_8823:20191024232914p:plain

「.NET Core 3.0」をインストール

dotnet.microsoft.com
.NET Core 3.0のランタイムをこことかからダウンロードしてインストールしましょう。
Visual Studioが正式に対応した今は必要ないかもしれません(未確認)。

「Blazor」プロジェクトを作成する

Visual Studio 2019を起動し、新しいプロジェクトの作成を選択
f:id:spi_8823:20191025000324p:plain

「新しいプロジェクトの作成」画面で「Blazorアプリ」を選択
f:id:spi_8823:20191025000545p:plain

プロジェクトの保存場所を指定する画面が表示されるので適当なプロジェクト名をつけて作成
f:id:spi_8823:20191025000656p:plain

「新しいBlazorアプリを作成します」と表示されるので、「Blazorサーバーアプリ」を選択して作成。
下の「Blazor WebAssembly」と書かれた2つはこの記事が執筆された時点では正式なリリースではないので無視してかまいません。
f:id:spi_8823:20191025000845p:plain


ここまですれば自動的に実行できるプロジェクトが作成されます。

デバッグ実行

いよいよ実行して動いているのを見て感動しましょう!
図で示した「IIS Express」と書かれたボタンをクリックしましょう。
f:id:spi_8823:20191025001412p:plain

すると……?ブラウザが立ち上がり、見事「Hallo, world!」と出力されたページが表示されています。
f:id:spi_8823:20191025001710p:plain

さて、このページがどこで書かれているかを少し覗いてみましょう。
プロジェクトの「Pages」というフォルダを見ると「Index.razor」というファイルが作られています。
f:id:spi_8823:20191025001850p:plain
このファイルに先ほど出力されていた文字列が記入されています。

これがブラウザで表示された「Hallo, world!」の正体、というわけです。
しかしこれだけではまだC#らしいことは何もしていません。
次の記事ではHTML内にC#を記述するための「Razor構文」について見ていきたいと思います。

次の記事
spi8823.hatenablog.com

EntityFrameworkCoreで整数とか日付とか以外のオブジェクトをフィールドに持つクラスをデータベースで管理したいときにやること

やりたいこと

例えば商品(Product)をデータベースで管理したくてかつ商品が会社(Company)の情報を持っていて欲しいとき。
つまりはクラスの入れ子になっているクラスをデータベース管理したいとき。
具体的に書くとこんなの

//商品クラス
public class Product
{
    public int ID { get; set; }
    public Company Maker { get; set; }
}

//会社クラス
public class Company
{
    public string Name { get; set; }
    public string Location { get; set; }
}

できない理由

これをこのままデータベースにマイグレーション(dotnet-ef migrations add hogehoge)しようとしても次のように怒られると思う。

The entity type 'Company' requires a primary key to be defined.
// Companyに主キーが無いぞ!出直してこい!!

これはCompanyというクラスを別のテーブルに突っ込んで管理しようとしてるからだ(と思う)が、わざわざそんなことしてくれんでもええのに……となる。

解決方法

要は別のテーブルにではなくProductのテーブルに突っ込んでくれればすむ話である。
EntityFrameworkCoreにはそのように指定する方法があって、次のようにすればよい

using Microsoft.EntityFrameworkCore;

public class Product
{
    public int ID { get; set; }
    public Company Maker { get; set; }
}

//この属性をつける
[Owned]
public class Company
{
    public string Name { get; set; }
    public string Location { get; set; }
}

親のテーブルに突っ込みたいクラスに「Owned」という属性をつけているだけである。
これを使うにはNugetとかでMicrosoft.EntityFrameworkCoreを入れておく必要がある。
こうすることによってProductのテーブルに「Maker_Name」とか「Maker_Location」とかのカラムが追加されてそこからMakerが読み取られるようになる。
Makerを別のテーブルに突っ込んでたりもしないので「Companyに主キーが必要だ!」とかも言われずにデータベースを作ることができる。