ただの適当な開発記

会社勤めしつつUnityでアプリ作ってる人の雑記

ボーイスカウト・ルールの重要性

ボーイスカウト・ルール

エンジニアの世界には「ボーイスカウト・ルール」なるものが存在するらしいです。

実際のボーイスカウトの規則には「自分のいた場所は、そこを出て行く時、来た時よりも綺麗にしなければならない」というものがあるらしく、それをエンジニアのコーディングに置き換えて表現したものがここでいうボーイスカウト・ルールですね。
※「ボーイスカウトの規則」と表現することもあるみたいです

myenigma.hatenablog.com
qiita.com

口癖のように使ってる人もいる

前のプロジェクトで一緒に仕事をしていたエンジニアが口癖のように言っていました。
「自分が書いたわけじゃないからって良くないコードを放置しちゃだめだよ。ボーイスカウトルールだよ」的な感じで。

例えば自分が新しい機能を担当したときに既存のクラスを一部使って実装するとする。元の処理を見たら結構汚いコードが書かれている。
そうなったときに、それを見て見ぬふりをするのではなく元の状態より綺麗にして作業に着手しましょう、ということですね。
前述したエンジニアの方が口を酸っぱくして言っていたので、自分は割とその精神が定着していました。

反発も勿論ある

「ただ、人のコードを勝手にいじる」ということに対して反発する人は少なからずいると思います。
(自分も勝手に人にリファクタリングされて「やばかったから直しといたよ」とか言われたら少しイラッとするかもしれません笑)
ただそこはプルリクなんかでお互いが議論して納得する形でよりよいコードに変えていけばいいのかなと思ってます。
実際に前の現場ではコードレビューやプルリクを導入していたので、人のコードに変更を入れた場合はgithubのConversation上で元の実装者とやりとりして納得した上でマージするようにしていました。(元の実装者が居ないパターンももちろん多くありましたが..)
逆にそういった文化がない現場だと、好き放題に人のをいじれるというのは無法地帯になってしまうかもしれません。

明確なアンチパターン

そんな状態で2ヶ月前から参画した直近の現場で驚いたことがありました。
コードにこんなコメントが書かれているんです

//この処理書いた人は割と頭悪い
//なぁにこれwwww

みたいなコメントがあるんです。
そしてこのコミットをした人は本当にただ感想を書いただけでそこを改善する処理は全く書いていない。

既存のコードに対してこういった感想を書くだけ書いて何も手を付けずに立ち去るというのは、結局「この処理は複雑で読みにくいと思うけど僕は何もできませんでした」って宣言しているようなものだと思うんです。

ただその人にも悪気があったわけではなく、プロジェクトの状況が燃えてて疲弊している中でつい見かけた良くない処理に対してキツめのコメントしてしまった、とかもあると思います。
しかし、こういったコメントを見つけてしまうと「ああ、このプロジェクトにはボーイスカウトルールとは真逆をいってるな」と思ってしまうわけです。

メリットを共有することが大事

そもそも「人のコードに手を入れて改善していく」という発想がない可能性があります。
大事なのは技術的負債を皆で返済していくようになると、自分がどれだけ楽になるかというのを共通認識として持つことなのかなと思っています。
今の現場ではプルリクに対するレビューを担当しているので、そういった姿勢をチーム内で共有出来るようになればいいなと思っています。
もしこれを読んでいる方で「こうしたら上手くいったよ」という例があればご教示いただきたいですm(__)m



というわけで珍しく会社で感じたことを書いてみました。明日もがんばるぞい

スクロールビューの負荷を軽減する方法 #Unity


最近はリリース直前のゲームプロジェクトでUnity(80%)やらphp(10%)やらバッチ(5%)やらエクセルマクロ(5%)やらを触っています。
リリース直前ということで申請までになんとかしなきゃいけないタスク一覧みたいなのがあるんですが、その中に
スクロールビューを軽くする
というタスクがありました。

まあ最近のスマホゲームなんかだと友達一覧やらプレゼント一覧やらクエスト一覧やらとにかく縦横にずら~っと表示する情報が多いわけです。
そこがとにかく重いと。

対応方法はいくつかあるんですが、今日はそのあたりについてまとめて書いてみようと思います。
(「いやいや違うよ」とか「こっちの方がオススメだよ」といった方法があればコメントお待ちしてます!

重い原因

まずスクロールビューを重くしている原因を整理してみます。

(a)スクロールしてる要素一つ一つの負荷が重い

f:id:cocokyoro:20170816000021p:plain
こんな感じでいっぱい処理がならんでいてそれぞれに重い処理がくっついてるとトータルの負荷は結構なものになってしまう

(b)表示したい要素を全て描画してしまっている

f:id:cocokyoro:20170816000334p:plain
こんな感じのビューがあるとして
(画像はフリーコンテンツの星宝転生ジュエルセイバー のを使わせてもらってます)
f:id:cocokyoro:20170816000256p:plain
Unityのスクロールビューはデフォルトでマスクのコンポーネントがついているのでこういう見た目ですが、
マスクをオフにすると
f:id:cocokyoro:20170816000617p:plain
こんな見た目になります。
マスクで見た目上は画像が消えていても、裏で描画の処理は走っちゃってます。
(ちなみに表示の負荷だけでいうとマスクをしているときの方がかかります)

と重くなってしまう原因の代表的なものとしてはこの2つが考えられます。

対策

(a-1)スクロールしてるオブジェクトにくっついてるスクリプトの処理を軽減する

- 使わないUpdateとかは消しておく
- Updateの中でFindやGetComponentを呼んでる箇所があったら無くす

(b-1)データ駆動のスクロールに切り替える

- EnhancedScrollerを使う
- 無限スクロールやデータ駆動のスクロールを提供するアセットです。
- 詳しくは次回のアドベントカレンダーのときに更新します

(b-2)描画範囲外の要素を非アクティブにする

- この記事ではこの対応について詳しく説明しようと思います

描画範囲外の要素を非アクティブにする

- ここの部分を描画しているとしたら
f:id:cocokyoro:20170820235821p:plain
- ここの部分のオブジェクトの処理は切ってあげた方がいいわけです
f:id:cocokyoro:20170820235946p:plain
- というわけで

  • 特定のRectに入っているかどうかを確認するscript

  • 指定した範囲内に入ったタイミングと出たタイミングで処理を走るようなComponent


を作りました。
前者についてはUnityのコミュニティに上がっていたやつを参考にしました
(参考 https://forum.unity3d.com/threads/test-if-ui-element-is-visible-on-screen.276549/#post-2978773)

使う側の例としては下記のような感じです。

f:id:cocokyoro:20170821003456g:plain

falseだったら(指定した範囲外に描画しようとしていたら)描画をオフにしたり処理が重いスクリプトをオフってあげればいいと思います。ここでは試しに色を変えています。

実用

public class SampleHeavyComponent : MonoBehaviour {

	// Update is called once per frame
	void Update () {
		for(int i = 0;i < 10; i++){
			Debug.Log("heavy log");
		}
	}
}

たとえばこんなコンポーネントが100個着いていたとして(わかりやすくするためにこんな処理にしてます)、描画範囲外にでたらこのscriptをオフにしてあげるようにします。

さっきの初期化の部分をこんな感じに変えます

private void SpawnScrollObject(){
		var scrollElement = Instantiate(_scrollElementOrigin);
		scrollElement.transform.SetParent(_scrollContentParent);
		var observer = scrollElement.GetComponent<ViewRectObserveComponent>();
		var heavyComponent = scrollElement.GetComponent<SampleHeavyComponent>();
		observer.Initialize(_targetViewRect, (entered) => heavyComponent.enabled = entered);
}


これでProfilerの処理が

  • Before

f:id:cocokyoro:20170822031411p:plainf:id:cocokyoro:20170822031415p:plain

  • After

f:id:cocokyoro:20170822031422p:plainf:id:cocokyoro:20170822031419p:plain
こんな感じになります。fpsが改善されてますね。

描画している部分以外は非アクティブにしているので、当然軽くなります。
「とにかく急遽スクロールを軽くしてくれ」と言われたらこの方法がおすすめです。

注意点

この実装方法はいくつか注意点があります

  • スクロールさせる要素が多すぎると重い

スクロールする項目があまりにも多いと、ViewRectObserveComponentの処理で結局重くなってしまいます。
ViewRectObserveComponentはUpdateで常に監視しているので、スクロールさせる要素が多いと結局重くなってしまいます

  • Hierarchy上の階層が深いと重い

rectTransform.GetWorldCorners
って呼んで字のごとくWorld座標を取得しているんですね。
なので階層が深ければ深いほどWorld座標を計算する負荷が大きくなります。
できれば指定した階層から相対的に位置をチェックするとかができればいいんですが、そこは今のところ未対応です



結局スクロールはデータ駆動が一番

さんざん色々言っといてあれですが、スクロールビューのパフォーマンスを最適化するのに一番良い方法はデータ駆動のスクロールビューに切り替えることです。
おすすめはこちらのEnhancedScrollerです。

こちらについては後日アセットアドベントカレンダーの更新時に詳しくかければと思っています!
atnd.org

ではでは

2017年5月~2017年7月のアセット売上

8月になりました。あつ〜

ということで過去3ヶ月のアセットストアの売上です。

f:id:cocokyoro:20170801010742p:plain

ご購入いただいた皆様ありがとうございますm(__)m
ReferenceFinderはコンスタントに売れているんですが、今思うともうちょっとわかり易い名前にした方がよかったのかな〜と思っています。
というのも同じような名前のアセットが多すぎまして..orz
ReferenceFinderがやってることはInspector上のSerializeFieldで参照してるオブジェクトの参照の一覧を表示しているわけで、InspectorReferenceViewerだとかSerializedComponentFinderとかあったんじゃないかなと。
まあでも今書いてて思いましたけどあまり長ったらしいアセット名だとそれはそれで敬遠されてしまいそうですね。

↑ありがたいことに☆5Ratingが表示されるようになりました!

この3ヶ月で思い出に残ってるのはMultiButtonScrollerに関する質問のメールをいただいたことです。
ちょっとチュートリアルの作りが簡素すぎたせいでご迷惑をおかけしてしまったので日本語のドキュメントをもっと充実させようと思いました。
やっぱ自分で使ってみないことには改善点も見えてこないので一度作ったアセットはどんどん使っていこうと思います(`・ω・´)

残る一つのPuzzleTemplateについても最近メールが来まして、どうやら特定環境で起動時にクラッシュする現象が起きているようです。。
こちらは現在原因を調査中ですのでもう少々お待ち下さい。

MacOS SierraにあげたらKarabinerが効かなくなったやつ

3年ぶりにMacのOSアップデートをしました。
普段開発で使ってると極力安定して動く環境から変えたくないのでずーっと10.10 Yosemiteを使ってました。

pc-karuma.net

2014年の10月からずっと放置してたっぽい..

で今回会社のシステムの都合でアップデートした方がいいかも...ということになりアップデート(結果アップデートしなくてもよかったんだけども)

アップデートしたところ愛用していたKarabinerというキーマップ変更のアプリケーションが機能しなくなってしまった..

調べてみると解決策いっぱいでてきた

qiita.com

使ってる置き換えは右のCommandボタンをescapeに変更するというvim用のやつだけなのでKarabiner-Elementsを使って対応
元々入ってるKarabinerを消さないとKarabiner-Elementsの変更が反映されないかも

github.com

置き換えの処理の部分は中身はc++シェルスクリプトで書かれてるみたい。viewの部分はobjective-cで書かれてる。

c++objective-cの部分の連携ってどうやるんだろうって思ったら普通にできるらしい

ja.stackoverflow.com

Objecive-C++という見慣れないやつが。。

hajimehoshi.hatenablog.com

Objective-C++ は、プログラミング言語 Objective-CC++ とを多重継承したような言語です。
お互いの文法がかち合わないので、混在することができます。

はえー知らなんだ


というわけで話がそれてきたので終了

真偽値で2つのメソッドを呼び分ける

少しでもコードを短く書きたい人は下のようなコードを見た時どう思うのだろうか。

    public void PlayNext()
    {
        if (_isEndless)
        {
            PlayNextEndless();
        }
        else
        {
            PlayNextNormal();
        }
    }

どこかのstaticなクラスに以下のようなメソッドを書くと

 public static void CallByCondition(this bool condition, Action onTrue, Action onFalse)
    {
        if (condition)
        {
            if (onTrue != null) onTrue();
        }
        else
        {
            if(onFalse != null) onFalse();
        }
    }

もとのやつは以下の感じで表現できる

    public void PlayNext()
    {
        _isEndless.CallByCondition(PlayNextEndless,PlayNextNormal);
    }

こういう拡張に慣れると他のプロジェクトにジョインしたとき非常に困る。
かといって導入しようとすると多分嫌がられるという笑

DoTweenの拡張メソッド

今の座標を基準に指定した値を追加で動かす、というありそうでないメソッドを拡張で追加

DoTweenの拡張クラス

今の座標から右に100f動かすのを繰り返したい!とかはこのメソッド使うとシンプルにできますね

DoTweenは無料ですよん ;)

実機でのデバッグサポートアセットSRDebugger使ってみた #Unity #AssetStore

最近プロジェクトで使い始めたアセットがとても便利なので紹介したいと思います。

SRDebuggerとは

こちらのSRDebuggerです。

こちらは実機でのデバッグをサポートしてくれるアセットですが、使える機能がとにかくリッチ。

  • 実機でのログ確認(Debug.Log系の出力確認)
  • 実機でのプロファイラー表示
  • 使用している端末のスペック表示
  • コード上で属性をつけた変数を実機上でいじれるようになる

こんなことができちゃいます。

実際の開発現場でもこのアセットの存在を知らずにこれに該当する機能を作ろうとしているプロジェクトは多いのではないでしょうか。
かくいう自分も会社で同じような機能を実装しようとしていたところに、前のプロジェクトで一緒だったエンジニアの先輩が教えてくれて知りました。笑

起動方法

デバッグメニューはこんな感じで画面の左上をタップすると起動します。

f:id:cocokyoro:20170530235059g:plain

このデバッグメニューを有効化するために何かを事前にやる必要はありません。
シーンにprefabを置くとか、事前にコードを置くとかは必要ないです。
これをどういう仕組でやっているかは後日別の記事で書きます。笑

機能一覧

上で取り上げた各機能について一つ一つ見ていきます。

実機でのログ確認

f:id:cocokyoro:20170531002019p:plain

こんな感じでDebug.Log,Debug.LogWarning,Debug.LogErrorで出力した文字列は実機上で見れます。

f:id:cocokyoro:20170531002045p:plain

上の3色のボタンのオンオフを切り替えればログレベルでの出し分けも簡単にできちゃいます

f:id:cocokyoro:20170531002114p:plain

文字列を入力してのフィルタリングも対応

f:id:cocokyoro:20170531002128p:plain

上の方にあるピンボタンを有効にするとデバッグウインドウを閉じてもログが見れます

実機でのプロファイラー表示

f:id:cocokyoro:20170531002803p:plain
こんな感じでUnityEditor上のProfilerと同じような情報が実機上で見れてしまいます
見れる項目はUpdate,Render,Otherの3つ、流石にEditorのProfilerよりは項目は少ないですが、実機上でこの項目が見れるのはありがたい

使用している端末のスペック表示

f:id:cocokyoro:20170531005443p:plain

こんな感じで端末のスペックが見れます。
うつってるのは愛用のKindleFireのスペックです

コード上で属性をつけた変数を実機上でいじれるようになる

こんなコードを書いてみます

※partialクラスである点に注意

using System.ComponentModel;
using UnityEngine;

public partial class SROptions {

	private float _sampleProperty1 = 0.5f;
	private float _sampleProperty2 = 0f;
	private float _sampleProperty3 = 0f;
	
        //プロパティをいじれるようにするための属性
	[Category("Sample Category1")] 
	public float SampleProperty1 {
		get { return _sampleProperty1; }
		set { _sampleProperty1 = value; }
	}
	
        //Rangeも指定できます
	[NumberRange(0,10)]
	[Category("Sample Category2")] 
	public float SamplePropert2 {
		get { return _sampleProperty2; }
		set { _sampleProperty2 = value; }
	}

        //同カテゴリ内に複数のプロパティをセットすることもできます	
	[NumberRange(0,10)]
	[Category("Sample Category2")] 
	public float SamplePropert3 {
		get { return _sampleProperty3; }
		set { _sampleProperty3 = value; }
	}
	
        //メソッドも簡単に呼べちゃいます
	[Category("Sample Method")] 
	public void SampleMethod() {
		Debug.Log("こりゃ便利だ");
	}
}

これで実行してデバッグウインドウを開き、"Options"の項目を開くと...

f:id:cocokyoro:20170531010952p:plain

すばらしい....

チームで開発していると「この項目を気軽にいじれるようにして欲しい」とか「あの処理いつでも呼べるようにしてよ」とかそういった要望が出てくるわけですが、この機能があれば対応できちゃいます。

まとめ

SRDebuggerは非常に高機能なデバッグサポートツールです。
もし、独自にこういったデバッグ機能を実装しようとしている場合は、一度導入して試してみることをおすすめします。

そんなわけで今日は以上です。