kazumalab tech log

流行りとリラックマと嵐が大好きです。技術的ログ。

集中力が切れたので対応してみた 等々力渓谷編

かずまです。

今日は朝モックとスタブについて記事を書いていたのですが、
集中力が切れたので16:00から一人で自然と戯れにいきました。

blog.kazumalab.com

ちなみにこのタイトルはこのブログを参考(パクった)にしています。
ぜひ箸休めにどうぞ!

codehex.hateblo.jp

15:00 ~ 下調べ

今日は朝から川越に行こうかな〜と思っていたのですが、残念ながらゴロゴロしていたら、
11:00ぐらいまで寝てしまっていました。残念。


ですので少しコードをカタカタ書いて、記事書いて〜ってやってたのですが、
集中力が切れてしまいました。


「どこに行こう...」

ということでGoogle検索

「東京 日帰り 一人旅」

検索しました。笑

そこで出てきたのが、「等々力渓谷」でした。

retrip.jp
このサイト。結構便利で色々使ってます。

世田谷のど真ん中にある自然の場所。
家からは乗り換え一回という楽さで行くことができました。

16:00~ 実際にいってみた

ゴルフ橋

まずは入ってすぐに大きな橋があります。
ゴルフ橋というそうです。昔は木製だったみたい。

f:id:kazumalab:20170820213207j:plain

等々力不動尊本堂

奥へ進むと等々力不動尊本堂へ続く道があります。
ここがまた雰囲気のあるライトで照らされていてきれいでした。
f:id:kazumalab:20170820213214j:plain

帰り道と川

帰りに撮影したのですが、ゴルフ橋を望みながら自然の音を堪能できました。
THE・夏という感じの場所でリフレッシュできました。

f:id:kazumalab:20170820213218j:plain

川が流れていて、水の音もまた、涼しさを運んできます。
対抗の人が来ると川に落ちそうで少しドキドキします。笑

いつもは音楽を聞くのですが、ここにいると音楽は必要ありませんでした。

最後に

リフレッシュした、良い週末でした。
なお写真は結構いい感じにしてありますが天気が良ければもっと良かったです。

いつもPCとにらめっこしているのでたまには自然とこうやって戯れるのもいいな
と感じました。次回は富士山とか川越あたりをブラブラしたい!(集中切れたとき!)

mockとstubが難しい件 RSpec 3.6

かずまです。
いろいろ考えることや、やれることも増えてきて楽しい半面悩ましいです。

Rubyをテストする際にmini testやRspecを使って検証することが多いと思います。
その中でもメソッドをモックする、スタブにするということを覚えました。

追記

PM兼PM on Twitter: "@kazumalab つ https://t.co/Wfvu46XrAa
これ聴くと、もうちょっと視野が広がりそうな一方、混乱するかも笑"

https://ajito.fm/6/

Mockの知識が広がるのを教えて頂きました。

ここのT-wadaさんとの対談を聞いてmockの使い方の歴史を知り感動しました。

たしかにMockはクラスの設計時に使うというのは興味深いし、使えそうです。

僕らが使ってたのはtest doubleのサブクラスのmockだったんですね。勉強になりました。

モックとスタブの概念

結構難しいので説明はできませんのでこれの回答っぽいリンクを貼っておきます。

TDD > モック / スタブ - Qiita

どちらとも共通しているのが、「あるオブジェクトのメソッドが呼ばれたときに
実際にそのメソッドを実行せずに自分で返ってきてほしいものを指定する。」というところだと思っています。

今の考えはこれですね。

ただし、モック、スタブにする部分はそのオブジェクトの振る舞いがすでにテストされていて暗黙的に理想とするデータを返す状態になっていることを前提としていると思っています。つまり、テストをしているメソッドのテストが完璧だったとしてもモックしたメソッドが不十分な振る舞いであれば足を引っ張るのはそのメソッドになるということです。

なので、それぞれのメソッドに対してユニットテストは書くべきだと思っています。

どうやって使うのか?

すでに使っているのであればこれ以降読む必要は無いと思います。
わかりやすいレシピ集を載せておきます。(3.3ですが。)

dev.classmethod.jp


僕が使う例があっているかどうかという部分は確かではありませんが、
次の例を出してサンプルとします。

ex. Railsだとcurrent_userにログインしているユーザー情報が入る場合のお話

ApplicationController.current_userで返ってくるものがnilのときはログインしていない状態、
userが返って来ればログインしている状態です。

class ApplicationController
  def current_user
    # return login user
  end
end


次のようなテストを書いたとします。
この場合、userの詳細ページにアクセスできればテストはsuccessです。

しかしUserControllerのbefore_actionにはログインしているかチェックするメソッドをセットしているので、
このテストは失敗します。

そこでテストでもログインさせる処理が必要です。
もちろん、sign_in_pathにユーザーのログイン情報をpostしてログインするのもありだと思います。
ただ、ログインするテストをしていれば、データをわざわざpostしなくてもいいわけです。(?)

今回はApplicationControllerのインスタンスメソッドをモック、もしくはスタブ化するので、
次のように書いていきます。

relishapp.com

Mock化はexpect

Stub化はallow

2つの違いは難しいのですが、

jangajan.com

ココらへんいいかと思います。
メソッドが呼ばれることを検証したい場合はexpect、
しない場合はallowでいいようです。

もちろんexpectを使うとcurrent_userが呼ばれない場合は
このテストは失敗します。
ちなみに、expectを使うと何度呼ばれたか?というのも検証できるので便利です。

relishapp.com

onceとかtwiceを使うのですね。

他にも

  • test_double
  • double
  • spy

などといった、インスタンス自身をモック、スタブ化するものもあるようです。
relishapp.com

テスト時にインスタンスを生成し、
引数にオブジェクトを渡して、そのメソッド内で渡したインスタンスのメソッドを呼ぶ場合などに
test_doubleを使って、そのメソッドをモックすることができます。

ここをみるとdoubleはtesto_doubleをincludeしてます...。
何をやっているかは深く読んでないのでここでは何も言えません。
ちょっとあとで読んでみます。

https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/test_double.rb


さて、実際にスタブ化してみます。
今回はメソッドが呼ばれたことを検証しないのでallow_any_instance_ofを使います。

これでテストが成功しました。

最後に

今回は例としてログインの部分をスタブ化して、擬似的にログインしてる形を作りました。
いい例だとは思えませんが、こんな感じでできる!と言うのはつかめると思います。

Gemを作る場合やAPIを使う場合に外部との通信が入ってしまったりする部分にmockを使ったりします。
APIなどはとくにAPIサーバー側でアクセス回数制限を設けている場合がほとんどなのでテストで消費するのはもったいないですよね。

OauthなどはMockするためのものが用意されているのでそれを使うといいですね。
他にも

github.com

VCRという一度だけ外部と通信を行って、それをymlにダンプしてそれ以降はそれを使って
テストを実行していくという便利なgemもあります。
まだ使ったことはありませんが、mockデータを作るのは面倒なので、一度使ってみたいものです。

github.com
webmockというそもそもhttp accessをもモックしてしまうものもあるので
世の中便利ですね。

間違っている部分や疑問に思っていることがありましたら、
かずま (@kazumalab) | Twitter
か、こちらのコメントまでお寄せください。

【Unity アセット真夏のアドベントカレンダー 2017】世界をローポリにしてやろうか!

かずまです。 今日は「Unity アセット真夏のアドベントカレンダー 2017」の12日目の記事になります。

昨日の記事

オノッチさんCharacter Particle!でした。

肝試しとかに使えそうで、夏にピッタリ!?(ホラー…!)

今回書くもの

さて今日は「世界をローポリにしてやろうか!」というタイトルで書いてきます。

今回メインで紹介するアセットは

このアセットになります。 お手頃価格で購入できますね!

ローポリのアセットって結構あるのですが、雰囲気に合わせたりするのが難しかったりします。 あとは通常のゲームを作ってて途中でローポリにしたい!そんな場面もあると思います。

そこでこのアセットです! モデルは準備する必要がありますが、それをローポリに変換してくれるEditor拡張のアセットです。

導入

インストールすると次のようになっています。

f:id:kazumalab:20170811231200p:plain

Editor拡張でwindow/VaccumShardersにビルドするためのEditorが入っているのでそこを開いてみます。

f:id:kazumalab:20170811231403p:plain

試して見る

とりあえずサンプルでSphereをSceneでローポリに変換してみます。

f:id:kazumalab:20170811231938p:plain

Sphereを指定して、今回は何も設定せずにGenerateしてみます。

f:id:kazumalab:20170811231955p:plain

すごく簡単にできました。 MeshもSharderはデフォルトのを使わずに独自で新たに作っています。 今回はrestoreしないにはチェックを入れていないので、Project内にMesh、Material、Prefabを作ります。

f:id:kazumalab:20170811232504p:plain

名前も自由に決められます。

できるMeshの頂点数

デフォルトのSphereは以下のようになっています。

f:id:kazumalab:20170811232608p:plain

頂点数 : 515, ポリゴン数 768 ですが、これを変換すると以下のようになります。

f:id:kazumalab:20170811232735p:plain

頂点数 : 2304, ポリゴン数 768 頂点数はかなり多くなっちゃいましたが、ポリゴン数は変わらず生成できました。

ちょっとAsset Storeにあるものをコンバートしてみる!

f:id:kazumalab:20170811234009p:plain

よく観るこのアセットです。

これをほいっと!やってみます。

f:id:kazumalab:20170811234626p:plain

若干テクスチャが変わっちゃう感じしますが、雰囲気が全く変わりますね。 これはいい感じっぽいです。」

Blenderで作ったモデルに適用してみる

f:id:kazumalab:20170811235425p:plain

適当に10分ほどで作ったモデルなので適当です。 これをコンバートしてみます。

f:id:kazumalab:20170811235554p:plain こんなに子のオブジェクトになっているのですが、大丈夫なのでしょうか? …親を指定すればすべて変換されます!

Materialのカラーだと白色になる

f:id:kazumalab:20170812001052p:plain Textureで色付けされていればコンバートがうまくいくのですが、マテリアルの色は引き継がれないようです。 手動で入れてあげればこんな感じになりました。

f:id:kazumalab:20170812001423p:plain

木のマテリアルは1つのオブジェクトで2つのマテリアルを使っていたため、コンバートした時に消えちゃいました。 残念。まぁそんな設計しなければいけるのですが。

ちょっと寄り道

いい感じになってきたので海もつけたいですね。 そこで、

これを使います! アニメーションもついていて、オブジェクトに接触する部分は水しぶきが発生します。 結構すごい。

f:id:kazumalab:20170812002230g:plain

ローポリすごーい!

まとめ

今回はローポリにしてしまうアセットを使ってみました。 これが使えればモデルを作るときにローポリで作らなくても良さそうな感じです。

明日!

明日の「Unity アセット真夏のアドベントカレンダー 2017」担当は Limes@XRDeveloperさんで「Set Pass Callを劇的に減らす「Mesh Baker」の使い方と、いくつかのモデルをHoloLensで表示した動作結果」です! Hololens!Mesh Backer!気になるものばかり!お楽しみに!

おさらい

今日使ったアセットをまとめました。

初めてのRubyGemsを作りました。

かずまです。
今回は色々困っていることがあってGemを作りました。

作ったもの

Haml-lintをAutoCorrectするものです。
今回はHash Attributeのあとにスペースを自動で入れてくれるものです。

既存のプロジェクトにHaml-Lintを入れたのですが、かなりHashのあとにスペースがなかったりしたので、これは手動でやっていたら果てしない時間がかかると思って作りました。

GlobalなRuby Gemsにはしないつもりです。
自分にとって必要だったので作成してローカルビルドして、ローカルインストールして使っています。

github.com

検証2日、実装1日ほどで作成したのでリファクタリング等はできていませんが、きちんと置き換えてくれると思います。

テストのカバレッジを上げていきたいですがmockを使ってテストを書きたかったので若干そこはまだです。

使ってみる

# bundle exec haml_lint_auto_corrector load file_name.html.haml

今回のlintの規約として

%div{ class: "sample" }
  %button{ class: "button" }= "Sample #{@user.name}"

ハッシュの間の値の前後にスペースを一つ入れるようにしないとLintで引っかかる場合に有効です。
上記コードであれば何もせずにそのまま終わります。

%div{class: "sample"}
  %button{class: "button"}= "Sample #{@user.name}"

このコードだとLintに引っかかるので自動で一つ前のコードのようにスペースを入れた形に置換してくれます。

Haml-lintにはAutoCorrectがない

Rubocopのように自動で置換してくれる機能がありません。
issuesにはたまにあがってくるそうなのですが、作者が好まないのか、Closeされるようです。

もちろん、AutoCorrectを使うのはあまりいい手だとは思えませんのでもしそれで作らないというのであればわかります。

ただ、これから作るプロジェクトであれば、作らなければOKなのですが、既存のプロジェクトだとかなりのコード量にもなっているので治すのは一苦労です。
ですので、使う用途としてはhamlファイルをいじるときに面倒なのでこのコマンドを実行して、自動修正して、あとのLintErrorは自分で修正する形がいいと思います。

実装

今回の実装

  • Pathからファイルを取得
  • バックアップを作成
  • nodeによる分割
  • 正規表現でlintに引っかかったらnodeの中身を置換
  • contextでマージ
  • バックアップが不要になったら削除

node一つがファイルの1行になっています。
contextが、置換したあとのデータファイルを持つイメージです。

難しい部分

面倒だったのが、Stringの中にインスタンスを入れて動的に変更する部分です。
上の例のコードであれば"#{@user.name}"です。

マッチしたら変更しない、にしようと思ったのですが、
1行レベルの正規表現マッチだったので、結構面倒な実装になるなーと思ったので、
計算量的には増えますが、一旦Hash全てにスペース入れたあとにマッチした部分だけもとに戻すということをしています。

if is_string_interprolation
  code_line.gsub!(/\#{\s.*\s\}/) do |word|
    word.gsub!(FRONT_REG_SPACE) { |w| "{#{w[2]}" }
    word.gsub!(BUTTOM_REG_SPACE) { |w| "#{w[0]}}" }
  end
end

色々ツッコミどころはありそうなコードですが、
そういった場合...気になったらpullrequestいただければ嬉しいです。
後にリファクタリングする予定ですが。。。

ということで今後もサービスを効率よく進めるために色々作りつつコードの綺麗さを上げて行きたいと思います。頑張ります。

Unity SteamVR Pluginのソースを読む Part2

かずまです。

今日も少しSteamVRのAPIを読んでいこうと思います。
前回は後半寝ぼけながら書いて...いました。

f:id:kazumalab:20170704070157j:plain

SteamControllerが有効になったところの処理を追っかけて終わりました。
今日はその続きから。

読むのもちろんこちら!


前提

まず、Controllerを動かすための分類分けをしておきます。

APIを提供している部分

  • SteamVR_Controller.cs

Controllerの実装を行っている部分

  • SteamVR_TrackedObject.cs
  • SteamVR_TrackedController.cs
  • open_api.cs
  • SteamVR_Utils.cs

基本開発で使うものはSteamVR_Controllerだけをスクリプトで呼び出して、ボタンが押された!などと言ったCallbackを受け取ることになります。
前回の読み会はわかりにくかったので、今回はサンプルを出して、そこから辿っていこうと思います。

// 常にControllerの位置を取るサンプルコード
void Start () {
	if (trackedController == null) {
		trackedController = this.gameObject.GetComponent<SteamVR_TrackedController>();
	}
}

void Update () {
	device = SteamVR_Controller.Input ((int)trackedController.controllerIndex);

	Debug.Log(device.transform.pos);
}

上記コードのDebugの部分、Controllerの位置を常に表示しているスクリプトについて辿って行きます。

device.transform

まずはtransformを構成している部分のお話です。
通常のMonobehaviorのtransformとは型が違うようです。

f:id:kazumalab:20170704054557p:plain

SteamVR_Controller.csの49行目に来ました。
ここではまずSteamVR_Utils.RigidTransformがどんなものなのかを見て、その後ゲッターのインスタンス生成時の引数poseが何かについて見ていきます。

SteamVR_Utils.RigidTransform

f:id:kazumalab:20170704055607p:plain

RigidTransformメソッドが何をしているのか?

ここでは4 × 4のMatrixを受け取ってVector3に変換しています。
Matrix4x4.identityは恒等行列を返しています。

恒等行列はこれです。

 {
Matrix4x4.identity =
\left(
 \begin{array}{ccc}
   1 & 0 & 0 & 0 \\\
   0 & 1 & 0 & 0 \\\
   0 & 0 & 1 & 0 \\\
   0 & 0 & 0 & 1
 \end{array}
\right)

}

UnityC#のリファレンスにも載っています。
Unity - スクリプトリファレンス: Matrix4x4.identity

// 
public RigidTransform(HmdMatrix34_t pose) {
	var m = Matrix4x4.identity;

	m[0, 0] =  pose.m0;
	m[0, 1] =  pose.m1;
	m[0, 2] = -pose.m2;
	m[0, 3] =  pose.m3;

	m[1, 0] =  pose.m4;
	m[1, 1] =  pose.m5;
	m[1, 2] = -pose.m6;
	m[1, 3] =  pose.m7;

	m[2, 0] = -pose.m8;
	m[2, 1] = -pose.m9;
	m[2, 2] =  pose.m10;
	m[2, 3] = -pose.m11;

	this.pos = m.GetPosition();
	this.rot = m.GetRotation();
}

ControllerとかHMDの座標はMatrixで返って来てるのかな?という所感です。
次にここを常に読んでいる場所があるはずです。そこを見ていきます。
座標ゴニョゴニョはわかりやすいものを参考文献に乗せています。

値がマイナスになっている箇所は位置の場合はZ軸に対して反転させている、という解釈ですが間違っていればご指摘ください。
参考にした資料にも載っていますが、GetPositionメソッドに書かれている通りです。

// SteamVR_Utils.cs 117行目
public static Vector3 GetPosition(this Matrix4x4 matrix)
	{
		var x = matrix.m03;
		var y = matrix.m13;
		var z = matrix.m23;

		return new Vector3(x, y, z);
	}

三列目を使っていることがわかります。
matrixにはGetColumn(int)というメソッドがあるのでそれで取ってきても同じことができそうですね。

ん?あれ?おかしい。
Rubyのメタプロっぽくなってるのはなんで?

  var m = Matrix4x4.identity;

この行ではMatrixの型が入るはず、、、なのになんでSteamVR_Utils.GetPositionが呼び出されてるんだろうか。
詳細を見てみるとExtension method from SteamVR_Utilsと書いてあります。

とりあえず、"Extension method from" でググってみたところ、C#には拡張メソッドがあるみたいです。
それを使えばRubyのメタプロっぽい書き方ができますね。ただし、新たにメソッド名を定義しては使えないみたいですね。

ufcpp.net

なので、ここでGetPositionの引数にthisが入っているのはそういうことだったんですね。
このクラス内だけで呼び出せる拡張メソッドということですね。
気になるのでこの話題は今度プログラム書いてみようと思います。

pose(TrackedDevicePose_t)

次にSteamVR_Utils.RigidTransformのインスタンスを生成する時に使用しているposeについて見ていきます。

f:id:kazumalab:20170704055500p:plain

poseはTrackedDevicePose_tという型を持っていて、それはstruct型になっています。
Trackingするもの(HMDとかControllerとかTracker)の情報を構造的に持たせるために作っています。
次のopen_api.csにかかれているTrackedDevicePose_tです。

// open_api.cs 3944行目
[StructLayout(LayoutKind.Sequential)] public struct TrackedDevicePose_t
{
	public HmdMatrix34_t mDeviceToAbsoluteTracking;
	public HmdVector3_t vVelocity;
	public HmdVector3_t vAngularVelocity;
	public ETrackingResult eTrackingResult;
	[MarshalAs(UnmanagedType.I1)]
	public bool bPoseIsValid;
	[MarshalAs(UnmanagedType.I1)]
	public bool bDeviceIsConnected;
}

StructLayout

最適なレイアウト(メモリレイアウトの話)をするために利用します。
そのためにまずはメモリのアラインメントを知る必要があるらしい。

前提としてメモリの読み書きは4、8の倍数になっている方が読み書きが早いので
プログラミング言語ではアドレスがきれいな倍数になるようにフィールドを並び替えます。

C#ではStructLayoutを使うことでそのフィールドをカスタムできるらしいです。
そもそも個人でゲーム作ってる時にアラインメントを気にしたことはなかった、と言うより初めて知りました。(恥ずかしい)

LayoutKind.Sequential

これは宣言した順番に並べていく手法。
未使用であってもきちんとそのメモリを確保します。

他にも

  • Auto: コンパイラー裁量で並び替えを認める
  • Explicit: 複合型の作者が明示的に位置を指定する

もしかすると今後使うところがあるかもしれないのでメモします。

MarshalAs(UnmanagedType.I1)

マネージ型とアンマネージ型

マネージ型はGC(ガーベージコレクション)が自動的にいらなくなったオブジェクトを破壊してくれたりする。
ココらへんは自動でメモリ管理を行ってくれないようになっているの管理しないと行けないのだろうと予測している。
MarshalAsを使うことで、マネージ型からアンマネージ型への変換が可能になるそうです。
ココらへんは個人的に若干難しく感じます。

I1

1 バイト符号付き整数。 このメンバーを使用すると、Boolean 値を 1 バイトの C スタイル bool (true = 1、false = 0) に変換することができるみたいです。ってかC#ではそもそも1と0のboolはできなかったのか....普段は使わないけど。

d.hatena.ne.jp

あとbool型をそのまま使うと8bitあるそうです。
bbs.wankuma.com
若干信憑性に欠けるソースかもしれませんが...。

今日はここまで!
これでtransformに何が入っていて、どのように値が生成されて...までが読めたはずです、はず。

最後に

今日はControllerが動いているところのソースを分解してみました。
このような感じでControllerが動いているんだなーという所感です。
若干これでいいのか?という感じで終わりますが、読みにくくはないかな〜という感じです。
正直openvr_apiについては5000行近くあるのに少ししか読めてないので結構つらそうだなーという...。

気になる点があればご指摘くださいませ〜。