Webで動くピタゴラスイッチ風のおもちゃ

NHKの番組[「ピタゴラスイッチ」](http://www.nhk.or.jp/kids/program/pitagora.html)で放映されている人気コーナー、通称「ピタゴラそうち」。

私は社会人になってからも[DVD](http://amzn.asia/d/hZjJ94P)を買ったりしている大ファンなのですが、
いつか自分で作ってみたいなーと思いつつ、これまで手を出したことがありませんでした。

そんな中、今年は先輩にアドベントカレンダーに誘って貰えたため、
この機会にピタゴラそうちならぬ**ピタゴラプログラム**を作ってみようと思います。

# ピタゴラプログラムとは

そもそもピタゴラそうちとは…という定義は大変なので、今回は"それっぽいもの"を作ることを目的とします。

通常の「ピタゴラそうち」は物理世界で動いていますが、今から作る「ピタゴラプログラム」は
デジタルならではの様々な表現を使うことができます。

例えば

* 座標を指定して物理法則に関係なくオブジェクトを動かすこと
* ユーザによるインタラクティブな操作ができること
* オブジェクトを変形させること

などです。

今回はこのような**「デジタルだからこそできる表現」**を意識しつつ、
ピタゴラそうちっぽいプログラムを実装していきます。

# 使用するライブラリ

ピタゴラそうちといえば物理法則は欠かせませんので、物理演算のライブラリを使います。

google先生に「2D 物理演算 javascript」と聞くと様々な種類がヒットしますが、
今回は以前少しだけ経験のある**Matter.js**を使ことにしました。

【Matter.js】 http://brm.io/matter-js/

# 制作

ここから先はゴールを決めず、気の向くままに実装していきます。

## 0.準備

Matter.jsのwikiを参考に、canvas上で物理演算ができるように準備します。

https://github.com/liabru/matter-js/wiki/Getting-started

ちなみに上記のサンプルを動かしたものはこちらです。

https://codepen.io/masaoblue/pen/OrNqmK

## 1.重力に従ってオブジェクトを落とす

では、最初に動かすものを決めましょう。

[Matter.jsのデモページ](http://brm.io/matter-js/demo/#mixed)にも色んな動作がありますが、
今回は上記のサンプルを見ていてひらめいた**「社名の順に並ぶブロック」**を作ってみます。

何を言っているのかさっぱり分からないと思いますが、つまりこういうことです。

https://codepen.io/masaoblue/pen/maPoqY
![image1.png](https://qiita-image-store.s3.amazonaws.com/0/105321/5996a02c-ca53-d50c-fbd2-1127e615b017.png)

canvas上にカーソルを乗せるとブロックが落下を始め、最終的に会社名の順に並びます。
※Windows10&chromeのみ動作確認しています。

massでオブジェクトの質量を、restitutionで反発係数を指定しつつ、オブジェクトの初期配置と角度を微調整することで、割と高い確率で並び順を制御することができました。

## 2.衝突イベントを検知する

丸いオブジェクトを見ていたら、どうしても坂を転がしたくなってきたので実装します。

https://codepen.io/masaoblue/pen/bOpXbB
（上と同様、カーソルを乗せると開始します）

しかし直感だけで適当に坂を配置したため、隙間が狭くて玉が途中で止まってしまいました。
![image2.png](https://qiita-image-store.s3.amazonaws.com/0/105321/5eb34b77-6cb8-0cf7-27a8-336c0f97bcd4.png)

じゃあ坂の位置を調整して…と一瞬考えたのですが、今回はせっかくのデジタル空間。
どうせならリアルではできない動きにした方が面白そうです。

そこで、「坂を動かす」のではなく**「衝突を検知したら玉を縮小する」**という
デジタルっぽい方法で解決してみました。

https://codepen.io/masaoblue/pen/LMZEjw
![image3.png](https://qiita-image-store.s3.amazonaws.com/0/105321/eb0a1bae-9069-3ec1-09f4-78a2ae8790a9.png)
（小さくすれば解決！）

これで無事に一番下までたどり着くことができました。

※ちなみにBody.scaleメソッドを使ってcircleオブジェクトを縮小すると計算精度が落ちて動きがおかしくなります。
　https://github.com/liabru/matter-js/issues/326
　これに気づくのに1時間かかった…

## 3.オブジェクトに感情表現を追加する

さて、ここまで来ると、「玉」と書かれたオブジェクトに妙な愛着が湧いてきました。
（以降"玉ちゃん"と呼ぶことにします。）

デバッグのために何度も動かしているうちに、
　「恐らく玉ちゃんは壁にぶつかった時、どうすれば良いか悩んでいるだろう」
と妄想が膨らみます。

だとしたら、何か動きを追加することで**感情を表現してあげる**必要がありそうです。

では実際に作ってみましょう。


初めに、漫画などでよくあるビックリした時の表現（Σみたいなやつ）を作ります。
canvasではquadraticCurveToメソッドを使うと二次ベジェ曲線が引けるのでこれを使いましょう。

https://codepen.io/masaoblue/pen/vvKBjp

![image4.png](https://qiita-image-store.s3.amazonaws.com/0/105321/9115c349-877d-7b31-791b-1ee88c98f6c9.png)
（ビックリした時に出るやつ）

最終的に、玉ちゃんが止まった時のイベントを検知し、このマークを適切な位置に配置すれば完成です。

![image5.png](https://qiita-image-store.s3.amazonaws.com/0/105321/241b2c4a-18a0-352c-36e5-55d32fd2046b.png)
（ビックリしている玉ちゃん）


# できた！

最終版はこちら。

https://codepen.io/masaoblue/pen/OrXPdj

不思議なもので、さっきまでただバウンドしているだけに見えた玉ちゃんが、
今ではなんとなく**喜んで飛び跳ねている**ように見えてきませんか？

![image6.png](https://qiita-image-store.s3.amazonaws.com/0/105321/a89a9909-f742-0123-ac32-1493ae4be3ba.png)
（壁を越えられて喜んでいる玉ちゃん）

# さいごに

本当はもっと色々動かす予定でしたが、今日はここでタイムアップ。
後半はPromiseを書くのも惜しくてcallback地獄を作ってしまいました。

いつかそのうち気が向いたら、続きを作るかもしれません。

それでは、機会を頂いた先輩に感謝しつつ終わりにします。

最後までお付き合い頂きありがとうございました。

# 参考URL

- typescriptの環境構築
    - https://qiita.com/ochiochi/items/efdaa0ae7d8c972c8103
- Matter.jsでオブジェクトに文字列を表示する
    - https://stackoverflow.com/a/53109020