道先が変わるパズルレールを作ろう

# はじめに
猫なでたいです。


さて、今回のアドカレではこんなの作ります。
<iframe width="560" height="315" src="https://www.youtube.com/embed/I0f0WIGK7c8?si=3cUmRDNtRpVAiL_o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

最近、江ノ島観光に何回か行きまして、その時に江ノ電のパズルレールという物を見たんですね。
もう作る物で出してますが、道筋が彫ってあるパズルをくみ上げて、その上を車両が通るというオモチャです。航空会社とか鉄道会社がよく出してます。

「あー、こんなのあったな...これ、行き先が常に変わり続けたら面白いんじゃないか？」

と思ったので、作りました。
最初は全部Solverの中で行う非常に難解な上に分かりにくい物だったのでアドカレにするに辺り色々整理しましたが、最近ちょっとディニ力が衰えて割とゴリ押しな所があります。ご愛嬌。

# 作る
さて、作っていきましょう。
まず大まかな流れですが

パズル本体を作り、組み変わるようにする
↓
変わるタイミングで行先変更出来る仕組みにする
↓
移動したパネルの上を、実際に移動するポイントを作成する
↓
仕上げ

になります。あら案外簡単。
では順に解説していきます。大雑把な解説になるので、わからない箇所は添付のシーン見てください。

## パズル本体を作り、組み変わるようにする
### パズル作り
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/80527714-8270-4a95-6b10-bd1a84e21218.png)
パズルって言っても、まずは簡単に行き先が決まっている物にします。
今回は、Lineノードを使って3種類のパズルにしました。
このlineノードは、後々移動先決定の時に番号を使用するので割らないでください。仕上げの時に出来ます。

###組み変わる仕組み作り
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/40105634-f637-3529-38f6-efdd04e37d2a.png)
パズルを設置するコピー元となるポイントを作成する。Gridで作成してもいいですが、自分は幅とかちょっとズレるの嫌だし書いた方が楽だったので書きました。

次に、組み変わるために必要なアトリビュートを作成します。
ここで必要なのは

i@seed : 全体のシード値を決める
i@class : 回転を決める
i@id : 番号
v@angle : 方向

```
float seed=@ptnum*5;
@seed=seed;

i@class=rand(seed)*4;

i@id=rand(seed*5)*3;
v@angle=set(1,0,0);
```
次に、方向を決めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/f0f06706-38cd-ddf9-f826-53129f8e8ddb.png)
ここでは回転方向を求めていますが回転の考え方は、適当な値から+1か-1を足す事を、変更タイミングで行っています。
ここで作成した値に90を掛けてラジアン角にしてやれば、qrotateで連続して回せる。
そして、もちろん突然変わる訳にはいかないので過去の値から新規の値までをlerpで繋いでいます。
ついでに、回転でパズルを変更するのでさすがに干渉が気になると思うので、ここで回転して変更されるパズル(過去の値と新規の値に差がある)は高さを変えて干渉しないようにします。

パズルを実際に回転させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/7cfc82d8-1c15-1317-ff92-807eef820dfd.png)
qrotateを使ってNを回転させ、求めている方向へ向くように調整しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/b7e97457-8364-3647-586c-d489c4800d46.png)
今までのにID付けてcopyするとこうなります。
キチンと回転が出てればOK。

## 変わるタイミングで行先変更出来る仕組みにする
こっからはSolverでやっていきます。
最初は配列でやっていたのですが、管理がほぼ全部VEXになった上に試行錯誤してた時の記憶が無く物凄い難解なコードになってたので作り直しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/0e7b59f2-3acf-15fd-6cad-4e1604ec8f6b.png)
まず、SOPで最初にアクティブにするプリミティブにactiveのグループを与えます。
今は0.02、2%のプリミティブが対象となっています。
また、先ほど作った回転の挙動はSolverの0入力に繋ぐと内部で参照できない為、1入力に繋いでおきます。

Solver内
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/c915ba99-ca9c-80f1-2e44-937fb59fe386.png)
Solver内部はこのようになっています。
処理の順番としては

アクティブなプリミティブにアニメーションを付与する
非アクティブプリミティブが自分自身がアクティブになるかを判定する
アクティブになるなら、そのポイントが入ってくる方向を割り出す
これらを切り替わりのタイミングで実行する

です。

アクティブなプリミティブにアニメーションを付与する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/dc51532c-1454-743b-079c-3cfd378d2d8a.png)
splitでactiveを分けた後、前回参照した入ってくる方向に応じて2つに分けています。
carveの値は

```
$T-int($T)+(1/$FPS)
または
1-($T-int($T)+(1/$FPS))
```

で書いています。
1秒間の間で0~1をアニメーションし、1以上になったらまた0に戻るようになっています。

非アクティブプリミティブが自分自身がアクティブになるかを判定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/5a20ae20-0146-b61a-230d-5acd140d8f45.png)
簡単に書きました。
自分自身のポイントと先ほど作ったアニメーションしているポイントとの距離を取り、0.1以下ならアクティブになっています。
ここに0.1の遊びを作ってありますが、更新タイミングは別で用意してるので問題ないです。0だとたまに動かない所ができてた。

ここで「@group_start_pt=@ptnum;」という処理を入れていますが、carveでアニメーションを作成した時にどちらからアニメーション開始するかで分割した所で使っています。
ここで0か1かで判断する為に、最初のパズル作成時に分割を使えないのです。

最後のWrangleで、1入力からSOPのアニメーションが入ってるので持ってきます。

諸々グループ等をリセットしたりpointからprimに移したりして、最後にswitchで更新タイミング以外の処理を無効にすれば終わりです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/01b58a2a-7320-d493-369c-bc73c5080beb.png)

## 移動したパネルの上を、実際に移動するポイントを作成する
先ほど作成したsolverで、

・現在アクティブか
・アクティブなら、向きはどちらか

という情報があるので、これを使って移動を行いますが、処理上の縛りでプリミティブを割ってないので、割った物に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/68a8c4fc-33ee-b96f-517b-5f63caaabb37.png)
IDや入出場所や並びが変更されなければ大丈夫です。

それぞれにprim_idを付与し、一致する物という条件で先ほど言ったactiveとstart_ptを持ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/6b74dd1b-e80b-32d8-4642-9fe4b4be7dcb.png)
移動のアニメーションは後付けなので、この2つの情報さえわかれば大丈夫なように作ってあります。

非アクティブを削除し、アクティブなプリミティブにアニメーションを付与すれば移動しているポイントが出来ます。

最後に、TimeShiftを入れておいてください。ここでtimeshift挟まないとビューポートとキャッシュの結果が違うという問題があります。小数点以下のフレームなのか不明ですが、Timeshiftで解決しました。

## 装飾
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/fbd38a3e-1be7-43d0-5757-3ac4c4f6e34a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/c8326c2f-9a27-3e83-379d-40773a6204cb.png)
車とパネルを作り直した。

パネルにはIDが付与されるように作成しているので、同じようにコピーすれば移植出来る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/978d1ba8-e183-7d93-0a11-190f50f78499.png)
車に関しては、そのままコピーすると移動を逆にしている箇所で車が逆になってしまうので、Nを反転させてからコピーする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/ea46ca16-1ffc-e5b3-4407-8978c152e9f6.png)

あとはレンダリングして終わり。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545265/9a011a65-77be-c826-e729-f90e6664d437.png)


さていろいろ作りましたが、実は車自体が移動してるのではなく、パネル毎に移動のアニメーションを連続させているので、見た目同じでもパネルが変わったら別の物になっている為、車のバリエーションが作れないという問題点があります。
移動先の状況と行き先を常に入れ続けるのだとね、クソムズVEX(当社比)しか書かなかったのよ...

# おわりに
長々と書きましたが、なんか汎用性も何も無いシーン説明になっていますね。ふとした思い付きを作りたい時とかに参考にしていただければ幸いです。

では、最後にシーンを添付します。
例の如く、自作発言、無断転載等は禁止です。

共有
https://drive.google.com/drive/folders/1GJbSRvQ2ZLnvSDiVrSg-9M5kchLkPGSs?usp=sharing

ではよいお年を～。