【Unity】VisualScriptingチュートリアル4

toshizabeth.hatenablog.com

第4回

概要

前回と同じく

www.youtube.com

こちらの動画を参考としたチュートリアルコードを書いていきます


前準備

前回はキューブオブジェクトを動かし、カプセルオブジェクトに接触した際に頭上のオブジェクトのアクティブ/非アクティブを切り替えるVisualScriptを記述しました
今回は「カプセルオブジェクトに接触した際に画面にテキストメッセージを表示する」をやっていこうと思います


  1. Canvasを一つ作成
  2. Canvas配下にMessageBoxという名前のGameObjectを配置
  3. その下にTextMeshProのGameObjectを配置

f:id:toshizabeth:20210920130701p:plain

メッセージウィンドウのように配置

f:id:toshizabeth:20210920144717p:plain

配置した TextMeshPro では日本語に対応したフォントを取り付ける必要があります
今回は 以下のサイト様のフォントを使用することにしました

jikasei.me

画面下部のダウンロードからフォントをダウンロードし、中にあるいずれかの xx.ttf ファイルをUnityに配置→読み込ませる
Window > TextMeshPro > Font Asset Creator からフォントアトラスを生成する

RenderModeはSDFAAで CharacterSetはCustomCharactersで Generate Font Atlas > 保存

f:id:toshizabeth:20210920144752p:plain

生成したフォントアセットの GenerationSettings を開いて AtlasPopulationMode をDynamicに,そして ClearDyanmicData にチェックを入れておきましょう
Staticのままではパフォーマンスが良いが使用する文字を自分で登録しなければならないので面倒くさい。
Dynamicの場合は実行中にフォントテクスチャを生成してくれるので楽

f:id:toshizabeth:20210920144916p:plain

これでTextMeshProに作成したフォントを取り付けると日本語が表示できるようになったと思います

ScriptMachineを取り付ける

MessageBoxにScriptMachineコンポーネントを取り付けます。
今回は条件がいらないのでScriptMachine。

ScriptMachine上からテキストを書き換えるために TextMeshPro のオブジェクトをScriptMachineに教える必要があります
なので Variables コンポーネントに Messageという名前で TextMeshPro を登録しておきます

f:id:toshizabeth:20210920145202p:plain

Editor Graph を押して実際にテキストを配置していきます。


しかし、このまま 右クリック > Add Node から TextMeshPro を探しても出てきません
VisualScriptingに登録されていないコンポーネントを使用するためには、TextMeshPro コンポーネントをVisualScriptingに登録する必要があります

Edit > ProjectSettings > Visual Scripting を開いて Node Library > +ボタン で TextMeshPro の DLL を登録。

f:id:toshizabeth:20210920145656p:plain

次に Type Options > +ボタン で TextMeshPro UGUI を登録

f:id:toshizabeth:20210920145731p:plain

TextMeshProのDLLをまずは登録して、その中にある TextMeshPro UGUI を登録します。


これで下にある Regenerate Nodes を押すと VisualScriptingでTextMeshProが使用できるようになります


TextMeshProのテキストを設定するのに必要なのは TExtMeshProUGUISetText ユニット

f:id:toshizabeth:20210920145856p:plain

全体図はこんな感じ

f:id:toshizabeth:20210920145905p:plain


  1. OnStart と SetTextをつなげる
  2. GetVariable の Object から Vairables に登録した Message を取得
  3. MessageとSetTextをつなげる
  4. String ユニットを貼り付けて表示したいテキストを入力してSetTextと結びつける


以上で、実行するとVisualScriptingに配置したテキストがデフォルトで登録されてあってテキストを書き換えて表示されます
条件を入れていないため起動時に速攻書き換えて終わり。

f:id:toshizabeth:20210920150122g:plain


【Unity】VisualScriptingチュートリアル3

toshizabeth.hatenablog.com

第三回

概要

先日、Unity公式のYoutubeチャンネルで素晴らしい動画が上がっていました

www.youtube.com

今回はこちらの動画を参考に、少しシンプルにしたものを手元で再現してみようと思います

実装

キューブとカプセルが配置されている世界で、キューブを手動で動かしカプセルに衝突したときに

カプセルの上に配置されたオブジェクトがアクティブ化。
衝突から離れたときに非アクティブ化。
を実現していきます


1. GameObjectの準備

Sceneに以下のオブジェクトを配置します

  • Cube : 動かす対象
  • Capsule : 衝突先のオブジェクト
  • Caution : カプセルの頭上に配置されたオブジェクト。衝突されたらアクティブ化される

f:id:toshizabeth:20210919192320p:plain

Cautionは最初非アクティブにしておきます


衝突判定に必要なため、

  • Cubeには BoxCollider と Rigidbody (UseGravityはOff)。そして判定に必要なため Player というタグを付ける
  • CapsuleにはCapsuleCollider (IsTriggerがON)

を付けておいてください


2. Cubeを動かせるようにする

キーボードの十字キーでCubeオブジェクトを前後左右動かします

   public class CubeController : MonoBehaviour
    {
        void Update()
        {
            if (Input.GetKey (KeyCode.UpArrow)) 
            {
                transform.Translate(0.0f, 0.0f, 0.1f);
            }
            
            if (Input.GetKey (KeyCode.DownArrow)) 
            {
                transform.Translate(0.0f, 0.0f, -0.1f);
            }
            

            if (Input.GetKey (KeyCode.LeftArrow)) 
            {
                transform.Translate(-0.1f, 0.0f, 0.0f);
            }
            
            if (Input.GetKey (KeyCode.RightArrow)) 
            {
                transform.Translate(0.1f, 0.0f, 0.0f);
            }
        }
    }
}

このような適当なクラスを作成してCubeに貼り付けちゃいましょう

f:id:toshizabeth:20210919192659g:plain



3. Capsuleに衝突したことを検知するGraphを構築する

Capsule に AddComponent から 「StateMachine」を付けます
New ボタンから適当にアセットを保存し、Edit Graph ボタンで編集に入ります

何もない場所で
右クリック > Create Script State を押して「接触状態の時の動き」を配置するユニットを作成します


f:id:toshizabeth:20210919193354p:plain

左上のタイトルを編集し

という名前をつけました。

接触状態と非接触状態が交互に切り替わるため、お互い Make Transition で相互につなげます

f:id:toshizabeth:20210919220749p:plain

接触状態 → 接触状態 の遷移条件

接触状態から接触状態になるためには「キューブがカプセルに接触した」つまり OnTriggerEnter が呼び出さればいいわけで
そのような状態を「非接触状態→接触状態」の条件に記載します (黄色の部分をダブルクリック)

  1. OnTriggerEnterユニットを置く
  2. if ユニットを置いて true と Triggeer Transition をつなげる

上記のみでは「Cube以外にあたったときも反応してしまう」ので、CompareTag を取り付けます。
これは Tag に記載したオブジェクトのみ反応してくれるユニットです。

今回はCubeにはPlayerというタグを付けました。
なので Compare Tag には Player というタグで反応してもらうようにします

f:id:toshizabeth:20210919220936p:plain

接触状態 → 非接触状態の遷移条件

先ほどとほぼ一緒ですが、 OnTriggerEnter ユニットではなく OnTriggerExit ユニットを取り付けます。
これで 「カプセルからキューブが離れたときに反応する」ようにします

f:id:toshizabeth:20210919221412p:plain


これでキューブからカプセルに接触したときに 接触状態の Script State に遷移するようになりました!

f:id:toshizabeth:20210919221823g:plain

接触状態のScriptStateにSetAcriveの処理を書く

これでオブジェクト同士の当たり判定で赤丸の接続状態に遷移するようになりました。

f:id:toshizabeth:20210919221954p:plain

次は
「 Caution : カプセルの頭上に配置されたオブジェクト。衝突されたらアクティブ化される」
を ScriptStateに記述していきたいと思います

赤丸の接続状態をダブルクリックして...
中に
接触中はCautionをアクティブに。非接触中はCautionを非アクティブに」
するユニットを配置していきます

その前に
Cautionオブジェクトのアクティブを切り替えるためには、Cautionオブジェクトを知っておく必要があります

カプセルオブジェクトにStateGraphを追加した時、合わせてVariablesというコンポーネントも追加されています。
簡単に説明すると、ここにオブジェクトを登録するとカプセルのStateMachineで参照できるようになる
ということです。

なので Variables に CautionCube という名前で Cautionオブジェクトをアタッチします

f:id:toshizabeth:20210919222543p:plain


これでStateMachine内でCautionオブジェクトのアクティブが切り替えられるようになりました
そして次の通りにユニットを配置します

  1. 接触状態になった時を知るためにOnEnterStateユニットを配置
  2. 接触状態になった時を知るためにOnExitStateユニットを配置
  3. それぞれ、SetActiveユニットを配置して、 Value に チェックあり、チェックなし版をつなげる
  4. GetVariableユニットを配置して、今回は「Object」からCautionCubeを選択してそれぞれ SetActive ユニットにつなぎ合わせる

f:id:toshizabeth:20210919222820p:plain


これで 接触、非接触状態になったときにSetActiveが切り替えられるようになりました。
GetVariable はその名の通り変数、オブジェクトを取得するユニットです。
Variableで登録したオブジェクトがここで参照できるようになります。

今回はカプセルのVariableコンポーネントにCautionを登録したため、Objectから選択するようになっています
(スコープ範囲による。今はスルー)


これで実行すると、キューブがカプセルに衝突すると、カプセルの頭上のCautionオブジェクトが表示されるようになります

f:id:toshizabeth:20210919223308g:plain


以上!

【Unity】VisualScriptingチュートリアル2

概要

以前の記事の続きです

toshizabeth.hatenablog.com

「Xが4移動したらCubeが動かなる」という実装をしてみたいと思います

実装

  1. ScriptGraphをダブルクリップして開く
  2. AddNodeから GetLocalPosition, If, LessOrEqual を配置
  3. GetLocalPositionから GetX を配置して LessOrEqual と結びつける
  4. LessOrEqualに4を入れる
  5. 各ユニットを結びつける

(途中からの動画ですが)

f:id:toshizabeth:20210919123335g:plain

各種ユニットの役目ですが

  • GetLocalPosition

CubeのLocalPositionを取得するユニット

  • LessOrEqual

Aの入力がBの値以下の場合 trueを返す


つまり、CubeのLocalPositionが4以上になったら If ユニットが false を返すようになるので Translate が働かなくなる

ということになります。


クライアントコードで書くと

void Update()
{
    var pos = cube.transform.GetLocalPosition();
    if (pos.x <= 4)
    {
        // 0.01動かす
    }
}

ということをやっているわけです

この記述は、「4動かしたら止まる」ではなく「4まで動かす」ですね(結果は同じですが)

f:id:toshizabeth:20210919123834g:plain


以上あっさりとしたものになりますが、これだけでも掴めるものがあると思います

【Unity】VisualScripting で "GUI Window tried to begin rendering " エラーが発生する

環境

  • M1 Mac
  • Unity 2021.2.0b12
  • VisualScripting v1.7.3

概要

VisualScripting の ScriptGraph上で 右クリック > AddNode を選択した際に

GUI Window tried to begin rendering while something else had not finished rendering! Either you have a recursive OnGUI rendering, or the previous OnGUI did not clean up properly.

が発生しました。

AddNode自体が失敗するので何もできない状態になりました

f:id:toshizabeth:20210919114916g:plain

原因

DisplayLink を利用してデュアルディスプレイ化をして開発していたのですが、

 ディスプレイ1 → UnityEditor  ディスプレイ2 → State Graph Editor

を配置しているときに、StateGraphEditor上で右クリックすると発生するようでした

つまり UnityEditor本体とGraphEditorを映しているディスプレイが違う場合にエラーが発生するようです。


同じ環境で作業される方は少ないと思いますが、2時間以上手間取って見つけたエラーなので同じことで困ってる方の手助けになれば..

VisualScriptingだけではなく他のEditorWindowでも同じことになる可能性はある為心に留めて開発していく

【Unity】VisualScriptingチュートリアル1

手っ取り早く動かしてみることを目標に。

「3秒後にxを2動かして止まる」

という挙動をとってみます


前準備

まず適当なオブジェクトをシーンに作成。

f:id:toshizabeth:20210917010405p:plain

このCubeに

を取り付けます

f:id:toshizabeth:20210917202419p:plain

次に今から作成するビジュアルスクリプトを保存する先を作成します


Inspecor上に New というボタンを押すと保存先を選択する流れになります

適当なフォルダに保存しちゃいましょう


このファイルはScriptableObjectとなっており、内部に情報を持ちます


編集

次に Edit Graph を押すと実際にビジュアルスクリプトを編集する画面が表示されます

f:id:toshizabeth:20210917204621p:plain


空きスペースを右クリックすると操作パネルが開きます。

  1. ScriptState を作成
  2. Startを右クリックして Make Transition
  3. StartからScript Stateをつなげる

f:id:toshizabeth:20210917211610g:plain

Startは開始地点

ScriptStateユニットは実際の動きを行う場所、例えばオブジェクトを回転させたり、移動させたりなど。

そして途中の黄色い部分は遷移するための条件を書く場所です


つまり「3秒後にxを2動かして止まる」を分解すると

 3秒後に というのは「黄色の部分」

 xを2動かす というのは「ScriptStateユニット」

が担当します


まずは「xを動かす」部分を記述します

  1. ScriptStateユニットをダブルクリック
  2. なにもない部分を右クリックして検索窓に「Translate」と入力して配置
  3. Xに0.01を入れる
  4. OnUpdateユニットとTranslateユニットをつなぎ合わせる

f:id:toshizabeth:20210917212642g:plain

この状態で再生しても何も起きません

開始していることをScriptStateに教えてないためです

なので次はScriptStateに問題なく遷移するための処理を追加します


  1. StartとScriptStateの中間にあったマークをダブルクリック
  2. 右クリックから「Start」といれて検索
  3. StartとScriptStateをつなぎ合わせる

f:id:toshizabeth:20210917233610g:plain

その後PlayModeに移行するとCubeがX移動します

f:id:toshizabeth:20210917234144g:plain

これはMonobehaviourで言うところの「 Startメソッドで、UpdateのたびにX移動するスクリプトをアタッチした」と同じ意味になります


次に「3秒後」という条件を追加します


  1. OnStartを削除してOnUpdateをつける
  2. if と検索して ifユニットを間に取り付ける
  3. GetTime と検索して 時間を取得するユニットを取り付ける
  4. GreaterOrEqual ユニットを取り付けて 「GetTimeした結果が3以上であれば if true を流す」ようにする

実行してグラフを見ると GetTime から値が流れ、3以上になった瞬間に TriggerTransition が起動する絵が見れます

f:id:toshizabeth:20210918101017g:plain


ここまでの実装で ある条件により動きの実行を制御する 事ができました

次はすでに動いている状態から「2動いて止まる」という事後条件を実装していこうと思います

UniRx UniTaskを利用してIObservableで通知が来るまでawaitする

UniRxで作成したIObservableを UniTaskを利用してとある演出シーケンスなどの一連の流れの中で使いたい。

async UniTask 何かしらの演出()
{
    // 演出A

    // UniRxから通知が来るまで待機したい

    // 演出B
}

UniTaskはIObservableをawaitできますが、待機終了する条件は 『OnComplete が呼ばれたとき。』

元々『UniTaskにSetNextが呼ばれるまでawaitする機能があれば出来そう。』と思っていましたが、「OnCompleteが呼ばれるまで」という条件でした


であればOnCompleteが呼ばれるようにすればいいわけで このときFirstオペレーターを利用できました

async UniTask 何かしらの演出()
{
    // 演出A

    // hoge先で OnNext() が呼ばれるまで await する
    await hoge.FugaObservable.First();

    // 演出B
}

Firstははじめの一度だけ通知を呼び出し、その後OnCompleteします。 つまり OnNext が呼ばれたあと First を通ることで await が終了するということ

でした。終わり


もしとある条件の通知が来た時のみ先に進ませたいのであればWhereを併用できます

async UniTask 何かしらの演出()
{
    // 演出A

    // hoge先で OnNext() が呼ばれるまで await する
    await hoge.FugaObservable
        .Where(x => x.IsOk)     // IsOK がtrueの時のみ進む
        .First();

    // 演出B
}

【C#】List<Value> からDictionary<Key, List<Value>> を作る方法

あるデータ構造クラス

class Data
{
    public HogeType Type;
    public int Value;    
}

そのクラスのリストがあるとき

List<Data> list;

このリストから

Dictionary<HogeType, List<Data>> dict

このような辞書クラスを作りたいとき。

ポイントはValue部分がListになっているところ。 つまり同じタイプを持つDataクラスをListにまとめたい。

Linqを使えばスマートにかけました

var dict = list
    .GroupBy(data => data.Type)
    .ToDictionary(group => group.Key, group => group.ToList());

listに対してGroupByでTypeでまとめてからToDictionary


あまりこういう書き方したことなかったので今更ですが。

Linq便利だなと再認識



全文

using System;
using System.Linq;
using System.Collections.Generic;

public static class C {
    
    public enum HogeType
    {
        A,
        B,
    }
    
    class Data
    {
        public HogeType Type;
        public int Value;    
    }
    
    public static void Main() 
    {
        var list = new List<Data>()
            {
                new Data { Type = HogeType.A, Value = 1 },
                new Data { Type = HogeType.A, Value = 2 },
                new Data { Type = HogeType.B, Value = 1 },
                new Data { Type = HogeType.B, Value = 2 },
                new Data { Type = HogeType.B, Value = 3 },
            };
        
        var dict = list
            .GroupBy(data => data.Type)
            .ToDictionary(group => group.Key, group => group.ToList());
        
        foreach (var pair in dict)
        {
            foreach (var data in pair.Value)
            {
                Console.WriteLine($"{pair.Key}, {data.Value}");
            }
        }
    }
}