Imaginantia

思ったことを書きます

記録 210714

特に何もしていませんので、特に何もしてないことについて書きます。

創作意欲

最近うるさくて申し訳ない気持ちでいっぱいです。でも吐かないと頭の中から出ていかなくてね…。

見てるとわかると思うんですが言ってることはいつも同じことで、ずっとぐるぐる回っています。

最高の何かを作りたい、けどそれには大きなコンセプトが必要、その為にはコンセプトを「選ぶ」必要がある… でも選ぶことは最強であることに反する。だからもっと良い概念を見つけなきゃいけない。そんなことをやってたら何も作れないので簡単にワールドを作りたい、けど所謂景色みたいなのは今の私には作れない気がするからギミック的なもの、そうするとパズルゲームとか謎解きとかやってみたいけどそれもまたコンセプトが無ければ始まらない。いくつかコンセプトの種はあってもそれをちゃんと成長させるには本当に集中して考える時間が必要… それ無しで出来るとしたら小さなお遊びみたいなものだけど、それはそれでちゃんと世界として成立させたいわけで、じゃあギミックもコンセプトも無しで基幹構造だけで成り立たせたい、でも私は手を動かすのが得意ではないのである程度自動的に生える方法がいい、でもまたそれも結局手順が必要で…

まぁだから大きな時間が無いことを言い訳にして延々と諦め続けているのです。

そしてこの感覚をずっと引きずっているせいで本来時間を費やすべきもの (現実じゃないよ) に費やせていないのです。それが最大の問題です。

もっと気軽に色々できるようになりたいし、同時に複数進行できるようになりたかったな。

 

…っていうのはまぁ話としてあるんですけど。

一番困るのは「これを堂々巡りしていること」なんですよね。反省が無い。

どうしても頭の片隅に残っていて、それを解消できないから考えるしかない。そして考えても良い方法は見つからない。だからなおさら残り続ける。

でもタイミングによっては一切そんなこと考えずにするっと作業しようとなれるときもあって。

この振動というか、不安定性というか、それの制御が全然できてないことが目下の問題なのです。

ぱきっとやることを分けられないんですね。はー。

まぁその解決になったらいいなと思ってこうやって文章を書いていたりするんですけど。つまり不安定な私を固定化することでそれを冷静に観察できるようになると。

そうしたら自分のするべきことをちゃんとわかるようになるんじゃないかなと。

 

やっぱり日記は続けるべきだったのかもしれんな。

覚悟

そんなことを考えつつ、こう、なんかよくわからないことをしています。

というのは。

新しいPCを買いました。…微妙に時期がなんとやらかもしれませんが、まぁ悪くはなかったと思います。というか私はそういうのを気にする派ではない。

延々と買わなかったのは惰性ですね。何よりWindows 8.1です。いろんなところで困っています。

これで気持ち晴れやかになったらいいんですけどね。ならんかまぁ。いやでもVRが快適になるような気はするので多少変化はあるかもな。

今使ってるVR用のノートPCは変わらず残して置くと思うので開発時の負荷計測もできます。悪くないわ。

 

そしてもう一つの覚悟が。Liveです。

最近一人でぽちぽちLive Liteで遊んでたんですが、長い時間掛けてやっとわかってきた気がします。音楽というものが。

いやまだわかりませんけれども。多少マシになってきた気がします。

その点において自作あぷりけーしょんでなんとやらとかしている場合ではないということがわかったのです。

やれることをやったほうがいいのです。それはそう。

何よりも元々自分に課していたはずの制約は結構解かれてきていて (アレとは関係あるのだろうか…)。全て自作しなければという強迫観念はほぼ消失したに近い。

まぁ自作する能力を手に入れたから、初めてその先へ進めるようになったというところなのかもしれない。

音は作れたけど音楽にはまだなってないわけ。そしてその過程には「順序」の概念が必要です。それはどう考えてもLiveのほうがよくできているのでした。

…というか、なんというか、Liveの哲学は私に合うんじゃないかなって思った。そうだといいなと願って、そう進むことにしました。きっと大丈夫。

 

ついでにPushを買ったのは、まぁ、お察しの通りというやつです。いや察せられるんだろうか。

「演奏」をしたかったので。今度こそ。

何より一度魂を売ってみようと考えたわけなので、それならいっそ、という思考でもあります。私は割とそういうのが多いかもしれないですね。外れると一気にぼろぼろ外れるのね。

Senselちゃんはアレはあれで使い所があると思うので何か~とか思ってますけどはて。まぁそのうち考えましょう。

変わらないようで、変わっていると思います。そうであると願っています。

 

あともう一つ今日なんとなく言ったのはHoudiniを買うか、というやつだったんですが、まぁApprenticeでまだ困らないかなと思って一旦はやめました。

次にワールドを作るときには多分Indieが必要になってると思います。そのときにまた覚悟決めましょうかね。

いつになるかはわかりませんけれども。

 

この一連の流れの起点は実はあのときの定規、なんですよね。気軽にものを買うという体験を久々にやったので、他の購入行動に対するハードルが下がってしまった感じがあります。

ただまぁずっと買わずにうーーってやってたわけで。これでよかったんだと思います。というか私はそういうタイプ、みたいですね。

来週には色々届いてそうな気がするので楽しみにしたいと思います。そういえばぶらさがり健康器も買いました。健康になります。

好きな服

なんとなく。服と顔の話を書きます。

にも書きましたが、最近はすっかり服選びがだいぶ自由になってきました。

まぁ増えたのはメイドちゃんのルと水着、あと今の森っぽいのですが。いや十分ね。

布面積が多ければ多いほどかわいいという観念で動いてる私ですが、意外とあの水着はアリだったというのは自分でも驚きでした。

まぁ比較的多いっていうのはそうなんですけど。普通に割とかわいいというか。ちゃんと気に入っててたまに一人で着てたりしました。

あとまぁ、何よりも大人っぽくないっていうのは大きいかな。後述しますけど、こう、所謂大人的な…言ってしまえば「堂々と肌を出せるタイプの人間が着るもの」ではないっていうのが、私の中の許容範囲なのかもしれない。

それと森っぽいの。察せられる感とても高いんですが、所謂森っぽい服はめちゃくちゃ私のド好みです。現実でも。持ってないけど。正直欲しい。

あと民族衣装っぽいのもそれに近いものを感じて好き。未だにこれ着せてないんすよね… めちゃすき。

で。

好きは好きなんだけどやっぱりかわいい系は着るにはなんか違うっていう気持ちはあって、まぁそうするとこれに落ち着くわけです。すごいわかりやすいですね。

ちなみにルは「願い」そのものなので次元が違う。

この「かわいい」わけじゃないけど特に意識されない自然さがある、っていうのはとても着やすいわけで。シンプルに好き。現実もこれになりたい。まぁ半分くらい近いと思うけど。

それで長い間落ち着いてたわけですけど。

Nellちゃんがあって。部屋着があって。ミリタリードレスがあって。メイド服があって。わりとなんでもよくなってしまって。

そうすると確かに森っぽいのはありなんです。それはそう。というわけで今これになっています。めちゃ気に入っちゃったので当分これかもしれん。

主張が激しくないのは何にせよ好きですね。私は空間に自然に存在しているのが好きです。

 

で。本題。

今までの服、どれもやっぱり「大人っぽくない」んですよね。そして幽狐ちゃんも子供っぽい顔つきです。

結論から言うと「大人っぽい」の、好きじゃないんだと思います。私。

そしてロリっぽいのはめちゃくちゃ好きなんだけど自分じゃない。だからこうなるんですね。これは何度か言ってますけど。

これは現実でもそうな話ですが、口紅みたいなエグいディティールめちゃくちゃ苦手で。あとぐわぐわしたぐるぐる髪の毛もあまり好きではなくて。いやすいません誰かを否定するわけではなく自分の嗜好の話であって、だから私の前に出るなとかいう話では一切ないわけですけど。

あと振る舞いがおとなっぽいひとはやっぱり常に苦手です。いろいろな側面がありますけども。

その点では実はあのメリノちゃんのあの水着が苦手です。だからと言って何というわけではないんですけども。アレ、なんか「そういう雰囲気」を感じる。

あとリメリバも微妙に苦手だったんすよね。そういう判定基準らしい。私。あ、だからと言って略

まぁ何にせよぶいちゃだと「大人っぽくないが、子供ほどではない」感じのアバターが多くてすごい棲みやすいんだなぁと思いました。私にとってね。

…それはそれとして。

ちっちゃいこめっちゃかわいい。具体的に言うとwa-iさんのこの写真みて画面前で崩れそうになってた。実は。

前ちゃのあの娘もかわいかったからな…。はい。かわいいね…。

そうじゃなくて。えーと。

あとりふかわいいよね。というかエリンニヤちゃん、等身とかは普通な気もするんだけどめちゃくちゃロリを感じる。顔か?りふが小さいからか?

まぁめちゃくちゃ純真さを感じるのでその辺かとも思うんですけども。まぶしい。

確かにそれほどちっちゃいこ、近くにはあまり居ないのでこう。こうな。願う。

あと当然「わかっているロリ」は違うんですよ。わかりますよね。寝る前ちゃんは寝る前ちゃんがかわいいのでOK。

…自分がなればいいとは違うのがこの辺の難しいとこよね。

 

あとそういえばパンク系の服も好きなんだけど自分じゃ着られないのよね。まぁこれも「見たい」タイプの好きで。

その点写真撮るのはすごい楽しい。めちゃダメージを食らうけど。かわいいので。

この辺もやっぱり感覚としては「大人っぽくない」に近くて、そういう「わかってますよ感」の無いのが好き。自分が唯好きなのだと見える格好が好き。

「見せるため」の格好が好きじゃないんだな私。それかも。

だからこそ「誰かに着せる」のもまた違うんだろうな…うごご。難しいね。

まぁなんであれ、自分の好きであれるこの世界がやっぱり一番いいですね。


 

まとまりはないんですが、まぁそれがこの記録だとおもいます。はい。

メモ 210711

鏡4枚はやばそうと思ったので常時1枚にする機構を作りました。

通常のぶいちゃの鏡であればInteractによるオンオフは一般的かと思いますが、今回に関しては鏡は「ワールドそのもの」といえる (姿見があって初めて試着 (=店) が機能する) ので「溶けたUI」のほうが自然です。

というわけで、最大1枚にしつつ選択を意識しないための構造を考えることになりました。

 

単純に考えれば一番近いものを採用するのが良さそうですが、鏡は遠方から見ても意味あるギミックです。

よって視線方向に応じて採用するのも妥当と言えます。

しかし鏡にめっちゃ近いところで横を向いたとしても、それはきっと一瞬横を向いただけで基本的に真正面の鏡を見たいのではないかと思いました。

というわけで「ある範囲内に鏡があれば、それを採用する」「そうでない場合、向いてる方向にある鏡を採用する」ということにしました。

Vector3 playerPos = loPlayer.GetPosition();
Quaternion invPlayerRot = Quaternion.Inverse(loPlayer.GetRotation());
int nearest = -1;
float dist = 4.0f;
int aiming = -1;
float aim = 0.85f;
for(int i=0;i<mirrors.Length;i++) {
    Vector3 p = invPlayerRot * (origins[i] - playerPos);
    p.Scale(new Vector3(1,0,1));
    float l = p.magnitude;
    if(i == currentMirror) l -= 0.5f;
    if(dist > l) {
        nearest = i;
        dist = l;
    }
    float z = p.normalized.z;
    if(i == currentMirror) z += 0.05f;
    if(aim < z) {
        aiming = i;
        aim = z;
    }
}
nextMirror = nearest != -1 ? nearest : aiming;

プレイヤーからのローカル座標に変換して、距離が (4m以下で) 一番近い鏡が nearest に、向いている方向に一番近い鏡が aiming に入ります。

ついでに現在アクティブな鏡は判定を緩める処理が入っていて、これは境界でばちばちしないための対策です。まぁ心理的にも合致してそうだった (現状が維持されるのは気持ちとして自然)。

Y座標を潰しているのはあの空間だと高さが意味を持たないからです。

 

あとはこれを反映するだけです。まぁやるだけ。

今回のこれは「鏡を選択する処理」と「選択を実際に反映する処理」で綺麗にぱきっと分けられて良いですね。

float dt = Time.deltaTime * 4.0f;
if(currentMirror >= 0) {
    if(currentMirror == nextMirror) {
        if (alphaValue < 1) {
            alphaValue += dt;
            if (alphaValue > 1) alphaValue = 1;
            materials[currentMirror].SetFloat("_Alpha", alphaValue);
        }
    } else {
        alphaValue -= dt;
        if(alphaValue < 0) {
            alphaValue = 0;
            materials[currentMirror].SetFloat("_Alpha", 0);
            mrs[currentMirror].enabled = false;
            currentMirror = nextMirror;
            if(currentMirror != -1) mrs[currentMirror].enabled = true;
        } else {
            materials[currentMirror].SetFloat("_Alpha", alphaValue);
        }
    }
} else {
    if(nextMirror >= 0) {
        alphaValue = 0;
        currentMirror = nextMirror;
        mrs[currentMirror].enabled = true;
    }
}

鏡のMaterialを弄ってalpha入れられるようにしてあります。(参考にしました)

素直なstate machineで嬉しいですね。うむ。

 

おわり。まぁこれはふとほしいよねってなってぱらっとつくったやつなので大したあれでもなし。

メイン作業なさってたみなさん本当にお疲れ様でした :pray:

雑記 210702

特に理由はないです。

比喩

何かを表すのに何か別のものを使うことをよく人類は行っていますが、当然ながら比喩がうまくいくには条件が必要です。

Aのとき、B
↓       ↑
Cのとき、D

「Aのとき、B」が元々言いたいことだとして。「Cのとき、D」であることを根拠とするならば。

AとCは相互交換可能でなきゃいけないし、BとDも同様に交換可能である必要があります。

そして加えて、この四角形そのものが本質的に構造を持たないことを示す必要があります。これは可換性に関わるよくある条件で、「真ん中に穴が空いていない」ことを要求するわけですね。

要は字面上うまくいっているように見えても本質的にうまくいかないケースがあるんじゃないのって話です。可能性の話ね。

 

で、まぁ何よりも重要なのは、その比喩がうまくいくことを説明するのは喋る側だろうというところで。

一方的に喋る人類は総じてまぁ無意味な音声を吐くものでありますね。

いい加減にしろ。

butterfly-effect

昔からバタフライエフェクトという概念は人類大好きに見えますが、それをなんだかどこか空想上の話だと思っているような気がしてなりません。

私達はキーボードを1mm押下するだけで海外のサーバのDBにデータを書き込めるのです。

制限を受けるのはただ「光速」のみ。要は「0.04秒後に地球の裏側で起きることは私達には絶対に変えられない」くらい、です。

逆に言えばその範疇であれば状況は変わりうるわけです。

つまり。「過去に戻ってもう一度」系物語は、だいたいえぐいことになるはずなのです。

Vivyは変えることを前提としていた物語なのでまぁええか、という気持ちだったんですけど。特に「影響の大きさ」の話もあるから。

この前見たとある映画がね。「過去のこのときの行動はこうに決まっている」系だったので。うーんってなりました。

人の行動はだいたい些細なことでどんどん変わっていくもので。ねぇ。

そういうところで「影響」について語らない、っていうのが、「強い概念を使う責任を取っていない」という状況、と思っています。

強い概念をいいとこだけ取るっていうことはできないものなのです。何故なら強いから。

ちゃんと責任を取ること、すなわち「畳み切ること」っていうのはとても大事なことだなと。とてもおもいました。

 

おわり。

望まれない考察

物語の話です。

最近なんだか話題になってたVivyを見ました。よかったです。

で、自分にとっての解釈と結論が出た状態で他人の感想を見に行くのが好きなんですが、ちょこちょこ興味深い気持ちになりました。

「設定の整合性」の話です。

以下、可能な限り「情報」は書かずに書いています。

 

全てのSFは「破綻すべく生まれるもの」だと思います。何故なら破綻しなければ新しい世界が見えないからです。

ここで「破綻」というのは「前提を追加すること」を指します。

前提を追加することで起こりうる矛盾を、展開の強さによって覆い隠す、というのが物語の基本構造、だと思っています。

要は「こんなことあったらおもしろいよね」って言う話です。いえ、正確には「おもしろいことになるよね」ですね。

基本的には何かを追加すると何かが壊れるものです。だから、その「何を許し、何を許さなかったか」という世界の境界が、その物語の意思であり、提示する対象でもあると思うのです。

 

さて、Vivyは初めから終わっています。

過去に戻るしAIは自律してるしUIは意味不明、もはやギャグとして有名な 𝑆𝑖𝑛𝑔𝑢𝑙𝑎𝑟𝑖𝑡𝑦 という言葉を山程呟くのは笑う他ありません。

つまりテーマはそこではありません

何よりもちゃんと説明されているのが「使命」という概念です。そしてそれがこの話の一貫した軸に見えます。

単純な話です。この概念についての解釈を構成するためにたくさんの前提を詰め込んだ、と言えるわけです。

概念の発展型を提示する為には過去に戻ろうが歌を歌おうが異常な技術が発達しようが問題はありません。

あとは「気にするべき矛盾」だけを避けて話を展開すればいいのです。

「心」については起承転結のファクターでしかない気がしています。あと無数にある既存作品への問いかけ。

 

技術的な話を色々と知っていると「どう考えても無理」であることを判定できるようになることがあります。

例えばハッキング、完全なAR、大容量通信、綺麗すぎるUI、プログラムの暴走、ちゃんとした知性…。

特に「実現不可能だろう」というよりも、「そう演出したのは自然に起きるからではなく作者がやりたかっただけだろう」という判断ですね。

だからこの辺の判定を迷うことはほぼ無いんじゃないでしょうか。まぁ完璧に実現可能な作品が存在した覚えは一切無いですけど。現実以外で。

要は「いろいろ知ってると気になってくる」現象には先があるという話です。

もうちょっと楽しむ為に眼を変えるべきなのです。

 

ということで技術系の話については概ね無視できるようになった気がする私なんですが、Vivyの10話ラストで敗北しました。

その破綻を許していいのかって思っちゃった。私の負けです。

アレは、本来ならば最も得意と言えるはずの「情報の複製」という能力を否定しているのです。

…こう書くと技術的に見えますが、まぁ観た人はわかるように、言葉にすれば単純なことです。

そしてその単純さが故に効力はめちゃくちゃ大きいその代償も

人間が「複製の失敗」に気づいていない、っていうのは私にとって大きな違和感でありました。

ただ、まぁその違和感によって別の物を際立てる、っていう意図とその実現は、とても納得できるものでした。

なのでまぁこれは私がその違和感を忘れれば良い話ではあるんです。そういう構造なのです。

これを含めて物語として…すなわち「複製の失敗に気づいていない」ということがあのテーマの一部 (不理解性) として解釈され、その上でVivyが「正解を提示する」っていう意味 (=唯一性、語られた可能性への反論) なんだと捉えることが出来ないわけでもないけど…
人間が気づいていないのではなく、同一性の解釈範囲が広いっていう解釈もできるし事実それは歴史上実在しているわけですが…
そこまで考えてない気がする。

 

長くなってしまいました。

まとめると、「破綻は基礎として、その上に描かれたものを純粋に受け取るべき」「でも中途半端な知識だとなかなかそれが出来なくて大変ね」という話でした。

おわり。

 


 

追記

phi16.hatenablog.com

違うことを書いているようでまぁ同じ話ですね。ちゃんと文章にするとわかりやすいね。

わからないということ

「何も知らない人」に、「1+1は?」と訊いたら、「わからない」と思います。それが正しい。

それで、ちょっと世の中のなんとやらというのを知ると、「2」と答えられるようになります。正しい。

そして、もっといろいろなことを知ると、「1」とか「10」とか、答えられるようになります。

最終的には「何を答えても良い」ということがわかります。それでいいんです。

ただ、それは、逆説的に「何を答えるべきか判断できない」すなわち「わからない」という状況になります。

だけど、これは最初の状況とは違います。これは「何がわからないのかちゃんとわかっている、『わからない』」なのです。

これがきっと唯一の差なのです。

 

こうなったときに、なんとやらを頼りとして「2と答えるべきだろう」と考える人がいます。私はその態度が好きではありません。

その選択が本質的な問いでない以上、ある選択を要求するならばその対価を払うべきなのです。

すなわち、「私は何かを頼っています」と語るべきなのです。自分の生きてきた文脈を。

その欠如が、同時に誰かを不幸にするのです。

 

選択を無に帰して、それでも尚意思を以て選ぶということが、唯一信頼できるものだと思います。

うごくものをうごかしたい

なんとなく書きたくなったので書きます。10割が算数の話だしまぁ「認識されないディティール」の話なので意味はないです。自分用のメモみたいなもん。

ひも

f:id:phi16_ind:20210612162407p:plain

CRTの後ろのこの子は、Capが元々垂れてたケーブルにしていたものの、CRTを動かすと動いてしまうのでやばいよね、ということで動的にいい感じにする必要がありました。

f:id:phi16_ind:20210612162312p:plain

そこで元のメッシュはsubdiv付き線分にしてもらって、いい感じにするということでいい感じにしました。

 

まず紐の垂れる形状は懸垂線というやつで、y=\cosh(x) の一部になることが知られています。

ではどの一部なのかが問題です。

今回の制約条件は「端点の位置」「紐の長さ」です。紐が平面にあると仮定すると、紐の形状を決定するのは「(一様) 拡大」「平行移動」の3つのパラメータ。

端点の位置を (0,0)(1,v) として正規化して、紐の形状を f(x)=\beta+\gamma\cosh((x-\alpha)/\gamma) で表すことにします。正規化された状態での (固定の) 紐の長さを L とします。

与えられた制約から:

  • 始点 f(0)=0
  • 終点 f(1)=v
  • 弧長 \gamma\sinh((1-\alpha)/\gamma) - \gamma\sinh((0-\alpha)/\gamma) = L

なので、解きます。解けません。

諦めて二分法にします。\gamma を固定した際の弧長を計算してぱたぱた動かしていきます。

\gamma から \alpha が計算できそうなことがわかったのでそこから倒します。がちゃがちゃ叩いたら次のようになりました。

  • A=\alpha/\gamma, B = 1/\gamma, C = v/\gamma とすると
  • \cosh(B-A) - \cosh(0-A) = C より
  • e^A = (e^B\sqrt{e^{-3B}(e^B(C^2-2) + e^{2B} + 1)}-C)\times e^B/(e^B-1)

よって \gamma を固定すると \alpha が出るのでそこから弧長 \gamma(\sinh(B-A) - \sinh(0-A) ) が計算できます。

後はぺちぺちやるだけです。ただこの時点で \gamma よりも 1/\gamma の方が単位がまともそうなことに気づいたので、1/\gamma に対して二分法をやります。

float ic = 3, dc = ic/2;
float c = 1/ic, ac = 0;
for(int i=0;i<12;i++) {
    float E = exp(1/c);
    float C = v/c;
    float eA = (E*sqrt(exp(-3/c)*(E*(C*C-2) + E*E + 1)) - C) * E/(E-1);
    ac = log(eA);
    float l = c*(sinh(1/c-ac) - sinh(0/c-ac));
    if(l < L) ic += dc;
    else ic -= dc;
    c = 1/ic;
    dc /= 2.0;
}

初期値は実験的に決めて 1/\gamma = 3 になりました。これで c つまり \gamma がわかったので、後は素直に答えが出ます。

 

さて曲線の形状がわかったので実際に曲げます。単なる平行移動では角度が変わらないので太さ一定になりません。よってちゃんと接空間を取って回転させる必要があります。

今回の曲線は (正規化された状態で) (t, \beta+\gamma\cosh((t-\alpha)/\gamma) ) と表されます。

よって接ベクトルは (1, \sinh((t-\alpha)/\gamma) ) (の方向) です。あとbinormalはY軸と直行するように設定しました。後はやるだけです。

全体としてこんな感じになってます。

float dist = distance(crtPoint.xz, fixPoint.xz);
float scale = (crtPoint.y - fixPoint.y) / dist;
float3 su = computeCurve(scale, 1.2/dist); // これがさっきのめんどくさいやつ

float3 target = lerp(fixPoint, crtPoint, float3(t, (su.y+su.z*cosh((t-su.x)/su.z))/scale, t));
// calculate tangent
float3 d = (crtPoint - fixPoint) * float3(1, sinh((t-su.x)/su.z)/scale, 1);
d = normalize(d);
float3 T = normalize(d);
float3 up = float3(0,1,0);
float3 B = normalize(cross(up,T));
float3 N = cross(T,B);
perp = mul(transpose(float3x3(B,N,T)), perp);

v.vertex.xyz = target + perp;

ちなみに元々ついてる斜めのケーブルを真っ直ぐに (tangent = +Z) にするために最初適当な逆回転を入れています。そしてケーブルの中心軸が target、そこからのズレを perp として、回転するときは perp だけを回す感じですね。

そういえば弧長パラメータは取ってないです。めんどくさくて。多分わからんし。

 

あとCRTが動いたときの揺れはUdonから渡ってきた適当な値を元にして target 側に足し合わせてるだけです (重みは 1-(2t-1)^2 らしい)。

正しくはないけどさすがにわからないと思うのでOKだと思います。

前後移動

あとは大した話ではないのだけど。

f:id:phi16_ind:20210612171613p:plain

この曲線は「\tanh の一部分を切り出して貼っつけて繰り返して \cos に突っ込んだもの」です。ええと。

結論から言うとこれなんだけど。

f:id:phi16_ind:20210612203439p:plain

まず \tanh を適当に伸ばしたりずらしたものがあって。

f:id:phi16_ind:20210612203758p:plain

ひっくり返して貼っ付けます。で、そうすると端点の傾きも一致するのでループできます。

f:id:phi16_ind:20210612203923p:plain

出力のY座標が「整数付近が広い」のと、「半整数付近も広い」のが重要。

あとは x\mapsto\cos(2\pi x) に突っ込むとこう。

f:id:phi16_ind:20210612204117p:plain

つまり整数らへんは1に、半整数らへんは-1になるわけです。

この曲線のパラメータは「傾きのきつさ (f1, 0より大きい値) 」「デューティ比っぽいの (f2, 0から1の間)」で制御できてて、こう、いい感じというわけです。

そういえばコントローラの画面にグラフを映すために コレ をやっています。これをやるために微分もついでに計算する必要がありました。

float2 crtF(float2 p, float x) {
    float s = p.x;
    float b = p.y;
    float v0 = tanh((0-b)*s), v1 = tanh((1-b)*s);
    float u = frac(x/2)*2-1;
    float ug = 1;
    float v = sign(u) * (tanh((abs(u)-b)*s)-v0)/(v1-v0);
    float vg = ug * s * pow(1 / cosh((abs(u)-b)*s), 2) / (v1-v0);
    float w = floor(x) + v*0.5+0.5;
    float wg = vg*0.5;
    float2 o = float2(cos(UNITY_PI*2*w), -UNITY_PI*2*wg*sin(UNITY_PI*2*w));
    o.x += tanh(o.y) * p.x / 15.0f * 0.1; // grad bump
    return o;
}

そういえば微分の出力を直接前後移動に足し込むことで「制御が行き過ぎた感」をちょっと入れたりしました。tanitta先生によるあどばいすからです。感謝。

 

さて、それはそれとしてあの5x5の「動き」ですが。アレはこの曲線に位相オフセットを入れているだけです。

発想のきっかけは \mathbb{Z}/16\mathbb{Z} とその上のフーリエ変換で、コントローラは周波数を指定しています。\mathbb{Z}/16\mathbb{Z} の元で (自己双対なので)。

なのでコントローラでは16通りがくるくる選択できるようになっていて、一周するともとに戻ります (横1のズレが綺麗に波を一周するので)。そう。

ついでに中心からの距離の二乗 (x^2+y^2) も使えます。これが中心からふわって動くようにみえるやつですね。

アーム

あとついでに。

f:id:phi16_ind:20210612211344p:plain

5自由度のIKです。ふつうにUdonによるボーン制御です。

f:id:phi16_ind:20210612211653p:plain

下から順に、XZの位置を弄る2つ、Pitchを保ったまま上下する為の1つ、Yaw角1つ、Pitch角1つです。

汎用的なIKの解き方はよくわからなかった (無知) ので適当にいい感じな方法を考えました。

 

まずpickupの角度をあわせます。YawとPitchは独立にできるのでするっといきます。

次に位置をあわせますが、Y座標をいじれるのも1自由度分しかないのでこれもするっといきます。

後はXZ座標ですが、ここまでくると平面のよくあるケースに落ちるので。ぐっ (先端に近い方を目的地に近づけて、その次にもう一つの関節も近づける) と。

で、これまでの工程を1Fに2回反復させて (何故ならそうしなかったらめちゃくちゃ振動したので) だんだん近づける、というやり方をしました。

まともに動いてるのでいいんじゃないでしょうか。

まぁちょっと今可動域が少し狭くて困ってたりはするんですけどね…。治すのも面倒なのよね。

おわり

はい。

Udonデバッグ記 / HalfLine

折角なので残してみます。というか後で私が参照する為に。

うちのワールド HalfLine では度々「キューブがでなくなる」バグが発生していました。幾つか発見し潰したものの今もまだ残っているようでした。

幸いにもUdonがクラッシュするタイプのバグだったのでログから色々情報が取れました。

f:id:phi16_ind:20210611191130p:plain

もうこれ箇条書きでいい?

  • Heap Dump0x00000001 に書いてあるのがクラッシュした Udon がついてる GameObject の名前 (string)。
  • Program Counter was at: 18536 っていうのは 18536 = 0x00004868 番地でクラッシュしたっていうこと。
  • 該当する Udon Program Asset の Program Disassembly → Disassembled Program を眺める。

f:id:phi16_ind:20210611191205p:plain

  • 確かに EXTERN to 'UnityEngineVector4Array.__Get__SystemInt32__UnityEngineVector4' と名前が一致している。
  • この辺のアセンブリを元にして Compiled C# Udon Assembly → Assembly Code から似たような記述を探す。
    • 縦の長さが違うだけでほぼ同じ場所にあるはず (全体行数に対する行番号の割合がほぼ同一という話)。

f:id:phi16_ind:20210611191620p:plain

  • #コメントアウトされている行が「元々U#で書かれていたコード」なので、それをオリジナルコードから探す (関数名がわかるのですぐわかるはず)。

f:id:phi16_ind:20210611191817p:plain

  • 引数を知りたい。Parameter Address: 0x00000012, 0x000000CA, 0x00000197 からそれぞれの値を探しに行く。
    • これは (EXTERN名から) 順に「呼び出し対象」「第一引数」「戻り値の格納先」であることがわかる (んだと思うけど仕様どこにあったっけね)。
    • Udonには即値が無いのでこれは全部Heapへの参照。
  • 0x00000012: UnityEngine.Vector4[] これは上の方にあるのでメンバ変数っぽい
    • [UdonSynced] Vector4[] data っぽい (確かにコードと一致)
  • 0x000000CA: 238 第一引数は 238 (int) っぽい
  • 0x00000197: (0.0, 0.0, 0.0, 0.0) まぁなんか適当な格納先っぽい

ということで今回は「配列 data に添字 238 での要素アクセスをした結果範囲外アクセスを起こした」ということがわかりました。おわり。

 

おわりです。後は知らん。多分うちのワールドだと配列 data はたまに短くなったりするから範囲外は弾かなきゃいけないんだと思う。しらんけど。

まぁちょっと調べて直します。

では。