ノートの端の書き残し

UnityやらC#やら。設計が得意かもしれない。

【補足】TrackやClipだけを対象にしたTimelineAction

前提

nigiri.hatenablog.com

本題

最後に補足するつもりでしたが書き忘れていました。 TimelineActionクラスを継承すると、選択中のクリップやトラックに対してのコマンドを定義できます。 選択中のクリップやトラックの情報はActionContextに包まれて渡されます。

でも、今はクリップしか興味が無いのにトラックの選択状態なんか調べたくない。もしくはその逆でトラックにしか興味がない、ということはあるかと思います。

そういう時はClipActionTrackAction、あとはマーカー用のMarkerActionを使えば、ActionContextじゃなくてIEnumerable<TimelineClip>などが渡されいちいち興味ない方のチェックを挟む必要が無く楽ができます。

こいつらも公式のAPIリファレンスにサンプル含め載っているので参考にしてみてください。

docs.unity3d.com

【Unity】Timelineエディタ拡張【TimelineAction】

作るもの

UnityのTimelineで、クリップやトラックを右クリックでできる操作を追加します。

環境

Unity2022.3.4f1
Timeline1.7.4

Timelineバージョン1.4.0以降なら問題無いです。

自作クリップ

Colorfloatを設定するようなクリップがあったとします。

ここで、あるクリップに設定されてるfloatの値だけを他の沢山あるSampleClipにコピペしたいな~と思ったとします。

シナリオ

実際にコピペする際の操作は、

  1. 1つだけSampleClipを選択し、右クリックでメニューを表示。コピーコマンドを実行すると、floatだけコピーしてどこかに保存する。
  2. 1つ以上のSampleClipを選択し、右クリックでメニューを表示。貼り付けコマンドを実行すると、コピーされたfloatがあれば選択されている全てのSampleClipfloatに貼り付ける。

というものになります。

TimelineAction

こういった、クリップやトラックに対する動作はTimelineActionというクラスを継承して作成できます。 さきほどのコピーのクラスは以下のようになります。

using System.Linq;
using UnityEditor.Timeline.Actions;

[MenuEntry("SampleClip/ValueCopy")]
public sealed class SampleClipOnlyValueCopy : TimelineAction
{
    public static float? ClipBoard { get; private set; }

    public override bool Execute(ActionContext context)
    {
        // バリデーションで、1つSampleClipが選択されていることは保証されてる
        var sampleClip = context.clips.First().asset as SampleClip;
        ClipBoard = sampleClip.Value;
        return true;
    }
    
    public override ActionValidity Validate(ActionContext context)
    {
        // トラックは選択されてなくて、
        // クリップは1つだけ SampleClip が選択されてたら表示
        // それ以外は表示しない
        var hasTracks = context.tracks.Any();
        var isOnlyOneSampleClip = context.clips.Count() == 1 && context.clips.All(clip => clip.asset is SampleClip);
        
        return !hasTracks && isOnlyOneSampleClip ? ActionValidity.Valid : ActionValidity.NotApplicable;
    }
}

Executeで選択されているトラックやクリップに対しての操作を定義し、
Validateで、コマンドを表示するか、非表示にするか切り替えます。
今はSampleClipにだけ行う操作なので、それ以外のクリップを右クリックした際には表示すらさせないようにNotApplicableとします。

MenuEntryAttributeでどんな名前で表示するかを決めます。

次に、貼り付ける方のコマンドは以下のようになります。

using System.Linq;
using UnityEditor.Timeline.Actions;

[MenuEntry("SampleClip/ValuePaste")]
[ApplyDefaultUndo("SampleClipValuePaste")]
public sealed class SampleClipValuePaste : TimelineAction
{
    public override bool Execute(ActionContext context)
    {
        var copyValue = SampleClipOnlyValueCopy.ClipBoard.Value;
        foreach (var clip in context.clips)
        {
            var sampleClip = clip.asset as SampleClip;
            sampleClip.SetValue(copyValue);
        }

        return false;
    }

    public override ActionValidity Validate(ActionContext context)
    {
        // トラックは選択されてなくて、
        // クリップはSampleClipだけ選択されてたら表示
        // クリップボードに値が無い場合は表示はするけど選べない
        var hasTracks = context.tracks.Any();
        var isOnlySampleClip = context.clips.All(clip => clip.asset is SampleClip);
        var hasValue = SampleClipOnlyValueCopy.ClipBoard.HasValue;

        if (hasTracks || !isOnlySampleClip)
        {
            return ActionValidity.NotApplicable;
        }
        return hasValue ? ActionValidity.Valid : ActionValidity.Invalid;
    }
}

コピーと同じくValidateExecuteMenuEntryがありますが、少し増えているものがあります。

まず、Validateの結果にInvalidが増えました。このとき、表示はするけど実行できない状態になります。
今ですと、SampleClipを選択してるけどコピーされてる値が無い場合は貼り付けられませんから、実行だけ禁止します。

また、クラスの属性にApplyDefaultUndoAttributeが追加されました。これは、Executeした場合に勝手にその操作をCtrl + ZUndoできるよう登録してくれるものです。 コピーの際にはどうでもよかったですが、貼り付けはUndoできてほしいので付けました。

終わり

Timelineは一般的に非プログラマが触ることが多いと思いますので、手軽に扱えるように環境を整えてあげたいですね。

それにしても、割と活用頻度が多そうなAPIなのですが、調べても全然ユーザーの資料が出てこず、不思議です。
(ApplyDefaultUndoなんか便利なのに、後述Qiita記事のInvokerの中身を追わないと存在に気付かなかった)

参考資料

公式のAPIリファレンス
docs.unity3d.com

TimelineActionについてまとめてくださっているQiita記事 qiita.com

ラズパイで室内環境をディスプレイに表示する

BME280センサーから温度、湿度、気圧を取得するのは以前やったので、それを表示できるようにしました。
視覚的な出力先があったほうが楽しいので。

ものとしてはSSD1306という安いディスプレイです。 akizukidenshi.com

I2C接続が複数できるのかわからなかったんですが、一応並列に接続すれば動作するみたいですね。

スクリプトとしてはBME280から取得、取得した数値をディスプレイに出力と単純に順番に処理しており、2回電流を流しているはずで、1回目はセンサー用、2回目はディスプレイ用です。ですけど回路的にはどっちにも流れてるように見えるので、本当にこれで問題無いのか……

他には

最初はラズパイにはiPadからリモートデスクトップで接続してたんですが、面倒なのでwifi子機を買ってデスクトップのWindowsPCからsshできるようにしました。今はWindowsスクリプト編集してscpで渡してるんですが、これもまだ面倒。VSCodeで直接sshして編集とかできるとか聞いたので、複雑なスクリプトを編集する場合はそっちにしたいですね……

また、RaspberryPi Pico WHも購入しました。初期設定だけ完了して、Hello World的なものは終わりましたが、結構活用が難しいですね。

akizukidenshi.com

OSが無くMicroPythonで何とか頑張らないといけないのが思ったよりも厳しめの制約です。
ただ電池駆動できるのはかなり魅力的なので、何か考えたいですね。

読書感想文「ルールズ・オブ・プログラミング」

リンク

https://www.amazon.co.jp/dp/4814400411

感想

大筋

プログラミング設計本にあたりますが、具体的なテクニックは例として出しながら、重視しているのは考え方、という内容です。
サブタイトルに「より良いコードを書くための21のルール」とある様にそれぞれのテーマに絞って21章あるのですが、
第1章に「できるだけ単純であるべきだが単純化してはいけない」というテーマを置いてあるのが素晴らしいです。

その他の20のルールも、究極的にはこの主張に戻ってくるものも多く、どうすれば単純なコードになるのか、という話だと捉えても良いでしょう。

実際私の考えも、単純で、読むだけで仕様が伝わってくるようなコードが最高だというものなので、非常に共感しました。

ゲーム開発者の設計本

また、「Ghost Of Tsushima」で有名なサッカーパンチのプログラマの著書ということもあり、出てくる例がかなりゲームっぽいのも嬉しいです。
あんまりゲーム関連でこういう設計本って無くて、普段設計本を読んでいても、どうしても「顧客管理システム」とか「商品流通システム」とか全然興味無いし身近に感じられない例ばかり出てきて退屈だったんですよね……

そういう点でも貴重な良書だと思います。
また、ゲームということでめちゃくちゃ実行時パフォーマンスを気にする業界での設計思想というところも非常に価値があると思います。実際パフォーマンスを気にする記述もたくさん出てきます。

文体

原著からして砕けた書き方のようですが、翻訳でもかなりフランクな文体となっています。個人的には読みやすければ何でも良いのですが、なんか気にする人もいるみたいですね。 (Amazonレビューが文体で評価下げられてて可哀想w)

総評

本書の全体的なテーマは「プログラミングが持つ複雑さをいかにして軽減、隠蔽し、単純に保って壊れにくくするか」という点に尽きます。アーキテクチャだとかデザインパターンだとかの本ではなく、考え方に注目しているので、そういう細かいテクニックが本質的な問題を解決しないと悩んでいる方は向いているのではないでしょうか。

また、ゲーム開発者の設計本だったり、なかなかレアなタイプの本でもあると思います。
オススメです。

ラズパイで定期的に室内の温度湿度気圧を測定してSlackにポストする

nigiri.hatenablog.com

の続きです。
前はサンプルに愚直に従ってセンサーから受け取ったデータをログに出すところまででしたが、せっかく取得したデータはどこかに保存したいので、とりあえずSlackに投げるようにしました。

なお、GPIO拡張はそのままブレッドボードにブッ刺すものだと知ったので配線が多少マシになりました。

Slackは個人で遊びに使う用のワークスペースを作っており、また以前WebHookのURLを発行していたので、 今回プログラムに加えた変更は、そのURLを使ってSlackに投稿するリクエスト処理を増やしただけです。

url = "slackのwebhookURL"
data = json.dumps({
    "username": "らずぱい4", # 適当にユーザー名
    "channel": "room_status", # 適当に投稿先のチャンネル「room_status」を作っておく
    "test": message # 投稿内容本体
})
requests.post(url, data=data)

あとはcrontabで、毎時の0分、15分、30分、45分に自動で処理を走らせるように登録しました。

00,15,30,45 * * * * python <.pyスクリプトのパス>

エラー処理とか何もしてないので、センサーモジュールの接続不良とかで一瞬で壊れますが、まぁ今は及第点でしょう。
また、その辺を徹底しようとするとpythonだと厳しいなと思い始めました。ラズパイ用のエディタで書いているのもやり辛さの大きな要因のように思います。
慣れ親しんだエディタや使いやすい言語など、ラズパイ開発環境を考えるのは重要そうです。(やっぱりRustかな)

さて、一応登録して30分ほど経過したところで、ちゃんとSlackに投稿してくれていますね。 (アイコンはデフォルトで設定してるやつです)

23:15に冷房を切ったんですが、勢いよく部屋が温められているのがわかります。
夜くらいもっと涼しくあってほしいです。

ラズパイ入門した

私はソフトウェアのプログラマですが、なんか組み込み的なものに手を出したくなったのでラズパイに手を出してみました。
ラズパイ4Bモデルです。

ラズパイだけあっても普通にPCでコード書くのと変わらないので、とりあえず温湿度、気圧センターを合わせて購入。

akizukidenshi.com

電子工作の部品も持ってないので、結局、

  • ラズパイスターターキット(ACアダプタとか入ってるやつ)
  • ジャンパー線オスメス全パターン
  • GPIO単位拡張ボード
  • ブレッドボード
  • 温湿度、気圧センサー
  • はんだ、はんだごて

を購入。はんだごては、温湿度、気圧センサーのピン接続のために必要なことが後からわかったので購入しました。まぁあって困らんやろ……
というわけでラズパイが温度、湿度、気圧を取得できるように接続

一旦取得に使用するプログラムも接続も含めて以下の記事をめちゃくちゃ参考にしました。

s-design-tokyo.com

リモートデスクトップ

ラズパイに色々線をつなぐ都合上、その操作のためにディスプレイをケーブル接続するのは面倒なので、リモートデスクトップが楽そうです。 とりあえず以下のページを参考にリモート接続できるようにして、

www.indoorcorgielec.com

iPadでリモート接続して操作するようにしました。

下図はiPadのスクショです。

センサーに息を吹きかけたら湿度が上がってるのが確認できるので、取得はできてそうです。
が、気圧の値がファンキーなので何か計算を間違えていそうですね。 計算途中で普通に桁を4つ間違えていました。修正してhPa単位になりました。

数値をもっと読みやすくするとか、定期実行するとか、簡単なディスプレイを接続して計算結果を表示するとか、まだまだやるべきことはありますが、最初はとりあえず動いてるからヨシ!

Unityでいろんなオブジェクトを一括で削除する際に、Undoのスタックが溢れて死ぬ

辛い思いをしたので備忘録です。

例えば、編集中は必要だけどゲーム実行時には不要なオブジェクトを、アセットバンドルビルド前に消したいとか思ったとします。
タイムラインの開発で紆余曲折あって結局ミュートにしちゃったトラックとか。

それをアセバンビルド前に検索して削除しようとするのは普通だと思いますが、あまりに大量のクリップやトラックを一気に削除しようとすると、なんかNative Crashとか言われてUnityが死にます。

この場合、オブジェクトの生成削除についてのUndo用の履歴が溢れている可能性があります。

自動化している場合はどうせUndoなんてしないんだから

Undo.ClearAll();

を小まめに呼んであげて、履歴が溢れないように気をつけましょう。