C++幼女先輩

プログラミング成分多め

UNITY Editor拡張で、タイムラインをスクリプトから生成する

お題

タイムラインを自動生成しよう
トラックやクリップも外から作成してみます
それに伴い、エディタ拡張の勉強

メニューよりEditorWindowを表示

とりあえず最も汎用的な?メニュの拡張から

メニューの拡張

Editor拡張で最も使われると思うので、よーく覚えよう

UnityEditor.MenuItem - Unity スクリプトリファレンス

MenuItemを使う
ドキュメントの通り、非常に機能が豊富なので細かい事は説明しません

まず、Editor専用コードなので、Editorディレクトリを作りその下にコードを置く事
そして、Editor拡張は静的関数しか呼べないので、そういう設計にする事

主な機能をざっと

メニューアイテムの追加

[MenuItem("hoge/fuga")]
のように属性をつけると、メニューに hoge->fuga と追加される
メニュー名に日本語も使えます
当然そのメニューを選んだ時に、その関数が呼ばれます
ホットキーも簡単に付ける事ができます
また、Validateもする事が出来ますし、メニューの表示順番も変更できます
Validate関数を設定すれば、関数がFalseを返すと無効、Trueを返すと有効になります

ヒエラルキー

MenuItemのパスの頭をGameObjectにすると、ヒエラルキーで右クリックした際のメニューに追加できます

    [MenuItem("GameObject/CustomEditor")]
    private static void Create()
    {
        Debug.Log("Menu");
    }

f:id:murasame-labo:20210618062518p:plain

ヒエラルキー右クリックでちゃんと表示されました

f:id:murasame-labo:20210618063104p:plain

もちろん、メニューの のGameObjectにも表示される

Assets

MenuItemのパスの先頭をAssetsにすると、Assetsビューのコンテキストメニューに表示される

    [MenuItem("Assets/CustomEditor", false, 1)]
    private static void Create()
    {
        Debug.Log("Menu");
    }

f:id:murasame-labo:20210618063228p:plain

もちろん、メニューのAssetsにも表示される

f:id:murasame-labo:20210618063546p:plain

CONTEXT

コンポーネントの 右上の3点アイコンを押したときのメニューを作れます!
もちろん、自作コンポーネントだけじゃなく、UNITYのデフォルトコンポーネントも可能です

    [MenuItem("CONTEXT/PlayableDirector/CustomEditor", false, 1)]
    private static void Create()
    {
        Debug.Log("Menu");
    }

今回はPlayableDirectorコンポーネントにメニューを追加しました

f:id:murasame-labo:20210618064111p:plain

今回使うもの

今回は、選択したGameObjectにPlayableDirectorコンポーネントを追加したいので
ヒエラルキー上のGameObjectのコンテキストメニューに追加する

コンテキストメニュー

public class CustomEditorWindow : EditorWindow
{

    [MenuItem("GameObject/CustomEditor", false, 1)]
    private static void Create()
    {
        // 生成
        GetWindow<CustomEditorWindow>("CustomEditor");

    }

ヒエラルキーのGameObjectメニューにCustomEditorを追加します

そして、CustomEditorWindow(自分のクラス)を呼び、GUIを表示させます
今回は EditorWindowを継承し、ウインドウを表示させます

EditorWindow

    private void OnGUI()
    {
        using (new GUILayout.HorizontalScope())
        {
            targetGo_ = Selection.activeGameObject;

            if (GUILayout.Button("CreateTimeline"))
            {
               // ここにボタン押したときの処理が入る
            }
        }
    }

とりあえずボタンが1個あるだけの単純なウインドウ

f:id:murasame-labo:20210618070129p:plain

以上でEditor拡張のおさらい終了

Timeline等をコードから生成

本題という事になる

GameObjectにPlayableDirectorのアタッチ

                if(targetGo is null)
                {
                    return;
                }
                // 選択中のGameObjectにPlayableDirectorコンポーネントをつける
                var director = targetGo.GetComponent<PlayableDirector>();
                if(director == null)
                {
                    director = targetGo.AddComponent<PlayableDirector>();
                }

AddComponentを使って普通にPlayableDirectorを作成する

TimelineAssetを作成しAssetデータベースへ保存する

TimelineAssetを作成します
つまり、タイムラインの情報すべてを保存するものです
ScriptableObjectで作成します

ただし、このままでは、シーン上に作られます
実際の運用では、タイムラインはファイル(Asset)にして読み込んで使うと思うので
AssetDatabaseに登録します
拡張子は playableです

                // TimelineAssetの作成
                var timelineAsset = playableDirector.playableAsset as TimelineAsset;
                if (timelineAsset == null)
                {
                    timelineAsset = ScriptableObject.CreateInstance<TimelineAsset>();
                    director .playableAsset = timelineAsset;
                }


                var path = "Assets/Resources/sample.playable";
                AssetDatabase.DeleteAsset(path); 
                AssetDatabase.CreateAsset(timelineAsset, path);
                AssetDatabase.SaveAssets();

AssetDatabaseの仕様で、ディレクトリが無い場合はエラーになるので、ディレクトリは事前に作っておく事
そして、既にAssetが存在する場合は上書きするとエラーになるので、事前にDeleteしておきます
SaveAssetは、即座にデータベースに反映するおまじないです

トラックの作成&バインディングを設定

今回はGroupトラックを作成し、そこにトラックを作成します
型は前回作った NewPlayableTrackです
Bindingにはゲームオブジェクトをつけます。今回はCubeという名前のゲームオブジェクトをつけます

                var groupTrack = timelineAsset.CreateTrack<GroupTrack>(null, "Group");
                var track = timelineAsset.CreateTrack<NewPlayableTrack>(groupTrack, "NewPlayable");

                var b = GameObject.Find("Cube");
                director.SetGenericBinding(track, b);

クリップ作成とKeyFrame作成

クリップを作成します。もちろん NewPlayableAssetで
そしてそのクリップにKeyFrame(AnimationCurve)を設定します

今回は最初が1fで、デフォルトのカーブでエンドが0fになる形で
もちろん、カーブのEaseを変えたり、KeyFrameを増やす事は容易

                var clip = track.CreateClip<NewPlayableAsset>();

                clip.CreateCurves(null);
                var curve = new AnimationCurve();

                curve.AddKey(new Keyframe(0f, 1f));
                curve.AddKey(new Keyframe((float)clip.duration, 0f));
                clip.curves.SetCurve("", typeof(NewPlayableAsset), "hoge", curve);

リフレッシュ

最後にリフレッシュ

                EditorUtility.SetDirty(timelineAsset);
                TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);

成果物

f:id:murasame-labo:20210618090053p:plain

とりあえず、目的の事は出来た

タイムラインをスクリプトから生成する

CreatePlayable周りの調査。AssetからBehaviourを、BehaviourからBindingを参照したい

CreatePlayable周りがちょっとややこしかったので調査
まず初めに断っておくが、本来はMixerを作るのがベストだが、今回はあえてMixerを作らずに頑張ってみる

ScriptPlayable

        var playable = ScriptPlayable<NewPlayableBehaviour>.Create(graph);
        var trackBinding = go.GetComponent<PlayableDirector>().GetGenericBinding(this) as GameObject;
 
        playable.GetBehaviour().target_ = trackBinding;
        return playable;

前回はこのようにしてBehaviourにバインディングを渡した
もちろん、単なるプロパティ経由なのでどんなものでも渡せる

ただし、この場合 PlayableAsset::CreatePlayableが呼ばれない そのため、PlayableAssetがBehaviourを知る方法がメンドクサイなど問題が生じる
なので、この方式を変更しようと思う

TrackAsset::CreatePlayable

BaseClassのCreatePlayableを呼ぶと、その中からPlayableAsset::CreatePlayableが呼ばれるようになる
具体的には

NewPlayableTrack

return base.CreatePlayable(graph, go, clip);

Assetの方は下記コードで、Behaviourの参照を手に出来る
templateという変数名にするのが通例のようだ
今回は template_というプロパティーを持たせる

    public NewPlayableBehaviour template_;

    public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
    {
        return ScriptPlayable<NewPlayableBehaviour>.Create(graph, template_);
    }

前にコード書いたときは上記で、template_にBehaviourの参照が渡ってきた覚えがあるが
コード間違えたかUNITYのバージョンか覚え違いか、Nullだったので変更した

    public NewPlayableBehaviour template_;

    public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
    {
        var playable = ScriptPlayable<NewPlayableBehaviour>.Create(graph);
        template_ = playable.GetBehaviour(); 
        return playable; 
    }

これで確実にtemplate_にbehaviourが設定される

上記で対応できると思ったが、Inspectorを作る時に不具合が生じる。今のところUNITY2020で問題が起こっている・・
UNITY2020では異なる方法で行う必要があるかもしれないので
今後は少し古いが UNITY2019.4での作業に変更

Bindingを渡したい

BehaviourにてトラックにBindingされたオブジェクトの操作をしたい事は多々ある
が、UNITYのTimelineはBehaviourから簡単にBindingが参照できない(ほんとこれは不便)
なので、どこかでBindingを渡す必要がある

まずBindingの取得方法だが、TrackAsset::CreatePlayable あるいは、PlayableAsset::CreatePlayableの引数の
GameObjectが、タイムラインのGameObjectになっていて
そこからPlayableDirectorコンポーネントを取得し、その中のBindingsから該当するトラックのBindingを検索する
具体的なコードは

        var trackBinding = go.GetComponent<PlayableDirector>().GetGenericBinding(key) as GameObject;

もちろんこのgoがGameObjectだが、問題はこのKeyである
Directorから何らかの方法でKeyを取得できるかもしれないが
ここはTrackAssetのthisを渡すのが簡単なので、TrackAssetから渡したい

結果

NewPlayableTrack

[TrackBindingType(typeof(GameObject))]
[TrackClipType(typeof(NewPlayableAsset))]
public class NewPlayableTrack : TrackAsset
{
    protected override Playable CreatePlayable(PlayableGraph graph, GameObject go, TimelineClip clip)
    {
        Playable playable = base.CreatePlayable(graph, go, clip);
 

        var trackBinding = go.GetComponent<PlayableDirector>().GetGenericBinding(this) as GameObject;
        Debug.Log("Track::" + trackBinding);

        var p = (ScriptPlayable<NewPlayableBehaviour>)playable;
        p.GetBehaviour().binding_ = trackBinding;

        return playable;
    }
}

NewPlayableAsset

public class NewPlayableAsset : PlayableAsset
{
    public NewPlayableBehaviour template_;

    public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
    {
        var playable = ScriptPlayable<NewPlayableBehaviour>.Create(graph, template_);
        return playable;
    }
}

以上で
PlayableAssetからは templateという名前でBehaviourが扱え
PlayableBehaviourからは binding
という名前でトラックにバインドされたオブジェクトが扱える

UNITYタイムライン PlayableTrackとBinding

PlayableTrack

プログラマとしては本題!
独自の処理をするトラックを作ります
用途はほんと色々あります
だいたい仕事では8割以上はこのカスタムトラックでタイムライン作るかも

Track、Asset、Behaviour、Mixerの4つで構成されます

PlayableTrack

カスタムのClipを入れる事のできるトラックです
UNITYから用意されているPlayableTrackをそのまま使う事も可能ですが
独自のバインディングやら初期化処理などを行う場合には自分で作ります
というか、自分で作りましょう

PlayableAsset

クリップ
実行時にインスタンス化するための情報を保持したりしている
実行時に下記のBehaviourをCreateする

PlayableBehaviour

インスタンス化したあとの実際のふるまいを定義する
カスタムトラックの肝

Mixer

上記のBehaviourの一種
クリップ同士(複数のBehaviour)をブレンドする事ができます

実際に作ってみる

Assets->Create->Playables
で、BehaviourとAssetのスクリプトのテンプレートが作成できる

f:id:murasame-labo:20210603160157p:plain

PlayableAsset

[System.Serializable]
public class NewPlayableAsset : PlayableAsset
{
    // Factory method that generates a playable based on this asset
    public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
    {
        return Playable.Create(graph);
    }
}

上記のような何もしないテンプレートが作成される

PlayableBehaviour

public class NewPlayableBehaviour : PlayableBehaviour
{
    // Called when the owning graph starts playing
    public override void OnGraphStart(Playable playable)
    {
        
    }

    // Called when the owning graph stops playing
    public override void OnGraphStop(Playable playable)
    {
        
    }

    // Called when the state of the playable is set to Play
    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {
        
    }

    // Called when the state of the playable is set to Paused
    public override void OnBehaviourPause(Playable playable, FrameData info)
    {
        
    }

    // Called each frame while the state is set to Play
    public override void PrepareFrame(Playable playable, FrameData info)
    {
        
    }
}

こちらは、コールバックのみのBehaviourが作成されました

BehaviourとAssetを連結させる

とりあえずPlayableTrackを作りクリップを作成する
今回はNewPlayableAssetという名前なのでそれを作成します

先ほどのBehaviourのコールバックにログを仕込んでも、まだ連結させてないのでコールバックが来ません
なので接続しましょう

AssetのCreatePlayableにて、今は汎用的なPlayableを作成していますが、それを自作のBehaviourにします

    public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
    {
        return ScriptPlayable<NewPlayableBehaviour>.Create(graph);
    }

これで、今回作ったNewPlayableBehaviourが呼ばれるようになり、ちゃんとコールバックが呼ばれるように

ScriptPlayable

    public struct ScriptPlayable<T> : IPlayable, IEquatable<ScriptPlayable<T>> where T : class, IPlayableBehaviour, new()
    {
        public static ScriptPlayable<T> Null { get; }

        public static ScriptPlayable<T> Create(PlayableGraph graph, int inputCount = 0);
        public static ScriptPlayable<T> Create(PlayableGraph graph, T template, int inputCount = 0);
        public bool Equals(ScriptPlayable<T> other);
        public T GetBehaviour();
        public PlayableHandle GetHandle();

        public static implicit operator Playable(ScriptPlayable<T> playable);
        public static explicit operator ScriptPlayable<T>(Playable playable);
    }  

IPlayableBehaviourを持つBehaviourをCreateしたり。
GetBehaviourとかGetHandleとかもしかして使うかもね

PlayableTrack

TrackAssetから派生する

[TrackClipType(typeof(NewPlayableAsset))]
public class NewPlayableTrack : TrackAsset
{
    protected override Playable CreatePlayable(PlayableGraph graph, GameObject go, TimelineClip clip)
    {
        Playable playable = base.CreatePlayable(graph, go, clip);
        return playable;
    }
}  

TrackClipTipeにカスタムのAssetを指定する
先ほどつくったPlayableTrackを削除し、今度は今作ったカスタムTrackを作成 そのトラック上で右クリックをすると、先ほど作ったNewPlayableAssetのクリップを作成できる

TrackBinding

カスタムトラックにオブジェクトをバインディングできます アトリビュートを追加

[TrackBindingType(typeof(GameObject))]
[TrackClipType(typeof(NewPlayableAsset))]

他にもトラックの色を変えたりアトリビュートがある
TrackBindingを設定すれば指定したオブジェクトに対して影響を与えるトラックが作れます
今回はGameObject型にしていますが、Animatorを設定したり出来る
TrackBindingにシーン上のCubeを指定しよう

f:id:murasame-labo:20210603202430p:plain

TrackBindingの取得

ここが非常に昔から不満点ですが
UNITYのTimelineではBehaviourからTrack情報を取得するのが難しいです

クリップの開始は OnBehaviourPlayのコールバックで取得しますがそのプロトタイプは

  public override void OnBehaviourPlay(Playable playable, FrameData info)

となっており、トラックの情報が取れません
なので以下の3パターンで取得する事になります

Behaviour#ProcessFrameで取得

ProcessFrameだけはプロトタイプが

public virtual void ProcessFrame(Playable playable, FrameData info, object playerData);

となっており、playerDataにBindingが入っているのでCastすればよいです

    public GameObject target;

    public virtual void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        target = playerData as GameObject;
    }

ただし、ProcessFrameは毎フレーム呼ばれる処理です。最初のフレーム時のみtargetを取得するように改造が必要ですが
そもそも初期化時に設定すればいいものを毎フレーム処理したくありません

Mixerを作りMixerのOnBehaviourPlay時に渡す

今回はMixerを作っていないので省略しますが
わりと見ますね
ただし、Mixerを作る必要があるのでMixer不要な場合は手間です

TrackのCreatePlayable時に渡す

今回はこれでいきます
Trackのコードを

    protected override Playable CreatePlayable(PlayableGraph graph, GameObject go, TimelineClip clip)
    {
        var playable = ScriptPlayable<NewPlayableBehaviour>.Create(graph, 1);
        var trackBinding = go.GetComponent<PlayableDirector>().GetGenericBinding(this) as GameObject;
        playable.GetBehaviour().target = trackBinding;
        
        return playable;
    }

CreatePlayableで渡ってきたGameObjectからPlayableDirectorを取得し、そこからBindingを取得して
作成したBehaviour(Playable)に渡す

これでOK

UNITYタイムラインをカスタマイズするよ

タイムラインとは

ムービーシーンとかカットシーンと呼ばれるもの
カメラワーク、モーション、エフェクト等を定義しておき、実行時にキャラをバインドする事で
同じ動作のムービーを別キャラ(別衣装)で行うことが出来る
例えばモンハンのムービーシーンのような、その時装備しているものと容姿でムービーを再生できる
そのような便利な機能がUNITYにはある

Timelineの作成

メニューの Windows->Sequencies->Timeline でタイムラインのタブを表示
f:id:murasame-labo:20210602143900p:plain

適切なGameObjectを作成しTimelineタブでCreateを押すと
Timeline.playable というファイルが作成される

と同時にPlayableDirectorのコンポーネントが追加される
f:id:murasame-labo:20210602154712p:plain

タイムラインの左側をクリックすると各種のトラックを作成する事が出来る
f:id:murasame-labo:20210602155523p:plain

動画編集などをした事ある人なら分ると思うが、各要素はトラックという単位で区切られ
例えば音声トラックには音声のクリップを複数配置して、タイムライン上でいつ再生するかを設定する
UNITYのタイムラインではさらに複数トラックをトラックグループという単位でまとめる事が可能

UNITYから準備されているトラックは6種類だが、自分でカスタムトラックを作る事が出来る
実際のゲーム開発では、独自のトラックを作る事がほとんどだと思うが
まずはUNITYが提供するトラックを紹介しよう

ActivationTrack

GameObjectの有効化、無効化を制御する
ActivationTrack を作成しタイムライン上で右クリックを行い、Add ActivationClipを選択すると
タイムライン上にActivationClipを作成できる
今回はテストにCubeオブジェクトを作りそれを少しして表示する

シーンにCubeを作りTrackにバインド(左側のNoneにCubeを設定)する
注意するのはこの時のGameObjectはシーン上にインスタンス化されたものを指定する必要があり
Prefab等のインスタンス化されたものは指定できない(後で説明するが、Prefab等を表示する場合は動的にBindingsを設定する必要がある)
クリップを調整して表示非表示のタイムラインを組み、Previewの再生をすると
Game画面にタイムラインのレンダリングが表示される

streamable.com

トラックにオブジェクトをバインディングしたら、PlayableDirectorのBindingsにも同じようにバインディングされています
(というか、同じものです)
f:id:murasame-labo:20210602164541p:plain
PlayableDirectorは再生時にトラックに適切なオブジェクトをバインドしたり、クリップをロードしたりアンロードしたり
全体の司令塔です(だからDirector)

シーンの方もクリップの設定に従ってオブジェクトのON/OFFが切り替わります
(もちろんTransformやらの値も、タイムラインに合わせて変化します)
で、タイムラインで使用するGameObjectは、タイムラインの子に追加すると管理が楽です
f:id:murasame-labo:20210602170544p:plain

AnimationTrack

AnimationTrackは、オブジェクトの移動や回転、アニメーションの再生等を行います
Animatorコンポーネントを持つオブジェクトをBindできます
トラックを右クリックで Add from AnimationClipで再生するアニメーションを選ぶ
当然トラックに複数のアニメーションを並べて連続再生する事も可能
クリップのつなぎ目をオーバーライドさせれば、アニメーションの補間もできます

ClipTransformOffset等を設定すれば、移動アニメーションも可能です
それらの動作に関しては、アニメーションカーブや、キーフレームを設定できます
今回はそのへんは行わず、適当なアニメーションを再生してみます

streamable.com
それっぽくなってきた

AudioTrack

音の再生を行うClipを作成します
動作は推測の通りなので割愛

ControlTrack

シーンあるいはPrefabからオブジェクトを有効化/無効化します
ActivationTrackに似てますが、こちらは主にParticle等を生成する時に使います
タイムライン上で右クリックで、Add from GameObjectします
シーン上のGameObjectを指定する事も可能ですが、今回はPrefabから直接生成します
何か適当なParticleを作成してPrefab化しておきそれを設定します
ParentObjectにシーン上のオブジェクトを指定すれば3D空間上のその位置にパーティクルを出す事が出来ます

f:id:murasame-labo:20210602194212p:plain

SignalTrack

任意のタイミングでイベントを発行する機能です
わりと最近のUNITYで追加されました
今度調べます

PlayableTrask

非常に有用な機能です
スクリプトを書いて独自のトラック、クリップを作成します
だいたいゲームの仕事でプログラマがやるべきは、このトラックの作成です
例えばブレンドシェイプをコントロールしたり、揺れもの、まばたき、目線
その他標準のトラックでは制御できない事をスクリプトで行います

非常に有用ですが、非常に複雑なためこちらは後程調査します

会社WebページをさくらVPSからAWS S3の静的ホスティングに変更

今まで
ドメインをお名前.comで
メールをお名前メール
Webページその他ホスティングをさくらVPS
にしていた

ただ、お名前メールのMXのサーバ名が不明だったので
お名前comからお名前メールにNSを転送してお名前メールの方でさくらに転送
という無駄な流れだった
今回、お名前メールのMXのURLも手に入れたため思い切ってS3にしてみた

結果は非常に簡単にできた
DNSも、お名前.comからNSをRoute53に飛ばし
Route53でAレコードを S3に飛ばし wwwサブドメインをCNAMEでS3に飛ばし
MXをお名前メールに飛ばせば動いた気がする

【UNITY 2020.2.6f1】Cinemachine FreeLookカメラでTPS操作をする。New InputSystemにも対応

Cinemachineとは

UNITYの提供するカメラ制御のコンポーネント

Cinemachineのインストール

PackageManagerよりインストールをする
その後メニューから Cinemachine-Cinemachine FreeLook を選ぶ
シーンに CM FreeLook1というGameObjectが作成されており、CinemachineFreeLookコンポーネントが付いており
MainCameraに、CinemachineBrainコンポーネントが付いている

f:id:murasame-labo:20210307031119p:plain

キャラクターをカメラが追従してほしいので
CinemachineFreeLookのFollowにキャラクターのGameObjectを、LookAtに注視点(キャラの頭とか)を設定する

このまま実行すると、Cinemachineが昔のInputを使っているため、New InputSystem を使っている場合はエラーになる
InputSystemに変更するには、CM FreeLook1 GameObjectにAddComponentで Cinemachine Input Providerスクリプトを付ける
これでNew Input Systemに変更されるのでエラーはなくなる
ただし、現状ではマウスの動きをキャプチャできないので、CinemachineInputProviderのXY Axisに Look Actionを設定する
f:id:murasame-labo:20210307214238p:plain

これで、キャラクターの動きにカメラが追従し、マウスでカメラを回す事ができる

ただし、このままではキャラの向いている方向に動くので
例えばキャラが右を向いている時に上を押すと右に歩き
もちろんそういう操作のゲームもあるが、今回はカメラの向きでキャラが動くように
つまりキャラがどの方向を向いていても上を押すとキャラがカメラからみて奥へ動くようにする

そのために、カメラからみたforwardベクトルとrightベクトルがワールド座標でのベクトルに変換するため
TransformDirectionを使いワールド座標に変換し
それにキー入力のx,zを掛ければ求まる(もちろんy成分は無視する)
また、移動方向を向くためにLookAtを使う
もちろん、キャラ座標で計算したり最適化は出来るがとりあえずいいだろう

            Vector2 m = move.ReadValue<Vector2>();
            // カメラの方向で移動させる
            Vector3 forward = playerCamera.transform.TransformDirection(Vector3.forward);
            Vector3 right = playerCamera.transform.TransformDirection(Vector3.right);
            moveDir = (m.x * right +  m.y * forward) * speed;
            moveDir.y = 0;

            // 進行方向を向く(今のところ補完入れてない)
            transform.LookAt(transform.position + moveDir);

コメントの通り補完は入れていないけど、今後はCharactorControllerを別のアセットに置き換える可能性もあるので
とりあえずここはこれで。

これで、カメラの回転とキャラ移動が思い通りになった

あとは、横に歩いている時に少しずつカメラが回って正面を向くようにするカメラが最近多いので
Cinemachineでも、BindingModeを変更すればそのあたりの挙動かわるようだ(よくわかってない)

f:id:murasame-labo:20210307215936p:plain

とりあえず今までのまとめ(まだゲーム内容決めてないけど)

www.youtube.com

【UNITY 2020.2.6f1】New Input System(1.0.2)を使ってみる

New Input Systemについて

新しい入力が出来ました
引き続き旧来の入力方法を使う事が出来ますが
これを期にInputSystemを覚えようと思います

旧来の方法

旧来のInputではInputManagerのAxes
下記のように入力を取得していました

h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
j = Input.GetButton("Jump");

New Input Systemを使う

細かいことはほかの人のブログで・・

まず、PackageManagerからInputSystemをInstallします
メニューのWindows PackageManagerから PackageManagerを起動し
左上のリストから、Packages: Unity Registryを選び、 Input Systemを検索
今回は最新の1.0.2 を選択しInstallします
ちなみに、Samplesからサンプルのパッケージをインストールする事もできます

f:id:murasame-labo:20210306183011p:plain

インストールを終えたら、New Input Systemを使うように変更します
ProjectSettingsのPlayerSettingから、Active Input Handlingを Input System Package(New)に変更します
そうするとUNITYエディタが再起動します

f:id:murasame-labo:20210306183340p:plain

これで、InputSystemが有効になり、旧来の入力が効かなくなります

InputSystemを設定する

InputSystemの設定は少し面倒です

まず、右クリックから Create-InputActionを行い、InputActionAssetを作成する
ダブルクリックを行い、ActionMaps、Actionsと、入力キーのぶんだけ作成します
例えば、移動はxzの2軸なのでMoveという名前で Vector2で作成します
キーボードだとWASDと矢印キーにそれぞれUp、Down、Left、Rightを設定
他にもGamePad、Touch、Joystick、XRも設定する
FireはActionTypeはButtonで、マウス左クリックやゲームパッドの右トリガーなどに割り当てる
そのほか使うボタンを登録します

f:id:murasame-labo:20210306184209p:plain

そしてそれを登録すればよいのですが
めんどくさいので UNITYが用意したものを使います

PlayerInputコンポーネント

動かしたいオブジェクトに、AddComponentから PlayerInput を追加します
すでに基本的なキー入力のAssetが設定されているので、非常に手抜きができます
DefaultMapをPlayerにし、BehaviorをSendMessagesにする

f:id:murasame-labo:20210306190525p:plain

実際に値を取得する時は下記のようになる

    public void OnMove(InputValue value) => move = value.Get<Vector2>();
    public void OnLook(InputValue value) => look = value.Get<Vector2>();
    public void OnFire(InputValue value) => fire = value.Get<float>() > 0;
    public void OnJump(InputValue value) => jump = value.Get<float>() > 0;

このように、Onアクション名 というCallbackで指定したアクションが取れます
コールバックで処理するのが面倒だなー、従来のようなポーリングで取りたい場合は
上記のようにコールバックで値を変数に保存してもいいですが
PlayerInputには、ポーリングをするインタフェースも用意されてます

    // InputActionを定義
    Private InputAction move, jump;

    void Start()
    {
        PlayerInput playerInput = GetComponent<PlayerInput>();

        // 各アクションの参照を取得
        move = playerInput.actions["Move"];
        jump = playerInput.actions["Jump"];
    }

    void Update()
    {
        Vector2 m = move.ReadValue<Vector2>();  // 2軸の値はVector2で取得
        bool j = jump.ReadValue<float>() > 0;  // bool値はfloat値で 0より大きいかで判断する
    {

これで、ポーリングでもイベントでも取得が可能です