Amebientのベースじゃないシステム

phi16です。Amebient Advent Calendar 5日目、前回私は基礎の話をしたので基礎以外のシステムの話をします。
いろいろあります。
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## バルブ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/ae60192a-0ad1-bcd7-c175-27344fca7c3a.png)
回ります。水も出ます。

これが生まれた理由は**Capが作ったから**で、丁寧にメッシュが分かれていたので**そういうことかな**と思って勝手に回せるようにしました。気づいたら**音楽的に便利な役回り**になってました。

捻っていくと段々水の出る間隔が増えていきます。最初は64拍に1回、だんだん増えて2拍に1回、そこからは[ユークリッドリズムの要領](https://twitter.com/phi16_/status/1298311044595511297)です。出る量を調節できる時点で結構便利なんですが、加えて**水面に当たったときの音が毎回違う**んです、これ。あと水音自体が違う (そのうち書かれるかしら)。だから水張って(5,8)のリズムで出しとくだけでも結構楽しい。

で、こいつの実装は**めちゃくちゃadhoc**です。まぁ元々「リズムは変化しない」前提でBakeとかやってたのでね…

まず5つのバルブそれぞれに専用のリズムタイプが割り当てられています(16～20)。そして、バルブを回すと**`Metronome`に通知が行って**、リズム計算時の定数を書き換えるようになっています。さらに`Metronome`が直接`DropRain`のマテリアルパラメータを変更します。これで音も見た目も揃うというわけ。設計としては悪なんですがまぁ、こういうのはadhocな方が良いこともあるもので…。
ちなみに水音が変わるのは`Rain`が「自身がバルブ用の雨粒であれば、再生直前に`pitch`を5種からランダムに選ぶ」ことで実装しています。

&nbsp;
ちなみにバルブ回すのはこう、**ぐっ**と… ちゃんと書くと「前フレームの`rotation`からの回転差分 (Quaternionの除算) を回転ベクトル形式にして軸成分を取り出し、角度に加算、その角度を元に見た目とリズムタイプに反映」する感じでやってます。閉じた状態からは一切逆回転しないけど、開きすぎて限界まで行くと段々回らなくなるのは……**ぐっ**と…。具体的には `maxAngle + (1 - Mathf.Exp(-(angle-maxAngle)/r)) * r` で出来てます (`r = 60°`)。これは`angle = maxAngle`のときに一回微分まで連続なので綺麗につながります。
`rotation`をそのまま使わないのは**軸が固定されているという事実に反する**からです。なので「回転する度に軸回転以外の成分が消え去る」解釈をしていて、そうするとこれになります。
デスクトップで掴んだ場合は**上下移動**を拾うようにしています。これは最終結果 (水が下に多く落ちること) への願いを前提とした操作体系です。直感的かどうかは…わかりません。私は良いと思いました。

あと同期はUdonSyncedでやっていて、角度そのものを同期させています。他人が動かした時にもスムーズに回るように「見かけの回転角が段々近づく」処理を入れているんですが、手を離した時にみょんって戻っていくのはこの辺の仕組みの流用ですね。

&nbsp;
余談ですが、バルブは微妙に前後にも動いています。なんとなく…。

## 鉄パイプ

ロッカーが開くと使えるようになる鉄パイプですが、これによって結構な仕様変更が生まれた過去があります…。

鉄パイプが生まれた理由は**ありそうだったから**ですね。そして**楽器を叩けそう**ですよね。そうだね。特にこれは「楽器と楽器をぶつけても音が鳴らないことへの回答」になるので、そういう解釈を考えると必須だとも思いました。

**どう鳴らすか**ですが、とりあえずこれも金属物体なので`Percussion`としての扱いが基本です。†継承†とか考えたくなるかもしれませんが、Udonなのでまずありません。そこで子GameObjectに`Mallet`というUdonを追加して、そこに色々書くことにしました。
基本的に動作はほぼ分離されていて、`Mallet`は「自身が`Percussion`に衝突した場合にそれを相手に報告する」という動作をするだけです。それを`Percussion`は記憶して、**次の16分のタイミングで鳴らす**ことになります (おかげで適当に叩いても合うので楽しい)。

しかし、**世界に配管が存在する**ことで話が変わってきます。金属なんです、これ。つまり音が鳴るんです。今までは雨の位置が固定だったので配管が鳴らなかったんですが、鉄パイプで叩けば鳴ります。そうですよね。
ということで`Instrument`というUdonが生まれました。これは`Mallet`の解釈だけをする`Percussion`です。`Mallet`は衝突対象が`Percussion`**か、`Instrument`**を持っているときに相手に通知をするわけです。

さて、そしてさらに問題が出てきます。**AudioSourceの位置**。元々は雨が衝突していた場所だったはずなんですが、今回は**鉄パイプが当たった場所**です。衝突判定で得た衝突点はありますが、**同期できません**。楽器サイズなら原点でもバレないかもしれないけど、配管になるとさすがにバレます。
ということで… まず衝突が発生したら**鉄パイプの番号 (0～3) と共に`SendCustomNetworkEvent`を楽器に送信**。そして現在の鉄パイプの存在領域の中で**最も楽器の原点に近い座標**を算出、そこにAudioSourceを設定しています。こうなった理由は「鉄パイプのどっちを手元に持っているかわからなかった」から。まぁ、完璧に作動しているわけではないですけどね。

鉄パイプの衝突判定は………**Raycastです**。棒だからね。ただちょっとおもしろい仕組みが入っていて、**曲がってる方の先端は、前フレームからの差分も判定に含まれています**。なんていうんだ。こういうこと。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/69fd49cd-c8a5-b03d-ebed-6081897fbe45.png)
赤だけでなくオレンジの部分の判定も取っているということです。おかげで結構激しく動かしても反応してくれます。
ちなみに線分で判定取るのはRaycastの`maxDistance`に棒の長さを入れてるだけです。

&nbsp;
概ねこれでいいんですけど、Udon的な問題が1つあって。「pickupしている間に`Mallet`が動作する」ってやる為にはpickupを検知する必要があるんですが、`Percussion`の`OnPickup`しか呼ばれないんですね。そうね。
なので`Percussion`には子に`Mallet`があるかを保持させて、イベントを横流しする必要がありました。というわけで**世界に鉄パイプを追加するとここまで追加する羽目になる**という話でした。

&nbsp;
あとおまけ話としては…水中だと鳴らないようになってたりはします。鳴らなそうなので。

あとそういえば飯盒も鳴ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/3404cca7-2960-5a4f-972c-4378a8796dba.png)
鳴るし、**揺れます**。結構知られてなさそうなイメージ。わざわざこの為に`HangObject`ってUdonまで作った。物理挙動はめちゃくちゃ適当だったりします、円筒形に衝突して撃力が発生したと仮定してぷらんぷらんと。もちろん同期しませんが、まぁそれは諦めがついています。
そういえば[バケツの物理挙動](https://phi16.hatenablog.com/entry/2020/04/22/233910)については随分前 (4月22日) に細かく書きましたね。長い伏線でした。伏線です。伏線か…？

## 電子楽器1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/df4f2f88-b181-e363-6415-542ae312620e.png)
この子たちのことです。名前無くてごめんね…。

これはらくとさんが電子音が欲しいと言ったところから始まり、**一度世界解釈に持ち込まれ**、結果として帰ってきたもの、みたいです。最初は地震計とか電子レンジとか言ってた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/63e4b9e9-b5fb-2660-ccdc-a401ed7793d6.png)
つまりあの機械フロアの時代の産物で。だから色味が似通ってて。**雷で同時に電源が入るんです。**

制作に関しては、まずらくとさんに欲しい仕様を聞いて、それを解釈したりしていろいろ弄りつつ、Capに装置を作ってもらって実装。完璧ですね。

この子は「16種類の音が外周に配置されていて、雨粒が落ちてきたときにカーソルの現在位置の音が鳴る楽器」です。音群は**センサの向く方向**によってランダムで決まっているんですが、鉄塔に向ければ向けるほど明るい音の割合が増えます。実は。

そこまではらくとさんの仕様だったんですが、私が追加したのが**傾いてる方向で位相を調節する仕組み**です。まぁ単純にランダム性と複雑性を増やしたかったのです。そしてこれらを**Visualとして解釈して**出来たのが…この謎図形です。私の趣味が10割ですね。

まず「これは通常の楽器とは違う」ことを示す必要があって、文様があるのはもちろんなんですけど加えて**中央の円の領域でしか音が鳴らない**ようになっています。これが「世界の世代の違い」というかなんというか… まぁ**文脈の分割**をしています。これは意味的にも必要で、**表面と裏面で違う**んですね、微妙に。その中間が無いので、「どこに当てても音がなる」楽器とは違う性質を持つわけです。

そしてそれに接続しているのが「傾いている方向に意味がある」ことを表すカーソル概念。「基準方向とそこからのズレ」っていう表現の仕方は [これ](https://twitter.com/phi16_/status/1231860286514061312) を作ったときの発想で、ある意味では供養になってたりします。下方にある浮いた扇型は**錘**です。あと上方にある4つの点は乱数の種 (の下位4bit) を表していて、224通りの選択を少しだけ可視化しています。

あと周りの「音を表す模様」がありますね。元々**中央から接続したら音がなる**という因果を考えていたので、放射状に線が伸びているのがベースとして頭にありました。ジャストの位置で接続する必要がないということをカーソルの先端が表しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/1de7f62e-8039-d91a-b1d3-d5f86483c3af.png)
この謎の記号群は音程を直接表現しています、実は。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/d57934ce-a088-7c86-071c-ee874207695c.png)
らくとさんから頂いた音のリストをどうにか解釈しようとして… 表 (F) と裏 (Dm) の基準音は単純にする、音が低いほど中央の線が長い (波長が長いので)、DとA・FとCがそれぞれ完全五度になるので対称のペアにして、残ったGはそれ自体を対称にする (実際良い位置に居るので)、基準音と同じ音にはちょっと特別なマーカーつけて、そしてそれらの結果**いい感じにかっこよくなるように**。みたいな感じで考えました。
**細かいことはまぁ良くて**、必要なのは「センサの向きが音の分布に影響することを伝える」くらいです。建物に向けると明らかに分布が偏る (基準音と同じ音しか出ない) のは観測できるので、あとは因果を結び付けられるかどうかですが… まぁ「調べようと思うとわかる、調べなくてもたのしい」くらいになってたんじゃないかなと思っています。勝手な期待です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/c8d612d9-dfbe-8d04-01c2-a24fd6d80241.png)
らくとさんの考えた仕様を (少なくとも私には) 良い感じに見た目に落とし込めたかなと思っています。そして意味わからんと思ってくれていたらそれは嬉しいです。意思が読めないが**確固として意思が存在する**ことはわかるもの、が好きです。

あと音が鳴ったら光ってエフェクト出すのはまぁ、ほしいですよね。ほしいのでつけました。

&nbsp;
ちなみに実装は… ええと、まず`SymbolControl`というUdonがあって、これを子に持つ`Percussion`は自身が電子楽器であると認識します。そして音を出すときに`SymbolControl`に頼んで、`AudioSource`に`pitch`と`clip`を設定してもらいます (音程があまりにも違うので3種類のAudioClipを使っています)。同時に「カーソル位置のエフェクトの再生残り時間」を1に更新。`Update`で段々減らしていきます。
エフェクト情報とあと勿論16個の音の情報をマテリアルパラメータに突っ込んで、[Geometry Shaderでごりごり](https://twitter.com/phi16_/status/1289467909639569408)と。ちなみにエフェクト部分も同じシェーダです、余ったポリゴンを使ってます。どうせUnlitだったから。
自身を電子楽器だと思いこんでる`Percussion`は自身に衝突した水滴 (と鉄パイプ) を無視するようになるので、これでいい感じに。あと`SymbolControl`に設定を頼むときには衝突点の情報も送っていて、中心に衝突することの検知はそっちでやっています。委譲って感じ。

あとこの子の挙動は`position`と`rotation`で確定するので何も考えなくても同期します。ありがた。

## 電子楽器3

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/6bcdaf42-78b7-694f-0531-3e8a31fa6484.png)
この子たちです。**2**はね、リソース不足でなくなったの…。

これもらくとさんの持っていた電子楽器構想にあったもので、特に私としては**水を掬う行為の大きな目的**になるのでめちゃくちゃ欲しいなとは思っていました。「水は掬えるべき」(掬えそうなので) なんですが、その願いの**先**に成れるんです。機能の存在価値を示すことが出来る。すごいギリギリだったけど作って本当によかったと思う。

まぁ水を入れると音が鳴るとはどこにも書いてないんですけど、水滴当てても音鳴らないし、色々試していったら見つかる範疇かなとは思います。思っています。

この子の見た目はすごい素直で、**音の高さそのもの**ですね。現在の水の量に対応する位置に光るラインが来ます。それだけです。

いくつかある固定ラインはまぁ意味があって、高い方から順にD,C,A,Gです。そして最高音と最低音はF。そうすると**F,D,AでD minor**、**F,C,AでF major**です。誰でも調和させられます。うれしい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/ffc160ec-0e98-c0c5-bf83-827cbc5843f5.png)
だから3つあるのか～みたいなことを私は考えていたんですが、なんかこう、本領はそんなもんじゃなかったらしい。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">From Amebientの練習風景/電子楽器3(開発コード)両手でグルグルやるのめちゃくちゃに楽しいんですよね... <a href="https://t.co/PQJiSgwYqz">pic.twitter.com/PQJiSgwYqz</a></p>&mdash; らくとあいす (@rakuraku_vtube) <a href="https://twitter.com/rakuraku_vtube/status/1285158987344670723?ref_src=twsrc%5Etfw">July 20, 2020</a></blockquote>

これ聴いたときは本当にびびった。これ**で**演奏するという発想が無かったので…。作ってよかったと心から思いました。

さて、実装は… まず`Percussion`ではあるんですけど無音のAudioClipを鳴らしています。そして`SymbolControl`があるのも同じ、`mode`が3になるだけです。実際に音を鳴らしているのは`SymbolControl`そのもので、AudioSourceをくっつけてあって`volume`と`pitch`を弄っています。水面の高さは`Surface`っていう別のUdonが持っているので、そこから情報を貰って音及びマテリアルパラメータに適用、という感じ。
同期は`Surface`がやってるのでこっちでは特に考えることはありませんでした。そんなとこかな。
あと水が入ってないときは (音量が0になった後で) ちゃんと`Stop`するようにもなってる。

そういえば海に入るとフェードアウトするようにもしてますね、**海にはあのambientだけがある**ようにしているのです。そういう願い。

## まとめ

基礎じゃない方の仕組みの説明をしました。全体的にadhocな実装になっているんですが、これはわざとです。

**使える情報をできる限り使う方が、良いものができる。** [ノーフリーランチ定理](https://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%BC%E3%83%95%E3%83%AA%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%81%E5%AE%9A%E7%90%86)の私の解釈です。

特にVRChat及びUdonというとても小さい環境なので、できることからやっていかないと。

あと情報量というものは「新しく在る構造」から生まれるもので、**違う要素**を色々作る必要が出てくるのです。例えば電子楽器1には色味が違う子が居ますけど、それの持つ情報量は「電子楽器1」として吸収されてしまい、そう多くは感じないと思います (音的には重要性ありますけどね、それは**違う周波数帯**という点で大きく情報を持っているから)。
システム的には汎用性はめちゃくちゃ欲しいものですが、創作的にはそれは願いと相反するのです。いえ、最も望ましいことは**その願いの複雑性を内包できるほどの汎用性**に辿り着くことではあります。それは**世界の統一理論**です。でもそこまで辿り着くことは本当に難しい。
Amebientは「たくさんの食器」や「雨粒の落下」に対する汎用性は持っていますが、それをベースとした上で**汎用性から外れた領域が存在する**ということが、事実として世界の強度を上げているとは思っています。一応それぞれ解釈はありますし、ね。(まぁ、Amebientという世界にはそれ以上の外枠があるわけですけど…)

できる限りの汎用性を確保し**展開**させた上で、それを越えた領域の**展望**を見せる。それがいいんだと思います。

電子楽器3が有り得るなら結構いろんなことがありそうだと思えるじゃないですか。ね。

はい。以上です。音関連の説明はこれくらいだと思うので、以降の私の記事はVisualとギミック系の話になります。ではでは。