simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:InputSystemのInputActionを使ってみた

前書き

まだ半年早いですが、これから作るゲームは新しい InputSystem で作っていきたいと思います。
ドキュメント読んで動作確認までできたので実装コードを記録しておきます。

以下、自問自答した内容です。

Input System の InputAction でキー押下イベントを利用

「スペースキーを押したらゲームオブジェクトの表示非表示を切り替えるコード実装見せて」

「以下の通りです」

PressSpaceKeyText.cs

using UnityEngine;
using UnityEngine.Experimental.Input;

public class PressSpaceKeyText : MonoBehaviour
{
    [SerializeField]
    GameObject menuPanel;
    [SerializeField]
    GameObject titlePanel;

    public InputAction inputAction;
    
    void Awake()
    {
        inputAction.performed += ctx => {
            menuPanel.SetActive(true);
            titlePanel.SetActive(false);
        };
    }

    public void OnEnable()
    {
        inputAction.Enable();
    }

    public void OnDisable()
    {
        inputAction.Disable();
    }
}

「なるほど、インスペクタの方で Space Key を指定する感じですね?
インスペクタビューを見せてください」

「こちらです」

f:id:simplestar_tech:20190106163115j:plain
上記スクリプトをアタッチしたオブジェクトのインスペクタ

「新しい Input System の導入方法は?」

※この記事を書いて数日後に Package Manager 経由でインストールできるようになりました

「以下の Git を Clone して、Packages フォルダの com.unity.inputsystem をコピーして Editor を再起動します。」
github.com

「OK、細かいところは Git のページを見よう」

おわりに

こういう、欲しい情報をピンポイントで答えてくれる AI ほしいなぁ

Unity:動的NavMeshの確認

ゲームの3大AI(人工知能)の一つはナビゲーションAI

ゲームAIを大きく分類すると次の三つがあることに気づけます

  • キャラクターAI
    • キャラの振る舞いを決定
  • ナビゲーションAI
    • 目的地への経路を決定
  • メタAI
    • ゲームの進行を決定

今回強く関係しているのはナビゲーションAIです。

キャラクターが行動できる範囲を Walkable (歩行可能) エリアと呼びますが、次の動画の水色の半透明な領域が計算によって求めた Walkable エリアです。

f:id:simplestar_tech:20190105172931g:plain
動的NavMeshの動作の様子
f:id:simplestar_tech:20190105173305g:plain
動的NavMeshの動作の様子2

ゲームプレイ中に動的に Walkable エリアが更新されていることが確認できました!
これ本当にすごいことを目の当たりにしている瞬間でして、長いこと Unity では静的にしか Walkable エリアを計算できませんでした。
要するに、ゲーム実行前のビルドの段階で Walkable エリアを作り込むことしか選択肢がなかったのです。

計算が非常に複雑な経路計算がゲーム実行中にできる
それは単にナビゲーションが動的に作成した地形に適用できるという話では収まりません
あらゆる知的計算は経路探索に置き換えることができます。
例えば、今日この記事を書いている私の頭の中は、様々な行動プランを選択できる状態の中、この記事を書き始めるべきだと経路を作って、その経路に従って行動しています。
つまり、キャラクターAIのプランニング機能を動的 NavMesh 生成処理で実現できます!
(そんな気づきを得て興奮できる人がいるかは別として…)

Unity での具体的な実装方法

まずは次の Unity マニュアルを最後まで読む
docs.unity3d.com

実装手順だけ簡単に示します。
ドキュメントに書いてある通りこの動的 NavMesh 機能は標準の Unity エディターインストーラーには 含まれていません

次の GitHub のページを Git Clone して、Assets 以下の NavMeshComponents フォルダをあなたの Unity プロジェクトの Assets 以下へ配置します。
github.com

NavMeshSurface コンポーネントが利用可能になるので、シーン内の空オブジェクトに次のように追加します。

f:id:simplestar_tech:20190105191205j:plain
NavMeshSurface コンポーネントを追加した GameObject のインスペクタ

もう一つ、ユーザースクリプトがゲームオブジェクトに割当たっていますが、こちらの実装は定期的に Bake ボタンを押すというものです。
以下の実装になっています。

NavMeshSurfaceBaker.cs

using System.Collections;
using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshSurface))]
public class NavMeshSurfaceBaker : MonoBehaviour
{
    void Start()
    {
        _surface = GetComponent<NavMeshSurface>();
        StartCoroutine(TimeUpdate());
    }

    IEnumerator TimeUpdate()
    {
        while (true)
        {
            _surface.BuildNavMesh();

            yield return new WaitForSeconds(5.0f);
        }
    }

    NavMeshSurface _surface;
}

実装の意味は、単に 5秒ごとに NavMesh を焼き直すというもの

計算に利用するCollier メッシュオブジェクトの集め方はいくつか用意されていて
All ですべてのオブジェクト、Volume で指定した範囲内のオブジェクトだけ、Child で Transform の子オブジェクトだけ、を使って焼き直しが走ります。
レイヤーマスクも指定できるので、なるべく簡素なメッシュを用意してそれに割り当てたレイヤーを利用すると 5秒に一回の計算量のスパイクを低くできます。

エージェントの操作

動的 NavMesh が作れても、経路計算の方法がわからなければ、宝の持ち腐れですね。
NavMeshAgent クラスを活用して経路計算を行ないますが、そちらの知識を確認したい場合はこちらの記事が分かりやすいのでどうぞ
nopitech.com

エージェントが宙に浮いちゃった!どうすれば良い?

f:id:simplestar_tech:20190105192908j:plain
NavMeshSurface を利用すると中空を歩いてしまう…

私の対処としては AgentBaseHeightCorrector.cs を次の実装で作り、キャラクターオブジェクトに適用しました。

using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshAgent))]
public class AgentBaseHeightCorrector : MonoBehaviour
{
    void Start ()
    {
        _nav = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        CorrectBaseHeight();
    }

    private void CorrectBaseHeight()
    {
        NavMeshHit navhit;
        if (NavMesh.SamplePosition(transform.position, out navhit, 10f, NavMesh.AllAreas))
        {
            Ray r = new Ray(navhit.position, Vector3.down);
            RaycastHit hit;
            if (Physics.Raycast(r, out hit, 10f, LayerMask.GetMask("Level")))
            {
                _nav.baseOffset = -hit.distance;
            }
        }
    }

    NavMeshAgent _nav;
}

※実装ヒントは次の討論からもらいました。
https://forum.unity.com/threads/navmesh-surface-bakes-slightly-above-the-surface.508532/


毎フレーム処理が CPU リソース的に勿体ないなら、上の TimeUpdate の妙技を使ってみるのもありだと思います。

おわりに

うわぁ、ここまで書いてから類似するテラシュールブログさんの記事の存在に気付く…
ていうか自分スター押してる…忘れていたが正解でした!

tsubakit1.hateblo.jp

ただ、読み直した感じ、いくつか現在の実装とは異なる部分が見られたので
本記事は新しい NavMeshComponents の動作確認を行ったという意味があったかも

Unity:改造方法の確認

前書き

ゲーム開発者会議などで、ゲームユーザーの100人に一人はゲームを改造して不正を行っていることが明らかになっています。
Unityを利用して企業や個人でゲームをリリースしていますが、それらのゲームが容易に改造されなように対策されていることを確認することは
その後の楽しいゲーム体験が損なわれないことを保証するうえで重要なことです。

例えば、不正に報酬を得るプレイヤーにランキング上位を独占されたり、対戦時に不当な負け方を強いられたりしたら、そのゲームを遊びたくなくなります。
そんなわけで、今回は Unity 製ゲームの改造方法の一つを学び、自分のゲームが対策されていることを確かめる手段を覚えてみます。

ゲームロジックの記述されているファイル

こちらの記事を参考にします。
baba-s.hatenablog.com

直近で作った VRM ランタイムロードの改造が可能か確認することを目的に具体的な作業を記録していきます。
Windows ユーザーに届けるように作ったパッケージは複数のファイルで構成されています。

f:id:simplestar_tech:20190105132428j:plain
Build指定先のフォルダの様子
ゲーム名_Data フォルダを開いてみましょう。
f:id:simplestar_tech:20190105132518j:plain
ゲーム名_Data フォルダの様子
ハイライトしたように Managed フォルダがあります。
この中の Assembly-CSharp.dll ファイルにゲームのロジックが記録されています。

.dll ファイルの中身の確認

ゲームロジックが記録されている Assembly-CSharp.dll の中身を確認するツールを手元に用意しましょう。
GitHub - 0xd4d/dnSpy: .NET debugger and assembly editor
こちらのリリースの最新版を取得して起動します。

追記:次の記事で紹介されている ILSpy というツールを使う人も多いみたい
dot-sharp.com


File > Open メニューから Assembly-CSharp.dll を開いて、中身を見てみます。

f:id:simplestar_tech:20190105134957j:plain
GameManager クラスの実装を表示している様子

すべてのゲームロジックがユーザーに公開されてしまっていることがこの確認操作でわかりました。

改造してみる

右クリックメニューから Edit Method が選べたので、こちらでLoadするファイル名を Vita から sendagaya_xiv に変更しました。
右下の Compile ボタンを押して成功したので、File > Save All で保存確認ダイアログが出るので OK を押して上書きします。

以上…

ゲームを起動すると、全く違うキャラクターをロードしてゲームが始まってしまいました。

f:id:simplestar_tech:20190105143716j:plain
改造後にゲームを起動すると Vita ではなく違うキャラがゲーム内に登場した

おわりに

PlayerSettings でMono から IL2CPP に切り替えると、ビルド時間は伸びますが難読化にはなる様子
ですが、オンラインゲームのような不正を行うと他のプレイヤーに害が及ぶようなケースでは何らかの対策を入れて、可能な限り不正の芽は摘んでおきたいところですね。

追記:そのマインドでこれを読むと理解や覚えが早くなるとか

www.slideshare.net

難読化についてはこちらを参考にしてアセットを買いました
【Unity】Unity 製の PC ゲームにおける逆コンパイル・改造の方法と難読化による対策を紹介 - コガネブログ

Tool:動画ファイルから連番画像ファイルを出力

機械学習の教師データとして特定のアニメの絵柄や色を利用できるものがほしい
具体的には Youtube などの動画を切り取って連番画像ファイルにするプログラムとかキャプチャソフトの使い方を探している

簡単に調べたところ次のダウンロードページ「iWisoft Free Video Converter」を発見
www.free-codecs.com

簡単な説明はこちらのページを参照しました。
iWisoft Free Video Converter のダウンロードと使い方 - k本的に無料ソフト・フリーソフト

日本語化もできるそうで、それを行ってから起動した画面がこちら

f:id:simplestar_tech:20190105095914j:plain
VideoConverter 起動画面

動画は Windows なら Win + G ボタンでゲームとして Youtube のブラウザを認識させて、録画ボタンで動画ファイルとして用意できます。
用意した動画をドラッグ&ドロップして認識してくれました。
Profile の項目を jpeg 形式にして、画像サイズを縮小するように設定して出力開始すると
ディフォルトで次のパスに連番jpg画像ファイルを出力しました。
C:\Users\simpl\Documents\iWisoft Free Video Converter

f:id:simplestar_tech:20190105103214j:plain
出力先の様子

当初の目的をクリアしていますので、記録はここまで

記録頻度を変更できる

Settings ダイアログにて fps の項目があり、ここで 1fps を選ぶと、動画を 1秒ごとにスライスした連番画像を出力できました。
動きの少ないアニメなどはこうした低FPSの設定で目的に合った学習データが揃えられそうです。
とても便利

Unity:VRMのランタイムロードがなんもわからん

こちら「2019年なんもわからんカレンダー」1月3日の記事です。

前回の記事は
Kazushige Mori(Cz_mirror) (@Cz_mirror) | Twitter さんによる「SmartRig Bipedなんもわからん。。」でした。

cz-mirror.hatenablog.com

前書き

自分だけの3Dキャラクターをあらゆるゲームに登場させるという、全人類の悲願(笑)がまさに今日成就しようとしています。
ウソだと思うあなた、次の VRoid Hub のサイトの文句をちょっと調べてみてください。

>いま、あなたのキャラクターが動き出す。
hub.vroid.com

  • 自慢の3Dキャラを3分でおひろめ。
  • 登録したキャラで遊べちゃう。
  • お気に入りがなければ、自分ですぐに作れます。

ということで VRoid Hub で公開されている VRM (3Dキャラクターデータフォーマット)をゲーム実行後にダウンロードして読み込ませる方法を確かめたいと思いまして
なんもわからん状態から調べて完全に理解した状態まで来たので、実装詳細を記録しておきます。

VRM を Unity ゲームで読み込むには?

VRM - dwango on GitHub 公式ドキュメントにざっと目を通し、次の UniVRM を使うと良いことが確認できました。
github.com

そんな UniVRM を使って VRM をランタイムロードするには?と調べるとドンピシャな記事を見つけました。
qiita.com

こちらの記事の内容で完全に理解できてしまうのですが、本記事では Unity 初心者にもわかるように詳細に作業ログを残します。

Unity Hub から Unity を起動

簡単な操作ですが、Unity Hub は比較的新しいツールなので、Unity ベテランの方でも使ったことがない人もいます。
今回は Unity 2018.3.0f2 を使い、空のプロジェクトを作成して起動しました。

UniVRM パッケージをインポート

次の UniVRM の README に従って最新のUniVRM-0.xx.unitypackageファイルをダウンロードして、Unityにインポートします。
GitHub - dwango/UniVRM: Unity package that can import and export VRM format

Humanoid適当アニメーションデータを用意

Unity の公開アセット(無料)の中から Unity ちゃんの歩行アニメーションをダウンロードして、Unityにインポートします。
unity-chan.com

ブレンドツリーのアニメーションコントローラを作成

こちらの過去記事で紹介している WalkRun ステートを持つ AnimatorController が用意できればゴールです。
simplestar-tech.hatenablog.com

ランタイムロードのコードを記述

GameManager.cs

using System.IO;
using UnityEngine;
using VRM;

public class GameManager : MonoBehaviour
{
    [SerializeField]
    GameObject m_target;
    [SerializeField]
    RuntimeAnimatorController m_animCtrl;

    private void Start()
    {
        LoadModel("C:/Users/simpl/Downloads/Vita.vrm");
    }

    void LoadModel(string path)
    {
        if (!File.Exists(path))
        {
            Debug.LogError("file not found path = " + path);
            return;
        }

        Debug.LogFormat("{0}", path);
        var context = new VRMImporterContext();
        var file = File.ReadAllBytes(path);
        context.ParseGlb(file);
        context.Load();
        context.ShowMeshes();
        context.EnableUpdateWhenOffscreen();
        context.ShowMeshes();
        SetModel(context.Root);
    }

    void SetModel(GameObject go)
    {
        if (go != null)
        {
            var lookAt = go.GetComponent<VRMLookAtHead>();
            if (lookAt != null)
            {
                go.AddComponent<Blinker>();

                lookAt.Target = m_target.transform;
                lookAt.UpdateType = UpdateType.LateUpdate; // after HumanPoseTransfer's setPose
            }

            var animCtrl = go.GetComponent<Animator>();
            if (animCtrl && animCtrl.runtimeAnimatorController == null)
            {
                animCtrl.runtimeAnimatorController = m_animCtrl;
            }
        }
    }
}
GameManager のインスペクタを設定

上記コードを空のオブジェクトに割り当てて、先ほど「mecanimのイロハ」で作った AnimatorController をインスペクタで設定します。

f:id:simplestar_tech:20190103182659j:plain
GameManager のインスペクタビューの様子

VRoid Hub から利用OKなVRMを取得

今回は VRoid Project が提供しているヴィータ.vrmをダウンロードして手元に用意しました。
hub.vroid.com

コードで期待しているパスにリネームして配置してあげます。
今回はコード内容からダウンロードフォルダに置くのが正解でしたね。
具体的にはこちら "C:/Users/simpl/Downloads/Vita.vrm"

Play モードで動作確認

ゲームを起動すると Start 関数が初回に呼ばれるので、このときにダウンロードフォルダにある Vita.vrm を読み込みます。
読み込み終えたゲームオブジェクトに AnimatorController が割り当たるので、次の動画のように歩くアニメーションを確認することができます。

f:id:simplestar_tech:20190103184255g:plain
Vira.vrm ロード結果インスタンスにAnimatorController適用結果

まとめ

UniVRMを使ってVRMのランタイムロードを行って、そのインスタンスブレンドアニメを適用して期待通り歩行~走行するモーションを確認しました。
それを行う細かい手順を具体的に記録して公開しました。

もっと深く調べたい人へ

私の貧弱なサンプルでは不満な方も多いと思いますので dwango さんが用意しているVRMの公式 Unity サンプルをご確認ください。
github.com

次の日の記事はこちら

lilea.net

AI:「作って動かすALife」本を読んでの感想

読んだ本の情報

作って動かすALife
――実装を通した人工生命モデル理論入門
www.oreilly.co.jp

著者の一人に、次の対談に出てくる池上高志さんがいたので気になってました。
boundbaw.com

ALife 人工生命の入門書として書かれています。

個人的なとらえ方によるざっくりとした内容

1. ALife とは?
これまでの抽象的な生命論はやめて、実験的な数学を通して具体的に生命について理解を深める研究のこと
2. 生命のパターンを作る
蝶の羽模様や魚や動物の体表の模様、貝殻のパターン模様などが単純な数式で再現できる例を示す(Gray-Scottモデル)
3. 個と自己複製
個の定義も難しいがそれは置いておいて、ビット配列の遺伝子パターンを読み取って自分と同じ形を複製する2D世界のマシンを作り、これが無限に増殖していくものは考え出されていて、実際コンピュータで計算できることも示されている。(しかし、とても計算量が多くて時間がかかる)突然変異が遺伝子の一部を壊し、自己の複製ではなく変異種を複製し始め、様々な個体が生まれ、時に元の個体を複製するように戻ったりすることが観測できた。
4. 生命としての群れ
Bird-oid と書いて鳥っぽいもの群れとして Boids と呼び、よく ECS などで群のシミュレーションを行うアレの話。巨大で複雑にしないと進化が起きないという話もあったかも
5. 身体性を獲得する
認知・判断・行動と直列で構築したAIはアホすぎて、簡単な目標もクリアできなかったが、「障害物を見つけたら避ける」「何もなければ適当に動き回る」「目的地を見つけたら近づく」という反射的行動、迷った時の行動、目的意識を持った行動を階層化し、より高い意識層が下位の反射層を制御することで、複雑な目標をクリアでいることを示した。
6. 個体の動きが進化する
遺伝的アルゴリズムの・選択・突然変異・交叉の例を示して、生命ってのは自然淘汰の中での単なる多様性を生み出しているだけなのかもねって話
7. ダンスとしての相互作用
相互に影響し合うことがなければ飽きが来る、同じことを繰り返していても飽きが来る、この飽きが安定状態から不安定状態へと遷移させ、似たりよったりの中から新しいものが生まれる進化が巻き起こる
8. 意識の未来
やはり知能は内部でイメージを持ち未来予測や期待をして、実際に観測した情報との統合を行っていて、この時間の流れの中に意識があると考える。
一見無駄とも思える冗長な未来予測が創造につながり、こうした予測と行動選択は現実を生きるために不安定でなければならず、これが曖昧で定義できない意識なのかもしれない。

個人的な感想

私は特に文献を漁らずに AI について思ったことを書いてきました。(隙あらば自分語り…)
たとえば、ちょうど 2年前の記事
AI的な調べもの - simplestarの技術ブログ
>人工知能はマイクロワールドから出られなかった
>AI に身体性を持たせる必要がある
>その身体性をマイクロワールドの中で与える試みを行おうと思う

それからマイクロワールドの開発を進め
計算に時間がかかりすぎるので Unity ECS を学んでいたのが最近までのこと

このマイクロワールドのAI作りで役に立つ知見が得られるかもしれないとこの本を手にしました。

普段考えていたことの言語化ができるようになった気がします。
例えば、飽きがこないゲームとは、相互作用があり、それが安定しないことである
とか、冗長な未来予測の中で創造や発見が生まれ、知的に振る舞うエージェントを表現できる、など

そのほか、認知・判断・行動の直列をやめて、並列化して本能と理性の階層化により、理性で本能を抑えると知的に振る舞うという新たな知見を得ました。

これから AI をプログラムに落とし込むときに役立つ知見が一点増えた気がしました。

Unity:Mathematics Noise 動作イメージをつかむ

前書き

Unity は Mathf という一般的な数学関数ライブラリをGameObjectの計算に提供してきました
docs.unity3d.com

今後の高速化案件の Burst Compile が必要になるケースでは新しい Mathematics という数学ライブラリを使わなくてはならなくなりました。
github.com

Unity.Mathematics
A prototype of a C# math library providing vector types and math functions with a shader like syntax. Used by the burst compiler to compile C#/IL to highly efficient native code.

Mathematics の中で個人的に noise 関数がいくつか気になっています。
関数名だけ眺めても動作イメージがつかめないので一通りどういうノイズなのか調べることにしました。

読者のみなさんにも確認結果を共有します。

情報ソース

noise を作成するコードやコメントは先ほどの Git の次のページから確認できました。
Unity.Mathematics/Noise

この記事で確認したい関数は次の通りです

  • float2 cellular(float2 P) // Worley ノイズ (Steven Worley 作 in 1996年頃)
  • float cnoise(float2 P) // クラシック Perlin ノイズ( Ken Perlin 作 1980年代)
  • float snoise(float2 v) // Simplex ノイズ (Ken Perlin 作 in 2001)
  • float3 psrdnoise(float2 pos, float2 per, float rot) // Simplex ノイズ with Rotating gradients and analytical Derivative.

順に使っていきましょう。

cellular noise

参考にした解説
thebookofshaders.com

snoise とは?

Simplex noise
Simplex noise - Wikipedia

Ken Perlin designed the algorithm in 2001

Perlin さんが提供したn次元のノイズ生成アルゴリズムの名称
次元数を増やすことができ、計算量も少ないという改善版

Perlin ノイズは旧アルゴリズムという扱いでした。(初めて知った)

cnoise は Perlin ノイズ

classic noise の略だそうな

psrdnoise

よくわかりませんでした。

追記:作りたかった記事見つけた。

gametorrahod.com