phi16です。Amebient Advent Calendar 5日目、前回私は基礎の話をしたので基礎以外のシステムの話をします。
いろいろあります。
バルブ
これが生まれた理由はCapが作ったからで、丁寧にメッシュが分かれていたのでそういうことかなと思って勝手に回せるようにしました。気づいたら音楽的に便利な役回りになってました。
捻っていくと段々水の出る間隔が増えていきます。最初は64拍に1回、だんだん増えて2拍に1回、そこからはユークリッドリズムの要領です。出る量を調節できる時点で結構便利なんですが、加えて水面に当たったときの音が毎回違うんです、これ。あと水音自体が違う (そのうち書かれるかしら)。だから水張って(5,8)のリズムで出しとくだけでも結構楽しい。
で、こいつの実装はめちゃくちゃadhocです。まぁ元々「リズムは変化しない」前提でBakeとかやってたのでね…
まず5つのバルブそれぞれに専用のリズムタイプが割り当てられています(16~20)。そして、バルブを回すと**Metronome
に通知が行って**、リズム計算時の定数を書き換えるようになっています。さらにMetronome
が直接DropRain
のマテリアルパラメータを変更します。これで音も見た目も揃うというわけ。設計としては悪なんですがまぁ、こういうのはadhocな方が良いこともあるもので…。
ちなみに水音が変わるのはRain
が「自身がバルブ用の雨粒であれば、再生直前にpitch
を5種からランダムに選ぶ」ことで実装しています。
ちなみにバルブ回すのはこう、ぐっと… ちゃんと書くと「前フレームのrotation
からの回転差分 (Quaternionの除算) を回転ベクトル形式にして軸成分を取り出し、角度に加算、その角度を元に見た目とリズムタイプに反映」する感じでやってます。閉じた状態からは一切逆回転しないけど、開きすぎて限界まで行くと段々回らなくなるのは……ぐっと…。具体的には maxAngle + (1 - Mathf.Exp(-(angle-maxAngle)/r)) * r
で出来てます (r = 60°
)。これはangle = maxAngle
のときに一回微分まで連続なので綺麗につながります。
rotation
をそのまま使わないのは軸が固定されているという事実に反するからです。なので「回転する度に軸回転以外の成分が消え去る」解釈をしていて、そうするとこれになります。
デスクトップで掴んだ場合は上下移動を拾うようにしています。これは最終結果 (水が下に多く落ちること) への願いを前提とした操作体系です。直感的かどうかは…わかりません。私は良いと思いました。
あと同期はUdonSyncedでやっていて、角度そのものを同期させています。他人が動かした時にもスムーズに回るように「見かけの回転角が段々近づく」処理を入れているんですが、手を離した時にみょんって戻っていくのはこの辺の仕組みの流用ですね。
余談ですが、バルブは微妙に前後にも動いています。なんとなく…。
鉄パイプ
ロッカーが開くと使えるようになる鉄パイプですが、これによって結構な仕様変更が生まれた過去があります…。
鉄パイプが生まれた理由はありそうだったからですね。そして楽器を叩けそうですよね。そうだね。特にこれは「楽器と楽器をぶつけても音が鳴らないことへの回答」になるので、そういう解釈を考えると必須だとも思いました。
どう鳴らすかですが、とりあえずこれも金属物体なのでPercussion
としての扱いが基本です。†継承†とか考えたくなるかもしれませんが、Udonなのでまずありません。そこで子GameObjectにMallet
というUdonを追加して、そこに色々書くことにしました。
基本的に動作はほぼ分離されていて、Mallet
は「自身がPercussion
に衝突した場合にそれを相手に報告する」という動作をするだけです。それをPercussion
は記憶して、次の16分のタイミングで鳴らすことになります (おかげで適当に叩いても合うので楽しい)。
しかし、世界に配管が存在することで話が変わってきます。金属なんです、これ。つまり音が鳴るんです。今までは雨の位置が固定だったので配管が鳴らなかったんですが、鉄パイプで叩けば鳴ります。そうですよね。
ということでInstrument
というUdonが生まれました。これはMallet
の解釈だけをするPercussion
です。Mallet
は衝突対象がPercussion
**か、Instrument
**を持っているときに相手に通知をするわけです。
さて、そしてさらに問題が出てきます。AudioSourceの位置。元々は雨が衝突していた場所だったはずなんですが、今回は鉄パイプが当たった場所です。衝突判定で得た衝突点はありますが、同期できません。楽器サイズなら原点でもバレないかもしれないけど、配管になるとさすがにバレます。
ということで… まず衝突が発生したら鉄パイプの番号 (0~3) と共にSendCustomNetworkEvent
を楽器に送信。そして現在の鉄パイプの存在領域の中で最も楽器の原点に近い座標を算出、そこにAudioSourceを設定しています。こうなった理由は「鉄パイプのどっちを手元に持っているかわからなかった」から。まぁ、完璧に作動しているわけではないですけどね。
鉄パイプの衝突判定は………Raycastです。棒だからね。ただちょっとおもしろい仕組みが入っていて、曲がってる方の先端は、前フレームからの差分も判定に含まれています。なんていうんだ。こういうこと。
赤だけでなくオレンジの部分の判定も取っているということです。おかげで結構激しく動かしても反応してくれます。
ちなみに線分で判定取るのはRaycastのmaxDistance
に棒の長さを入れてるだけです。
概ねこれでいいんですけど、Udon的な問題が1つあって。「pickupしている間にMallet
が動作する」ってやる為にはpickupを検知する必要があるんですが、Percussion
のOnPickup
しか呼ばれないんですね。そうね。
なのでPercussion
には子にMallet
があるかを保持させて、イベントを横流しする必要がありました。というわけで世界に鉄パイプを追加するとここまで追加する羽目になるという話でした。
あとおまけ話としては…水中だと鳴らないようになってたりはします。鳴らなそうなので。
あとそういえば飯盒も鳴ります。
鳴るし、揺れます。結構知られてなさそうなイメージ。わざわざこの為にHangObject
ってUdonまで作った。物理挙動はめちゃくちゃ適当だったりします、円筒形に衝突して撃力が発生したと仮定してぷらんぷらんと。もちろん同期しませんが、まぁそれは諦めがついています。
そういえばバケツの物理挙動については随分前 (4月22日) に細かく書きましたね。長い伏線でした。伏線です。伏線か…?
電子楽器1
これはらくとさんが電子音が欲しいと言ったところから始まり、一度世界解釈に持ち込まれ、結果として帰ってきたもの、みたいです。最初は地震計とか電子レンジとか言ってた。
つまりあの機械フロアの時代の産物で。だから色味が似通ってて。雷で同時に電源が入るんです。
制作に関しては、まずらくとさんに欲しい仕様を聞いて、それを解釈したりしていろいろ弄りつつ、Capに装置を作ってもらって実装。完璧ですね。
この子は「16種類の音が外周に配置されていて、雨粒が落ちてきたときにカーソルの現在位置の音が鳴る楽器」です。音群はセンサの向く方向によってランダムで決まっているんですが、鉄塔に向ければ向けるほど明るい音の割合が増えます。実は。
そこまではらくとさんの仕様だったんですが、私が追加したのが傾いてる方向で位相を調節する仕組みです。まぁ単純にランダム性と複雑性を増やしたかったのです。そしてこれらをVisualとして解釈して出来たのが…この謎図形です。私の趣味が10割ですね。
まず「これは通常の楽器とは違う」ことを示す必要があって、文様があるのはもちろんなんですけど加えて中央の円の領域でしか音が鳴らないようになっています。これが「世界の世代の違い」というかなんというか… まぁ文脈の分割をしています。これは意味的にも必要で、表面と裏面で違うんですね、微妙に。その中間が無いので、「どこに当てても音がなる」楽器とは違う性質を持つわけです。
そしてそれに接続しているのが「傾いている方向に意味がある」ことを表すカーソル概念。「基準方向とそこからのズレ」っていう表現の仕方は これ を作ったときの発想で、ある意味では供養になってたりします。下方にある浮いた扇型は錘です。あと上方にある4つの点は乱数の種 (の下位4bit) を表していて、224通りの選択を少しだけ可視化しています。
あと周りの「音を表す模様」がありますね。元々中央から接続したら音がなるという因果を考えていたので、放射状に線が伸びているのがベースとして頭にありました。ジャストの位置で接続する必要がないということをカーソルの先端が表しています。
この謎の記号群は音程を直接表現しています、実は。
らくとさんから頂いた音のリストをどうにか解釈しようとして… 表 (F) と裏 (Dm) の基準音は単純にする、音が低いほど中央の線が長い (波長が長いので)、DとA・FとCがそれぞれ完全五度になるので対称のペアにして、残ったGはそれ自体を対称にする (実際良い位置に居るので)、基準音と同じ音にはちょっと特別なマーカーつけて、そしてそれらの結果いい感じにかっこよくなるように。みたいな感じで考えました。
細かいことはまぁ良くて、必要なのは「センサの向きが音の分布に影響することを伝える」くらいです。建物に向けると明らかに分布が偏る (基準音と同じ音しか出ない) のは観測できるので、あとは因果を結び付けられるかどうかですが… まぁ「調べようと思うとわかる、調べなくてもたのしい」くらいになってたんじゃないかなと思っています。勝手な期待です。
らくとさんの考えた仕様を (少なくとも私には) 良い感じに見た目に落とし込めたかなと思っています。そして意味わからんと思ってくれていたらそれは嬉しいです。意思が読めないが確固として意思が存在することはわかるもの、が好きです。
あと音が鳴ったら光ってエフェクト出すのはまぁ、ほしいですよね。ほしいのでつけました。
ちなみに実装は… ええと、まずSymbolControl
というUdonがあって、これを子に持つPercussion
は自身が電子楽器であると認識します。そして音を出すときにSymbolControl
に頼んで、AudioSource
にpitch
とclip
を設定してもらいます (音程があまりにも違うので3種類のAudioClipを使っています)。同時に「カーソル位置のエフェクトの再生残り時間」を1に更新。Update
で段々減らしていきます。
エフェクト情報とあと勿論16個の音の情報をマテリアルパラメータに突っ込んで、Geometry Shaderでごりごりと。ちなみにエフェクト部分も同じシェーダです、余ったポリゴンを使ってます。どうせUnlitだったから。
自身を電子楽器だと思いこんでるPercussion
は自身に衝突した水滴 (と鉄パイプ) を無視するようになるので、これでいい感じに。あとSymbolControl
に設定を頼むときには衝突点の情報も送っていて、中心に衝突することの検知はそっちでやっています。委譲って感じ。
あとこの子の挙動はposition
とrotation
で確定するので何も考えなくても同期します。ありがた。
電子楽器3
これもらくとさんの持っていた電子楽器構想にあったもので、特に私としては水を掬う行為の大きな目的になるのでめちゃくちゃ欲しいなとは思っていました。「水は掬えるべき」(掬えそうなので) なんですが、その願いの先に成れるんです。機能の存在価値を示すことが出来る。すごいギリギリだったけど作って本当によかったと思う。
まぁ水を入れると音が鳴るとはどこにも書いてないんですけど、水滴当てても音鳴らないし、色々試していったら見つかる範疇かなとは思います。思っています。
この子の見た目はすごい素直で、音の高さそのものですね。現在の水の量に対応する位置に光るラインが来ます。それだけです。
いくつかある固定ラインはまぁ意味があって、高い方から順にD,C,A,Gです。そして最高音と最低音はF。そうするとF,D,AでD minor、F,C,AでF majorです。誰でも調和させられます。うれしい。
だから3つあるのか~みたいなことを私は考えていたんですが、なんかこう、本領はそんなもんじゃなかったらしい。
From Amebientの練習風景/電子楽器3(開発コード)両手でグルグルやるのめちゃくちゃに楽しいんですよね... pic.twitter.com/PQJiSgwYqz
— らくとあいす (@rakuraku_vtube) July 20, 2020
これ聴いたときは本当にびびった。これで演奏するという発想が無かったので…。作ってよかったと心から思いました。
さて、実装は… まずPercussion
ではあるんですけど無音のAudioClipを鳴らしています。そしてSymbolControl
があるのも同じ、mode
が3になるだけです。実際に音を鳴らしているのはSymbolControl
そのもので、AudioSourceをくっつけてあってvolume
とpitch
を弄っています。水面の高さはSurface
っていう別のUdonが持っているので、そこから情報を貰って音及びマテリアルパラメータに適用、という感じ。
同期はSurface
がやってるのでこっちでは特に考えることはありませんでした。そんなとこかな。
あと水が入ってないときは (音量が0になった後で) ちゃんとStop
するようにもなってる。
そういえば海に入るとフェードアウトするようにもしてますね、海にはあのambientだけがあるようにしているのです。そういう願い。
まとめ
基礎じゃない方の仕組みの説明をしました。全体的にadhocな実装になっているんですが、これはわざとです。
使える情報をできる限り使う方が、良いものができる。 ノーフリーランチ定理の私の解釈です。
特にVRChat及びUdonというとても小さい環境なので、できることからやっていかないと。
あと情報量というものは「新しく在る構造」から生まれるもので、違う要素を色々作る必要が出てくるのです。例えば電子楽器1には色味が違う子が居ますけど、それの持つ情報量は「電子楽器1」として吸収されてしまい、そう多くは感じないと思います (音的には重要性ありますけどね、それは違う周波数帯という点で大きく情報を持っているから)。
システム的には汎用性はめちゃくちゃ欲しいものですが、創作的にはそれは願いと相反するのです。いえ、最も望ましいことはその願いの複雑性を内包できるほどの汎用性に辿り着くことではあります。それは世界の統一理論です。でもそこまで辿り着くことは本当に難しい。
Amebientは「たくさんの食器」や「雨粒の落下」に対する汎用性は持っていますが、それをベースとした上で汎用性から外れた領域が存在するということが、事実として世界の強度を上げているとは思っています。一応それぞれ解釈はありますし、ね。(まぁ、Amebientという世界にはそれ以上の外枠があるわけですけど…)
できる限りの汎用性を確保し展開させた上で、それを越えた領域の展望を見せる。それがいいんだと思います。
電子楽器3が有り得るなら結構いろんなことがありそうだと思えるじゃないですか。ね。
はい。以上です。音関連の説明はこれくらいだと思うので、以降の私の記事はVisualとギミック系の話になります。ではでは。