【Unity】Playable と Timelineについて

これまで触ってなかったUnityの「TimeLine」について調べる機会がありました。 その中で「Playable」についてブログに書いていきたいと思います。

Playable API

調べるとUnityには「Playable API」と呼ばれるものがあるのがわかりました。 これは、アニメーションを処理Animator Controllerとかで使われている内部処理を行っているのがPlayable Apiです。

Animation Controller

個人的にAnimator Controllerも余り触ったことないのですが、次の画像のようにStateでAnimationを管理しています。

f:id:albatrus:20210707121036p:plain
Animator ControllerのWindow

PlayableGraph

PlayableGraph を使用すると、複数のデータソースをミックス、ブレンド、変更し、1 つの出力として再生できます。 例えばAnimator Controllerで行うことのできるAnimationの再生やブレンドなどが実装できます。Animationだけではなく、Audioや独自に定義したScriptなどを組み合わせることができます。

また、Scriptで生成ができるので、ゲーム再生中などで動的に作成することができます。ですので、AnimatorControllerのように事前にAssetを用意しておく必要はありません。

Playable API - Unity マニュアル

PlayableGraph visualizer

PlayableGraphのPlayableがノードベースで確認できるWindowです。 PackageManagerからインストールできます。

GitHub - Unity-Technologies/graph-visualizer: Visualizer for your Playable graphs

f:id:albatrus:20210707125454p:plain
PlayableGraph Visualizer

PlayableGraphの生成
var playableGraph = PlayableGraph.Create()
PlayableGraphの再生
playableGraph.Play();

Playable

playable は IPlayable インターフェースを実装する C# 構造体です。 Playableを組み合わせてPlayableGraphを作っていきます。

主なPlayable
  • AnimationClipPlayable
  • AnimationMixerPlayable
  • CameraPlayable
  • TextureMixerPlayable
PlayableGraphにPlayableを紐付ける

playableを生成するときに、PlayableGraphを紐付けます。

var animationClipPlayable = AnimationClipPlayable.Create(playableGraph, clip);

PlayableOutput

PlayableGraphはPlayableOutputを使って出力を行います。 出力形式に応じてOutputクラスが違います。

主なPlayableOutput
  • AnimationPlayableOutput
  • AudioPlayableOutput
  • ScriptPlayableOutput
  • TexturerPlayaleOutput
PlayableGraphにPlayableOutputを紐付ける

playableOutputを生成するときに、PlayableGraphを紐付けます。

var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Name", animator);

簡単な例

AnimationClipを再生するPlayableGraphです。

// 生成
_playableGraph = PlayableGraph.Create();
        
// PlayableGraphに PlayableとOutputを紐付ける
var animationClipPlayable = AnimationClipPlayable.Create(_playableGraph, _clip);
var animationPlayableOutput =
AnimationPlayableOutput.Create(_playableGraph, "Animation", GetComponent<Animator>());
        
// 出力設定
animationPlayableOutput.SetSourcePlayable(animationClipPlayable);

// 再生
_playableGraph.Play();

f:id:albatrus:20210708123532p:plain
シンプルなPlayableGraph

Playable詳細

visualizerの中でノードをチェックすると詳細を見ることができます。 AnimationClipの場合、Timeが渡されています。このTimeを元にアニメーションを再生しています。

f:id:albatrus:20210708123739p:plain:w300
Timeのところが更新されている

PlayableとTimeline

最後に本来調べていた項目であるTImelineとの関係です。 まず何もないTimelineをVisualizerで見ると次のように表示されます。

f:id:albatrus:20210708130257p:plain
空のTimeline

Visualizerに表示されるということはTimeLineはPlayableGraphで構成されていそうです。

Trackを追加してみる

試しにActivation TrackをTimelineに追加をしてみると、PlayableGraphの構成が変わりました。

f:id:albatrus:20210708151808p:plain
Activation trackを追加した場合

挙動だけを見ると「ActivationMixer」と「Playable」が追加された感じです。

複数追加すると

次にもう一つActivation Trackを追加してみます。

f:id:albatrus:20210708152301p:plain:w400
Trackが2つの場合

TimelineのTrackごとにOutputが設定されます。 今回は同じTrackを追加したのでOutputはScriptOutputになっています。

TimelinePlayable

Outputする前にかならずTimelinePlayableを通っています。 TimelinePlayableでは、 TrackとそのClipを担当しているPlayableを作成してリンクする役割をしています。

docs.unity3d.com

最後に

今回調べてみて、Playableについてなんとなくその概要がわかってきました。 次はTimelineで使われているPlayableを拡張周りをどうやってやるのかや、実際にPlayableを使ったらないができるかなどが気になりましたので、その辺り調査したいと思っています。

【C#】クラスをDeep Copyする

ローカルに保存したデータクラスを変更した場合、その参照先のデータも変更されます。 例えば、Saveボタンがあり、ボタンを押したときだけデータクラスを変更したい場合には、変更前の値をどこかに保持しなくてはいけません。

単純に次のように別の変数を用意しても、クラスが参照型のため、currentInfoが変わった時、prevInfoも変わってしまいます。

// HogeInfoはデータクラス
HogeInfo prevInfo = currentInfo

この場合、クラスをDeepCopyをすれば問題が解決できます。

DeepCopy

DeepCopyを行うときには Activator.CreateInstance()を使い、新たにクラスインスタンスを作成します。

次に、Copyをする対象の変数を取得し、コピー元のあたりを使って上書きます。

public static HogeInfo DeepCopy(HogeInfo self)
{
    // インスタンスを作成
    var instance = Activator.CreateInstance(typeof(HogeInfo), true) as HogeInfo;
    var type = self.GetType();

    // データクラスはプロパティで構成されているのでプロパティを取得しています。
    var properties = type.GetProperties();
    foreach (var property in properties)
    {
        property.SetValue(instance, property.GetValue(self));
    }
    return instance;
}

Activator.CreateInstance

var instance = Activator.CreateInstance(typeof(HogeInfo), true) as HogeInfo;

docs.microsoft.com

第2引数ではコンストラクタを公開しているかどうかのフラグです。

今回していない場合はtrueを選択します。 また別途パラメーターがあるコンストラクタを定義している場合には、パラメーターなしのコンストラクタを用意する必要があります。

public HogeInfo()
{
}

置き換え処理

今回の例ではプロパティの値を置き換えていました。

var properties = type.GetProperties();
foreach (var property in properties)
{
     property.SetValue(instance, property.GetValue(self));
}

プロパティ以外にも取得する方法があります。 例えばInstanceされたprivate変数を取得する場合は次のような記述ができます。

// private変数の場合
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var field in fields)
{

}
BindingFlags

BindingFlagsはこちらを参考に。 docs.microsoft.com

使用例

DeepCopyは次の用に利用します。

HogeInfo prevInfo = HogeInfo.DeepCopy(currentInfo);

これで変更前の値を保持したローカルクラスを作成できます。

【Unity】クラス同士の比較を行いたいときに IEquatableを使う

とあるデータクラスを作ったときに、その中身が同じかどうかを比較をしたい場合にIEquatableを使って比較するのが良いと教わったので、実装をしてみました。

IEquatable

このインターフェイスは等価かどうかを判断するEqualsが定義されています。 継承したクラスではEqualsを使って判定を行います。

 public interface IEquatable<T>
  {
       bool Equals(T other);
  }

docs.microsoft.com

実装

たとえば次のような情報のデータクラスがあるとします。

public class HogeInfo : 
{
    public string Id {get; private set;}

    public int Value {get; private ser;}

    public HogeInfo(string id, int value)
    {
        Id = id;
        Value = value;
    }
}

このクラスにIEquatableを使って等価判定用のメソッドを用意します。

public class HogeInfo : IEquatable<HogeInfo>
{
    public string Id {get; private set;}

    public int Value {get; private ser;}

    public HogeInfo(string id, int value)
    {
        Id = id;
        Value = value;
    }

     public bool Equals(HogeInfo other)
     {
        if (other == null)
        {
            return false;
        }

        return Id.Equals(other.Id) &&
               Value.Equals(other.Value);
    }
}

比較する

実際に等価比較を行う場合に定義をしてEqualsを使います。

var info1 = new HogeInfo("hoge", 100);
var info2 = new HogeInfo("hogehoge", 100);
var info3 = new HogeInfo("hoge", 50);
var info4 = new HogeInfo("hoge", 100);

bool isEqual1 = info1.Equals(info2);  // false
bool isEqual2 = info1.Equals(info3);  // false
bool isEqual3 = info1.Equals(info4);  // true

最後に

当初はIEquatableを使わずに専用の判定メソッドを用意していたのですが、C#の振る舞い的に等価判定はEqualsを使っているのでそれを踏襲した形で対応をしました。

ニーアレプリカントをクリアした

少し前になるのですが、ニーアレプリカントをクリアしました。

ニーアレプリカント

本作はPS4版でリメイクされた作品ですが、私は過去作をやっていません。 ただ続編のニーアオートマタはクリアしているので、順番が逆にPlayをしたことになります。

ストーリー

ニーアシリーズには複数のEDが用意されているのですが、今回は全てのEDを見ることができました。 EDごとに少しずつではあるのですが、別視点からの会話やムービーが入ったりするので、EDの回数を重ねるごとに物語の背景がわかってきます。 最後のEエンドに関してはリメイクで初めてゲームとして追加されたシナリオで、このEDに関してはニーアオートマタをやっていると少し懐かしいような描写もあり良かったです。

ただEエンド以外は基本同じことの繰り返しになるのでそのあたり辛いですが、Eエンドにまでの道のりと考えれば許せる範囲だなと思いました。 ニーアオートマタが終盤話がよくわからなくなってきたのに対して、レプリカントに関してはちゃんとストーリーを追えていたと思います。

戦闘

リメイク前の作品をやってことがないので、過去との比較はできないのですが、操作性はオートマタによく似ています。 中盤からとても硬い敵が出てくるあたりかなりストレスになっていたのですが、これはEDの周回を重ねて自身が強くなっていく想定の敵のようで、これをうまく乗り切ればそこまで躓くことはないと思います。 武器も3種類使えるので攻撃のバリエーションも考えながら遊べるのも良かったです。

最後に

38時間くらいでEエンドまでクリアできたので、まあまあのボリュームでした。 最初のEDまでは20時間かからなかったと思いますので、それを加味するとお手軽に遊ぶことができるのではないかと思います。 ニーアシリーズは雰囲気がすきなので、今後の次回作や過去作のリメイクが今後あってほしいなと思っています。

ゼルダ無双 厄災の黙示録クリアした

先月くらいから遊んでいたゼルダ無双を最近クリアしました。 簡単ですが遊んでみた感想などを書いていきたいと思います。

ゼルダ無双 厄災の黙示録 -Switch

ゼルダ無双 厄災の黙示録 -Switch

  • 発売日: 2020/11/20
  • メディア: Video Game

無双シリーズ

無双シリーズは本作品以外にもたくさん出ているのですが、私はゼルダ無双で初めて遊びました。 ですので以前の作品との比較はできないのですが、楽しく遊べました。

基本、細かな操作は必要とせずにタイミングよくボタンを押していけば敵を倒すことがでるのですが、 ステージで登場するボス的な敵に対しては考えて操作をしなくてはいけません。 ただ、その際にも適切なヒントが表示されるので、そこまで難しくはありませんでした。

ストーリー

ストーリーに関してはネタバレになるので書きませんが、「ゼルダの伝説BtW」とは違った展開になり、話も完結します。 ですので、「ゼルダの伝説BtW」をやっていなくても十分に内容は楽しめるはずです。

ゼルダの伝説 ブレス オブ ザ ワイルド - Switch

ゼルダの伝説 ブレス オブ ザ ワイルド - Switch

  • 発売日: 2017/03/03
  • メディア: Video Game

ボリューム

私はエンディングはとりあえず見たのでクリアしたのだと思っているのですが、まだできることがたくさんあります。 クリアするだけでも100時間くらい時間を使っていたので、ボリュームは結構あります。 また、追加コンテンツも有料配信されるので、全てやろうとすると相当な時間がかかりそうな気がします。

最後に

ゲーム性は違うのですが、ゼルダの世界観がとても楽しめました。 ストーリー的にはこの作品のほうが個人的に好きです。

ゼルダの伝説BtW」の続編も楽しみですね。

NETFLIXの本を読んだ

連休中なのですが、前から積んでいたNETFLIXの本を読みました。 こちらの本です。

内容

よくある企業本は会社の歴史を振り返ったり、創業者(またはCEO)のこれまでの歩みを伝記として書かれたりすることが多いのですが、今回の本は、NETFLIXがどうやって大きくなっていったかではなく、NETFLIXの企業文化について書かれています。 ですので、この本を読んでもNETFLIXが過去何をしていた、今どのような事業をしているかはわかりません。

個人における最高水準の報酬を払う

この本の中で自分が1番心に響いたのがこの社内ルールです。 平凡な社員を10人ではなく、一人の優秀な人材のほうが良い成果を生むことができるという考えはNETFLIXだけではないと思います。 自分もエンジニアとして働いている中で、周りが優秀な人が多いこともあり、ゲーム業界で生き残っていくために常にLvを上げ続けないといけないと再認識させられました。

最高水準の報酬

同業他社基準で設定されているので、人を雇用するマネージャー的な人は、この報酬を常に把握しておかないといけないとあり、そのあたり大変だなと感じました。 当然他社はかんたんに報酬を公開しないので、ヘッドハンティング業者に探りを入れて聞くなどしないといけないので、結構水物の値になるなと感じました。 自分も今の水準が業界のどのあたりなのか知らないので、報酬以外にも会社を選ぶ基準はあると思いますが、聞いてみたいなと思いました。

ポジション

この本では、最高水準の報酬は常に変化すると書かれています。 今後どの分野に注力するかで必要なスキルは変わってくるので、需要が下がれば報酬が下がったり、または解雇されることもあります。 自分がやりたいことと会社に求められることが同じだと勉強もしやすいと思いますが、違ってくると修正をしていかないといけません。 会社だけでなく業界のニーズもあるので、今どの分野・スキルが必要になっているのかを把握しておく必要もありそうです。

情報はオープンにする

社員に当事者意識をもたせるために、様々な情報を公開しているのですが、この例え文言がすごくしっくりきました。

野球のルールも説明せずに、写真を試合に送り込んでいるような状況だ。社員は一塁から二塁へ盗塁をしようとするが、試合全体の流れがいまどうなっているかまるで理解していない

個人プレーではなくチームプレーが必要なので、ここの能力が高いだけではなく、今の状況を理解し、それを加味しどう動いていくかが大切だと感じました。 この本の別の項目であげられている「意思決定の承認を一切不要にする」も社員の当事者意識を上げる考えだと思いました。

日本人について

NETFLIXの日本支社についての話も記載があり、それはフィードバックなどのコミュにケーションについてでした。 詳しくは本を読んでいただきたいのですが、文化によって様々な背景があり、それらの人々と働くには「対話」をすることがとても大切だと書かれています。 たくさんの質問をし、好奇心を示すほど、異文化の相手にも適切なフィードバックを与えることができるとありました。

自分はそのような異文化でのコミュにケーションは余り経験がないのですが、同じ文化間での対話はとても大切なのだと感じました。

最後に

NETFLIXに関しては一ヶ月だけ視聴した経験があるので、サービス面についてそれほど詳しくないです。(ウィッチャーのドラマみるために契約していた) ただ、周りでNETFLIXを見ている人が多かったので、興味があり本を読んだのですが、結構考えさせられることが多い内容でしたので読んでみてよかったと思いました。

【読書】赤と白、我が人生を読む

ゴールデンウィークということで普段余りやらないことをやっているのですが、今日はサッカーのアーセン・ベンゲル監督の「赤と白、我が人生」を読みました。

アーセン・ベンゲル

ご存知の方は知っていると思いますが、今回読んだ著者のアーセン・ベンゲル氏は英国プレミアリーグアーセナルの監督を22年間努めたことで有名です。 私がアーセナルというチームを知ったのが2006年のチャンピオンズリーグ決勝を見た時でした。 このときはバルセロナが2-1で勝った試合なのですが、前半にレッドカードで10人になったアーセナルの戦い方がとても印象に残っていました。

UEFAチャンピオンズリーグ 2005-06 決勝 - Wikipedia

名古屋グランパス

ベンゲル監督は、日本の名古屋グランパスでも監督をされていた経験があり、実は日本にも馴染み深い人物です。 今回の本では日本で監督をする経緯なども書かれています。 天皇杯を優勝するなど、いくつかのタイトルを獲得していることなどしっかりと結果も出しています。 日本の選手に対してもとても好意的に捉えている感じで、ぜひ日本代表監督になってくれたらなと個人的に思っています。

監督論

本の内容は、自伝になるので幼少期から現在のFIFAの役員になるまでの半生が綴られているのですが、サッカー監督としてどうすればよいのかの「監督論」が多く語られていました。 これはサッカーだけではなく全ての責任者にも通じる話に感じました。

最後に

本の中では今後監督業はやらなさそうな感じでしたが、日本代表監督ではなくてもよいので、何かしらのチームを率いる姿を見てみたいなと思いました。