mattak's blog

人生を1ミリ進める

Unity 2018.1 のguidかぶりに注意

Unity 2018.1 で新しくプロジェクトを作成した際に、 Scenes/SampleScene.unity が作成され自動的に初期シーンとして開かれるようになりました。 とても便利な機能なのですが、unitypackageをexportする際には注意が必要です。

はまったところ

f:id:mattaclj:20180524234231p:plain

こんな感じの構造のunitypackageをexportしたかったのですが、新規作成したプロジェクトでimportしてみると下記のようなimport画面に...

f:id:mattaclj:20180524234434p:plain

ExamplesディレクトリがScenesディレクトリに、 EnumSelectionDemo.unitySampleScene.unity に認識されてしまっています.

なぜおきるのか?

unitypackageの実態はzipファイルなので、これを解凍してみるとその理由がわかります。 .unitypackage => .zip に変更して、解凍してます.

f:id:mattaclj:20180524235131p:plain

guidをdirectoryとして、3つのファイルが各ディレクトリに保存されています

  • <guid>/asset
  • <guid>/asset.meta
  • <guid>/pathname

詳しくは下記のような感じ

  • asset は実態ファイル (Sample.csのようなファイルがそのまま).
  • asset.meta は対応するunityのmetaファイル.
  • pathname は展開先のそのままのパスが1行書き込まれているだけ (例: Assets/Plugins/EnumSelection/Editor/EnumSelectionDrawer.cs)

この情報を元に.unitypackageはimportされるのですが、ここでexportした際のguidが展開先のプロジェクトのguidと一致しているとそのファイル もしくはフォルダが上書きされるという仕組みです.

つまり、今回は2つのguidがかぶっていたためにimportの認識がおかしくなっているように見えたというわけです

  • Scenes.meta のguid == Examples.meta のguid
  • SampleScene.unity.meta のguid == EnumSelection.unity.meta のguid

UnityEditor上でファイルやフォルダをリネームするとguidは保持したままにしてくれるので、SampleScene.unityとScenesのguidが別プロジェクトでも一致してしまっていたというわけです。

注意

通常はguidがかぶるような危険性はほぼ確率的には起こり得ないと思うのですが、Unityのデフォルトで生成される Scenes.meta と SampleScene.unity.metaはすべての新規プロジェクトで一致してしまうのでライブラリを作ってexportする際には気をつけてください..

下手をすると今後世界中のライブラリでguidの衝突が起きる可能性があるという感じです。

多分テンプレートからコピペするようなUnityの実装なので、guidを動的に生成するようにfixしてくれればこの問題はなくなると思うのですが、一旦修正されるまではexportする際のguidには注意が必要そうです.

あとでissueおくろう..

試行 8

gawkべんりだ

よくやる処理がgawkで便利にできると知ったのでいくつかメモ

画像サイズ変更

例: - sample.png => sample.jpg

従来 (確認 => 実行)

$ for f in $(ls *.png); do; echo convert -geometry 480x480 $f ${f/.png/.jpg}; done
$ ^echo^

gawk (確認 => 実行)

$ ls *.png | gawk '{a=$1; sub(/\.png$/,".jpg",a); print "mv",$1,a}'
$ ls *.png | gawk '{a=$1; sub(/\.png$/,".jpg",a); print "mv",$1,a}' | sh

従来のやり方が手癖に残ってて、そっちを使うことが多いけど、gawkパターンはきちんと正規表現できたりパイプできたりするので使い分けていこうとおもう

合計

例: pngファイルの容量調べる

$ find Assets -type f -name '*.png' | xargs -n1 ls -l | gawk '{SUM+=$5}END{print SUM/1024,"KB"}'
268.779 KB

集計

例: フォルダごとの容量調べる

$ find . -type f -name '*.png' | xargs -n1 ls -l | gawk '{ dir=gensub(/^(.+)\/[^\/]+$/,"\\1","g",$9); a[dir]+=$5 }END{ for (k in a) printf("%s\t%.2f\n", k, a[k]/1024)}' | sort -n -k2
./node_modules/mapnik/lib/binding/node-v48-darwin-x64/share/mapnik/gdal 1.97
./node_modules/tilelive-vector/node_modules/tiletype/test/fixtures  12.66
./node_modules/tiletype/test/fixtures   12.66
./node_modules/boom/images  28.79
./node_modules/mapnik/node_modules/boom/images  28.79
./node_modules/hoek/images  37.05
./node_modules/mapnik/node_modules/hoek/images  37.05
./node_modules/hawk/images  76.83
./node_modules/mapnik/node_modules/hawk/images  76.83
./node_modules/node-pre-gyp/node_modules/hawk/images    76.83
./node_modules/tilelive-vector/node_modules/hawk/images 76.83

ヒストグラム

例: material-design-icons内のpngのファイル容量(byte) 分布

$ find . -type f -name '*.png' | xargs ls -l | gawk 'function step(n,s){return int(n/s)*s}function idx(n){ return n<1000 ? step(n,100) : "1000+" }{ a[idx($5)]++ }END{ for (k in a) print k,a[k] }' | sort -n -k1
0 1242
100 16501
200 17071
300 11819
400 8396
500 5590
600 4340
700 2934
800 2250
900 1753
1000+ 6023

行集計の便利さを感じる. こっからサクッとグラフにできるとさらにいいなあ

試行回数7

私生活をマージナルゲインしていく

失敗の科学をよんで知った概念. 1%の改善をつくみあげいけば... 的なやつ。

エンジニアだと普通にすごしていると日々マージナルゲインしていると思う。 escが押しにくいからキー配置をかえてみようとか、macのspot lightが使いにくいからalfredつかってみようとか、Editorの設定変えてみようとか、なんでもいいけどそういう些細なやつ。 やっていくうちに、積み上げていくうちに、そのうち大きな違いが出ている。

この概念は自分も持っていたし、仕事では実践する機会も多かったのだけど、私生活ではマージナルゲインできていなかった。

最近、触発されていくつか実生活で意識して試してみてよかったことがあったのでいくつか。

Run & 風呂 => 夕食夕食 => Run & 風呂

帰宅後に走ってから、めしを作って食べるパターンが多かった。 なんとなく、走り終わって1日にやることをやり終えてから夕食をたべたいっていう気持ちだった。

この順序を逆にしてみた。 逆にするだけで、効果があったのをここ数日で実感した

  1. 夕食後を食べた後にだらだら問題 (めしながらアニメ、YouTubeでダラダラ)
  2. 歯磨き忘れる問題
  3. 食事時間遅い問題
  4. 就寝時間遅い問題
  5. 皿洗い忘れ問題

上記問題が解決ないし部分的改善を果たせた

1 => 運動により心拍変動が起きたりシャワーでスッキリした直後の方が、めし後よりも意思が流される確率が下がった たぶん。 2 => 食後の再石灰化時間を待とうと思って、そのまま歯磨きしないパターンがいくつかあったのがなくなった 3 => 単純に1hほど夕食時間が早くなった. 4 => 就寝前3時間ルールが早まった. Run => 食 よりも 食 => Run のほうが睡眠効果高かった 5 => シャワーと歯磨き後になんとなくやるパターンもちらほら増えた

ただ順番をかえるだけのことだけど、当たり前だった行動に結構な違いが生まれるというのは新鮮だった.

布団/敷布団/枕を防ダニ/防ハウスダスト

アレルギー性鼻炎がひどい。 布団からのホコリの飛散が大きいことに気づいたので、完璧に防塵・防ダニの寝具に変えてみた

ミクロガードオンラインショップ

定量的な測定はできてないのだけど、体感としては少し症状が和らいだ感じがする。 あと肌触りいいので純粋に前より寝やすくなった

カーテンを散乱するやつに

アカリナについて | 暗い部屋を明るくするカーテン・採光ブラインドの通販 | あかりラボ

部屋が暗くて、休日に萎えることが今までも多々あったので、光を散乱して部屋を明るくするカーテンに変えてみた。 これも計測できてないけど、体感として部屋の照度は上がっている。朝明るくて起きやすくなった感じはする. あとカーテンがまったく透過しないのが割りと気に入ってる.

とかとか、他にもシャワー浴びながら掃除とか、ポモドーロタイムで部屋の換気とか細かいことをいろいろ試している。 失敗したり、まったく効果がないこともあるけれど、宝くじを引くような気分でやっている。 ハズレも多いけれど、タダだし、たまに当たるとうれしいし、やっていて生活の変化が楽しい。

試行6回目

プロジェクト全体のコードの行数からいい感じかどうか知りたい

数年前のプロジェクトで100行未満のファイルをプロジェクト全体の95%にする。 という趣意でメンテしやすさを確保しようとした。 (テストやコードジェネレートしたものは除いて計測するような指標)

speakerdeck.com

これはわりと簡便で良い指標だと個人的に感じている. どんなプログラミング言語アーキテクチャでもすぐに適用でき、ある程度プロジェクトの健康状態がそこからわかるからだ。 この話をすると、クラス感の依存関係をみたり、コミットログでの編集頻度などより複雑な解析しようと試みる人がいる. それはそれでとても良いのだけど、個人的にはこの指標の柔軟さ、簡便さ、費用対効果の高さが気に入っている.

ファイル行数の分布グラフがばらついているプロジェクトは、だいたい何か設計や機能が失敗している. 昔どこかの勉強会で、行数が多いほどエンバグする可能性が高まるという当たり前の事実を聞いた。 実体験としてはこれはまさしくその通りで、たいてい行数が多いファイルは何か失敗しているし、過去数プロジェクトでも同じ傾向がみられた。 自分がリードした過去3プロジェクトでは、アーキテクチャは違えど、この基準を全てで満たすように設計できた。

この測定を自分はコマンドラインでかき捨てのスクリプトを書いてやることが多かったのだけど、 簡便に実行できるようなツールやサービスをつくってみたい.

ちょうどrails学んでいるので、railsで何か書いてみようと思う。

という意気込みだけを雑に書く

試行回数5

Boidsを試す

Unite 2018でBoidsの話があったのだけど、面白かったので真似して実装してみる.

Boidsとは

Boids - Wikipedia

群体のシミュレーションをする際に、3つのルールを適用すればObject群がそれっぽく動くということらしい.

wikipediaの画像が分かりやすかった.

ルール1. 分離

  • ローカルの群れを避けるようにステアリングする

ルール2. 整列

  • ローカルの群れたちが向く方向の平均にステアリングする

ルール3. 凝集

  • ローカルの群れの平均位置に向かってステアリングする

実装

雰囲気で実装してみる.

github.com

public class Boid : MonoBehaviour
{
    public bool ApplySeparation = true;
    public bool ApplyAlignment = true;
    public bool ApplyCohesion = true;
    public bool ApplyRandom = true;
    public float Radius = 10f;

    private Transform[] allmates;

    private void OnEnable()
    {
        this.allmates = this.transform.parent.GetComponentsInChildren<Boid>()
            .Where(it => it.gameObject.GetInstanceID() != this.gameObject.GetInstanceID())
            .Select(it => it.transform)
            .ToArray();
    }

    private void FixedUpdate()
    {
        var randomStrength = 0.0001f;
        var flockmates = TransformUtils.SelectLocalFlockmates(this.transform, this.allmates, this.Radius);

        var v1 = this.ApplySeparation
            ? TransformUtils.CalcSeparationRule(this.transform, flockmates) * 10f
            : Vector3.zero;
        var v2 = this.ApplyAlignment
            ? TransformUtils.CalcAlignmentRule(this.transform, flockmates)
            : Vector3.zero;
        var v3 = this.ApplyCohesion
            ? TransformUtils.CalcCohesionRule(this.transform, flockmates)
            : Vector3.zero;
        var vr = this.ApplyRandom
            ? new Vector3(
                Random.Range(-randomStrength, randomStrength),
                Random.Range(-randomStrength, randomStrength),
                Random.Range(-randomStrength, randomStrength))
            : Vector3.zero;
        var diff = v1 + v2 + v3 + vr;

        this.transform.LookAt(this.transform.position + diff);
    }
}

各ロジック

public static class TransformUtils
{
    /// <summary>
    /// Calc boids rule1. steering position by separation.
    /// </summary>
    /// <param name="target">target object</param>
    /// <param name="flockmates">local flockmates without the target object</param>
    /// <returns>stearing vector by separation rule</returns>
    public static Vector3 CalcSeparationRule(Transform target, Transform[] flockmates)
    {
        if (flockmates.Length < 1) return Vector3.zero;
        var v = flockmates.Select(it => target.position - it.position).Aggregate((sum, it) => it);
        return v / (0.00001f + v.sqrMagnitude);
    }

    /// <summary>
    /// Calc boids rule2. steering position by alignment.
    /// </summary>
    /// <param name="target">target object</param>
    /// <param name="flockmates">local flockmates without the target object</param>
    /// <returns></returns>
    /// <returns>stearing vector by alignment rule</returns>
    public static Vector3 CalcAlignmentRule(Transform target, Transform[] flockmates)
    {
        if (flockmates.Length < 1) return Vector3.zero;
        return flockmates.Select(it => it.forward).Aggregate((sum, it) => sum + it) / flockmates.Length;
    }

    /// <summary>
    /// Calc boids rule3. steering position by cohesion.
    /// </summary>
    /// <param name="target">target object</param>
    /// <param name="flockmates">local flockmates without the target object</param>
    /// <returns>stearing vector by cohesion rule</returns>
    public static Vector3 CalcCohesionRule(Transform target, Transform[] flockmates)
    {
        if (flockmates.Length < 1) return Vector3.zero;
        var average = flockmates.Select(it => it.position).Aggregate((sum, it) => sum + it) / flockmates.Length;
        return average - target.position;
    }

    /// <summary>
    /// Select local flockmates within the circle radius
    /// </summary>
    /// <param name="target">target object</param>
    /// <param name="mates">flockmates object without me</param>
    /// <param name="radius">raidus of target flockmates</param>
    /// <returns></returns>
    public static Transform[] SelectLocalFlockmates(Transform target, Transform[] mates, float radius)
    {
        if (mates.Length < 1) return new Transform[0];
        var radius2 = radius * radius;
        return mates.Where(it => (it.position - target.position).sqrMagnitude <= radius2).ToArray();
    }
}

結果

なんかたのしい! せっかくなのでちょっと整理したらライブラリとして体裁整える。

試行数4

試行していく

雑に書いていく運動

3冊読んだ.

どれもいい本だった。 失敗の科学は、マージナルゲインの考え方、失敗を隠蔽する組織と失敗を共有し学ぶ組織の違いが印象的だった。 仕事は楽しいかねは、何度もいくつも同時に試行していくことを例示豊富に教えてくれた。 チーズはどこへ消えた?はコンフォータブルゾーンから出て勇気を持って新しいチーズを探していくことことの喜びが示唆されていた.

最近アウトプットが少ない、何かを試行する機会も少ない。 どんどんと何かを試行していきたい。でもって試行したことをカジュアルに残していこうと思う。 できればそれをFB回路に繋げて、少しづつ改善していきたい. 強化学習もそうだけど、どんどん試して試行して、たくさん失敗することは大事だ。

意識して試して、楽しんで、失敗して、少し改善をやっていこうとおもう。 とりあえず今年100個何か新しいことを試行するのが目標.

あと234日. 2日に1個何かを試していくペース. ちょっときついか... 週に1個は何かやっていく感じで行こう. あと33週なので 30個くらいを目標にする。 うーんでもぬるい感じがする。

とりあえず、3冊読んだのであと97個ということにする。

Rider で全体に適用したコードフォーマットを切り替えたい...!

本当はプロジェクトの設定でコードフォーマットを指定するのが良いのかもだけど、自分用メモ。

OSSはデフォルトの設定、業務は特定の設定でのコードフォーマットをRiderに適用したい.

最終的に Riderの設定をgit管理するのが良さそうという結論に至った。

gist.github.com

git check out resharper-host/GlobalSettingsStorage.DotSettings で一発リセットできるので便利!