モブプロ向きでない環境でモブプロする工夫を頑張った話

[1年ほど前にペアプロ･モブプロを導入した話はこちら](https://qiita.com/YudaiTsukamoto/items/06b426f4dbee268d5035)(宣伝)

# モブプロ向きの環境
普通モブプロの(物理的な)環境といえば, 

* 大きいプロジェクタないしはモニタ
* そのモニタを全員が快適に見られる椅子の配置
* そのモニタに繋がっている1台のPCとPC用の机

が必要である.
この動画にあるような光景がおそらく典型的だと思われる.
https://youtu.be/dVqUcNKVbYg

あるいはそこまで大きいモニタでなくても[このスライドで紹介されている写真](https://speakerdeck.com/takaking22/mobupuroguramingutoiudong-kifang-number-devlove?slide=25)のようにモニタを囲んで輪になって座っても大丈夫だ. 大抵のオフィスにある会議室ならこの環境が既に整っている.

# 我々のオフィス環境
自分のチームでもモブプロしたいが, 圧倒的にモブプロ向きでない環境でとても困った.

* でかいモニターなんか無い
    * あったとしても置く場所など無い
* プロジェクタを投影する場所も確保できない 
* 4,5人で1つのモニタを快適に見る椅子配置をするスペースがない
* 会議室は常に埋まっている

ということでモブプロ環境を新たに用意するのは完全に不可能なので, **普段自分たちが使っている自席をモブプロ環境にしてしまう**のが現実解となった.

## 自席の環境
我々の自席を雑な図にするとこういう感じで, エンジニア5名が長机に横並びで座っている.
![スライド1.png](https://qiita-image-store.s3.amazonaws.com/0/48596/32c5c6a6-0392-3a90-187a-5523be327181.png)

なんだか図にすると伝わりづらいがとにかく狭い.
少し離れたところにもう1名エンジニアが座っているので, この狭い自席周辺で6名でモブプロをしたい.

例えばドライバーが真ん中に座り, ナビゲーターもドライバーの真正面のモニタを見ることにしてモブプロするとこうなる.
![スライド2.png](https://qiita-image-store.s3.amazonaws.com/0/48596/363e494d-8884-19fa-13d2-2089f03af185.png)

まず通路を完全に潰してしまう. 自席付近はしょっちゅう人が通り過ぎていくところなので(それもまた集中を削がれるのでつらいが)できれば人が通れる状態にしてあげたいのだが, どうやっても通路は潰れてしまう.
で, 通路を潰してまで集合しているのになんと**後ろの2名か3名かはお互いの体が邪魔だったりモニタから遠すぎたりでロクにコードが読めない**のだ.

# モブプロをするための工夫
これでは楽しいはずのモブプロも全く楽しくなくなってしまうのでなんとかならんのかと考えた.

## 1つのモニタを見るのをやめる
モブプロの説明として｢1つのモニタと1つのPCを使って複数人でプログラミングすること｣などと書かれていることが多い.
いろいろ考えた結果, 我々の環境においてはモブプロの定義(?)であるところの**｢1つのモニタをみんなで見る｣という前提をブチ壊せばなんとかなるのではないか**というところに思い至った.

そこでこういう[HDMIスプリッターと呼ばれているもの](https://www.amazon.co.jp/dp/B07BHJ3YC3)を購入し, **ドライバーのモニタに写っている映像を横並びになっているモニタ全てに写す**ことにした.
![スライド3.png](https://qiita-image-store.s3.amazonaws.com/0/48596/13bfa6e0-2217-fece-9809-4e203ffba891.png)

これで各モニタにドライバが見ている映像と全く同じ映像が写せるようになったので, ドライバーの周りにナビゲーターが密集する必要がなくなった.

これでコードが見えない人もいなくなったし, 通路も確保できたし, 当初の問題は概ね解決された.

### 新たな問題発生
これで万事うまくいくと思ってウキウキでモブプロを開始したが, ものの数分で新たな問題が発生した.
![スライド4.png](https://qiita-image-store.s3.amazonaws.com/0/48596/bce6676d-112d-74cb-ecd7-3f19219e7ecf.png)

ナビゲーターがコードに口出ししたくなったときに, **自然と自分の目の前のモニタを指さして話し始めてしまうのだ**.
当然ドライバーは指さされたモニタを見ていないので, どこを指さしているのかわからず, ナビゲーターが何を言いたいのかもわからない.

これはみんなで1つのモニタを見ているときには全く意識していなかったし予想もできなかった問題で, 初めてこの問題にぶち当たったときはチームみんなで爆笑した.
なんせ**｢指さしてもわからないから行数を言うとか言葉でわかるようにドライバーに話しかけよう｣**とみんなで声掛けしあって注意していてもみんな自然と指が出てしまうのだ.

ナビゲーターがいかに指というコミュニケーションツールに頼りきりで, それが失われるとこんなにコミュニケーションが取れなくなるものかと驚いた.

## ドライバーに指示出しするツールを作る
指さし縛りで数日モブプロしてみたが, やはりどうにもナビゲーターとドライバーの意思疎通がうまくいかずに困ってしまった.
ちょっと考えて, ｢要するに見てるモニタが別でもドライバーとか他のナビゲーターに対して指に相当する何かを提示できればいいんだろ?｣と思い, テレビゲームっぽい感じにすればいいじゃんというところから着想を得て**ナビゲーターが画面上にポインターを表示できるツール**を作った.

![8ou17-zsxjx.gif](https://qiita-image-store.s3.amazonaws.com/0/48596/6b6c7c14-faff-4ada-e712-f873f77a6fbf.gif)

ゲームパッドを使って画面の赤いポインターをグリグリ動かせる(GIF画像見づらかったらすんません).
このツールを適当に**mobpro-pointer**と名付けた.

## mobpro-pointer
https://github.com/takuan-oishii/mobpro-pointer

たまたま手元にNintendo SwitchのJoy-ConがあったのでJoy-Con前提で作った.
ゲームパッドのボタンあたりの数字がガッツリハードコードされており, 恐らくJoy-Con以外のゲームパッドを接続してもまともに使えない.
ElectronもGamepad APIも触るのがはじめてでいまいち勝手がわからずエイヤで完成させてしまった.
いい感じにしてくれる方からのプルリクをお待ちしております.

### 使い方
Electronを使って作ったのでリポジトリをcloneして`electron`コマンドで起動すればすぐ使える(はず).
起動したらBluetoothでJoy-Conを接続する.
Joy-Conは片方を普通に横持ちで持つ. Rボタンを多用するのでストラップも付けておくとRが押しやすい.
Joy-Conは何個接続してもOK. Joy-Conの数だけポインターを表示できる.
ゲームパッドごとにポインターの色を変えるようにしているのでみんなでポインターを表示させても自分のポインターを見失わずに済むはず.

ポインターはRボタンを押している間だけ表示される. 
ポインターはスティックで移動できる. ポインター見失い防止のために一度Rボタンを離して非表示状態にすると, 再びRボタンを押して表示状態にしたときには画面中央にポインターがくる.
Aボタンを押しながらスティックを倒すとポインターの移動速度が遅くなる. Joy-Conだとスティックの倒し度合いを取れなかったので(ちょっと倒すのと全部倒すのが同じ値になる), こういう仕様にした.

### 仕組み
画面いっぱいに透明なウィンドウを常に最前面に来るように配置し, さらにそのウィンドウのクリックも無効にしている.

Gamepad APIでUSBやBluetooth接続されているゲームパッドからの入力を取得している.
Gamepad APIの仕様に詳しくないのでよくわかっていないが, アプリケーション的にはゲームパッドの接続数に限度を設けていないのでGamepad APIの限界まで何個でもゲームパッドを接続できるはず. 自分の手元では2個しか試してないけど.

ポインターの描画はCanvasを使っている.

# まとめ
HDMIスプリッターを使って1つの画面を見ないようにしたのはかなりアタリだった. これだけでかなり快適にモブプロできるようになった.
mobpro-pointerは作ったばかりでまだ実戦投入していないが, 試しにちょっと同僚に触ってもらったところ**使えそうじゃん!**との評をもらった.

**モブプロ環境は頑張ればなんとかなるぞ!!**

# 余談
使えそうじゃんとは言われたが, ポインターを常時表示させて謎の遊びを始めてしまってもいるのでもしかしたら**ツールの出来はいいけど人間のほうがダメ**という理由で使えないかもしれない.