Auguri Buon Anno！

# 記事の内容
私、三毛公爵は昨年度もアドベントカレンダーに参加しておりまして、年末のカウントダウンタイマーを作りました。

https://qiita.com/mikekousyaku/items/ff25266d436edac39a25

これは何を隠そう、アウグーリオ・ボナーノの布石だったのです。
アニメ「ARIA」に登場する仮想の風習で、年を跨ぐ瞬間にものを投げて「Auguri Buon Anno！（アウグーリオ・ボナーノ！）」イタリア語で「よい年を」と言い放つ行事があります。
このスクリプトは同時にものを自動で投げるスクリプトになります。

![DALL·E 2023-12-13 23.24.22 - A lively scene at a festival in a Venetian town, depicted in a Japanese anime style. It's midnight, and the streets are brilliantly illuminated with c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/4205f769-d4c9-51f2-e19e-63e392f04460.png)

同じ言葉でもアウグーリオボナーノというとお呪いみたいで幸せになれそうです。
へい、そこのおぜうさん。僕とアウグーリオボナーノしませんか？

# 対象
VCIやってる方

# 記事作成の趣旨
昨年、下記３ステップで完成させようとしていた本プロジェクト、忙しくて（言い訳）②で挫折してしまいました。悲願の③を１年越しに達成しようといいうのが記事の趣旨です。アドベントカレンダーの季節が来ると「やり残したプロジェクトが」と思い出したくないので消化しておきましょう。

【去年考えていたステップ】
➀秘密作戦001:Luaで産声をあげよう:Luaの導入とその他周辺技術習得方法
➁秘密作戦002:年末までの秒タイマーを作ってみよう：VCIで実際年末を秒数でカウントダウンするタイマーを作ってみる
➂秘密作戦003:今回作りたい本当のお題：今回作りたい本当のお題

実は飛ばす帽子も準備しておりまして、去年の画像の右端にしっかりとお皿に乗った帽子があるではないですか！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/a948fc04-88fa-bd15-e236-04fe1d0d56f2.png)

今回はVCI Attachableというコンポーネントを使いまして、実現して逝きます。

# 構想
こんな感じでカウントダウン完了した時点での高さを10として、フレームで飛び方と落ち方を決めてやります。
帽子の位置は数値で打ち込むのでアンタッチャブル関係ないよね？アンタッチャブルだよね？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/b2bb1b53-1d36-e491-9695-56d1fc5f98b2.png)

#帽子の準備

ちょちょいとblenderで帽子を作りまして
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/c0cd8f10-be31-911a-2f1a-33a2c43b5d62.png)

これをunityにインポート。コライダーをつけます。
コライダーに関しては過去に@sirohige様がまとめておられるので
こちらに準拠します。

https://qiita.com/sirohige/items/8ed60d2b24a34e113016

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/29015c19-c6c2-c292-a594-bc460c3d9aff.png)

unityの環境にもっていくと何故か白くなってしまいます。

これはmaterial（3Dモデルにおける「マテリアル」とは、その3Dモデルの表面の見た目や質感を決定するための情報や設定のこと）Blenderで使われているものをしっかりと反映できていないからです。

このあたりは非常に深そうな話なので、とりあえずうまくいった方法をご紹介。

https://cgbox.jp/2023/04/15/blender-unity-material/

この記事の通りにしたらここまでいけました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/0204a703-0379-5fa8-e3be-6fba499f9011.png)

なんか光の反射が気になるなと思ったらunity側の光のマークをオフにしたらかなり見えやすくなりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/c9b4d632-4e7a-fe28-fb2e-17ef06b50422.png)

こういう細かいところで結構ひっかかるんですよね。

さてもう少し細かいところを設定していきましょう。

https://qiita.com/sirohige/items/29b052766df87bf9232d

こちらを参考にヒエラルキー構造を整理していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/62ac1740-51ff-b1fc-ed10-efc5143e96d5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/f9080d21-a30e-3002-08e8-271a4f0da478.png)

大きさ間隔等揃えながらシュコシュコやっていきます。

さてここまでできたら、ちょっと装着できるかチェックしてみましょう。

![2023121615352751_マイルーム_mikekousyaku.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/3c544aaf-6125-6f88-3fde-a5528a7fec20.jpeg)

なんということでしょう！
あどけない和風美少女に、紳士の象徴たるシルクハットのミスマッチのギャップ。

これぞ萌えというものでしょう。

さてモデルの原型はできたので、ここからスクリプトをゴリゴリ動かしていきましょう。

帽子が吹っ飛ぶトリガーはもちろん年末のカウントダウンですが、
それではいかんせんテストができません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/9e5b1a05-1a5d-87c8-bcdf-896e912f6b29.png)

ボックスのサブアイテムを握る度に、0.5ｍずつ帽子が上がっていくスクリプトを書いてみました。

これは・・・応用範囲が広そうですね。
空飛ぶカツラ・・・鷲掴みにしたら出てくるもの・・・おっとこれ以上はいけない。

```lua:AuguriBuonAnno.lua
local bousi = vci.assets.GetTransform("hat_04")
local botan = vci.assets.GetTransform("botan_01")
local yomikomi =1000
local test =100

function update()
    if(yomikomi>500)then
    print(botan.GetLocalPosition().x)
    yomikomi=1
    end
    yomikomi=yomikomi+1
end

function onGrab(target)
    if target =="botan_01" then
        local bousi_pos=bousi.GetLocalPosition()
        bousi_pos.y=bousi_pos.y+0.5
        bousi.setLocalPosition(bousi_pos)
        print("掴まれた！")
    end
end
```

しかしなんとここで大きな仕様上の壁が！
アタッチブルなVCIはスクリプトでの移動より、アタッチの方が優先されるらしく、装着した状態で飛ばすことができませんでした。

ママ、年変わりに帽子を空に投げるんだから装備してなくてもセーフよね。

ということで、構想通り、上がり方と落とし方を数値入力して、終了なのですが・・・・・やってみても美しくない。

ということで三毛公爵の数学の式の知識を集結して下記の様なコードに落とし込んでみました。

```lua:AuguriBuonAnno.lua
local bousi = vci.assets.GetTransform("hat_04")
local botan = vci.assets.GetTransform("botan_01")
local yomikomi =1
local keika =0
local start =0
local vx=0
local vy=0
local vz=0
local bousi_pos
local n=1
function update()

    if keika > 0 then
         if start ==0 then
            bousi_pos=bousi.GetLocalPosition()
             vy=(8-bousi_pos.y)/100
             vx=2*(math.random()-0.5)/100
             vz=2*(math.random()-0.5)/100
             start=1
         end

         if start==1 then
            if yomikomi<100 then
                bousi_pos.y=bousi_pos.y+vy/yomikomi*10
                bousi_pos.z=bousi_pos.z+vz
                bousi_pos.x=bousi_pos.x+vx
                bousi.setLocalPosition(bousi_pos)
                yomikomi=yomikomi+1
            end
         end
         
         if yomikomi>99then

            
            if(bousi_pos.y >0)then
            print(math.random()-0.5)  
            n=n+1
                    bousi_pos.y=bousi_pos.y-0.0001*n%100*vy

            bousi.setLocalPosition(bousi_pos)

            end
         end

     end
end

function onGrab(target)
    if target =="botan_01" then
        keika=5
        print("掴まれた！")
    end
end

```
こんな感じで飛びます。

<iframe width="882" height="551" src="https://www.youtube.com/embed/XWPHXOy5nHU" title="2023 12 17 02 36 14" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

現状はここにonGrabでデバッグ用に用意したボックスに紐付けられていますが、これを年末のタイマーと紐付けるだけとなります。
```lua:AuguriBuonAnno.lua
local bousi = vci.assets.GetTransform("hat_04")
local botan = vci.assets.GetTransform("botan_01")
local yomikomi =1
local keika =0
local start =0
local vx=0
local vy=0
local vz=0
local bousi_pos
local n=1
function update()

    if nokori()<0 then
         if start ==0 then
            bousi_pos=bousi.GetLocalPosition()
             vy=(8-bousi_pos.y)/100
             vx=2*(math.random()-0.5)/100
             vz=2*(math.random()-0.5)/100
             start=1
         end

         if start==1 then
            if yomikomi<100 then
                bousi_pos.y=bousi_pos.y+vy/yomikomi*10
                bousi_pos.z=bousi_pos.z+vz
                bousi_pos.x=bousi_pos.x+vx
                bousi.setLocalPosition(bousi_pos)
                yomikomi=yomikomi+1
            end
         end
         
         if yomikomi>99then

            
            if(bousi_pos.y >0)then
            print(math.random()-0.5)  
            n=n+1
                    bousi_pos.y=bousi_pos.y-0.0001*n%100*vy

            bousi.setLocalPosition(bousi_pos)

            end
         end

     end
end

function nokori()
    local byou=math.floor(1704034800-os.time())
    return byou
end
```

ということで、このタイマー動作するかはわかりませんが一応できました。
皆様まだもう少し今年が残っておりますが、来年も良い年でありますように。

アウグーリオ・ボナーノ

![2023121703100539_マイルーム_mikekousyaku.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/d85a73d0-c919-335a-2553-023bed0bda0e.jpeg)

# 最後に
あまり私はガリガリにVCIをプログラムすることがないのですが、このように遊ぶ機会を作ってくださった、さっきのなな様はじめ、参考にさせていただきました先輩諸氏に熱く御礼申します。

# 追記

毎週だいたい日曜日に開催されるバーチャルキャスト意見交換会にて、アドバイスを頂きました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/b018dc73-859d-a7cc-fa45-611e63e0efb4.png)

装備してからでもデタッチして飛ばせばええやん！ということで、こちらの記事をご紹介いただきました。

https://developer.virtualcast.jp/vci-docs/api/classes/ExportTransform/DetachFromAvatar.html

色々自分で作成した記事なのですが、後々残りカウントとかの動作確認とかしていたらバグがポツポツ見つかりまして、記事を作成したあとにようやく形になりました。

追記という形で、記載致します。デタッチ教えていただきました@mumei_daikon　様に感謝を。

```lua:AuguriBuonAnno.lua
--VCIの子オブジェクトとしてunity上で設定した場合
--Luaからはオブジェクトとして受け取るという認識です。
local bousi = vci.assets.GetTransform("hat_04")

--適当に設定した変数です。100frameで打ち上げる予定だったので100フレームカウントする
--そんな変数です。
local yomikomi =1

--デバッグ用変数です。最終的に特段意味はありません。
local keika =0

--帽子を上空へ飛ばす時、帽子の位置により速さ等を変化させます。
--そのためif文に入るための初期化変数を作りました。1度初期化したらおさらばです。
local start =0

--帽子を飛ばす書くx,y,z軸方向の変数です。フレーム毎にこの大きさが変わります。
local vx=0
local vy=0
local vz=0

--帽子の位置を覚えさせるvector3の変数です。
local bousi_pos

--100frame上空に打ち上げた後に、下がり方を規定する変数nです。
--ふわふわさせたかったので剰余で速さが変わる変数nを定義しています。
local n=1

--デバッグ用変数です。ここに入っては意味はありません。
local tobasi=0


--アップデート関数です。
function update()
    --残り秒数をコンソールに表示
    print(nokori())

    --nilバリューを出さないためにbousi_posを初期化します。
    bousi_pos=bousi.GetLocalPosition()

    --年末カウントが以下でかつ、高さが0.1以上の時に帽子を投げ飛ばす条件
    --ただしこれだけだと永遠にぴょんぴょんし続けるのでy>0.1というので一度停めてあります。
    if nokori() < 0 and bousi_pos.y>0.1 then

        --帽子の位置に合わせて帽子の飛ぶ速度を計算、初期化しています。
         if start ==0 then
            bousi_pos=bousi.GetLocalPosition()
             vy=(8-bousi_pos.y)/100
             vx=2*(math.random()-0.5)/100
             vz=2*(math.random()-0.5)/100
             start=1
             --start=1となったのでこれ以降は初期化されません。（if文に入らない)
         end

            --初期化が終わった段階で各方向に先程決めたvx,vy,vzを1フレームずつ動かします。
            --この時、装備しているとスクリプトでの移動に反応しないのでデタッチします。
            --これを100フレーム続けます。
         if start==1 then
            if yomikomi<100 then
                bousi.DetachFromAvatar()
                bousi_pos.y=bousi_pos.y+vy/yomikomi*10
                bousi_pos.z=bousi_pos.z+vz
                bousi_pos.x=bousi_pos.x+vx
                bousi.setLocalPosition(bousi_pos)
                yomikomi=yomikomi+1
            end
 
         end

         --100フレームの上昇が終わったあとの下降のシーケンスに以降します。  
         if yomikomi==100 then

            --帽子が0.2mよりも高い位置にいる際に、延々と降下を続けます。
            --位置変動については降り方をパラメーターでいじっているだけです。
            if(bousi_pos.y >0.2)then
                    n=n+1
                    bousi_pos.y=bousi_pos.y-0.0001*n%100*vy
                    bousi_pos.z=bousi_pos.z+vz/2
                    bousi_pos.x=bousi_pos.x+vx/2
                    bousi.setLocalPosition(bousi_pos)
            else
                --どうしてもカウントダウン終了後のif文の条件定義を正確に決められなかったので、
                --yomikomi=101とすることでその他のif文にひっかからないようにして
                --動作を防いでいます。
                --start=2等も同じ考え方です。
                --while文などで落ちる処理を計算して最後に終わったフラグ変数管理をすればとも
                --思ったのですが、while関数で位置制御をすると一瞬で下降が完了してしまいました。
                yomikomi=101
                start=2
                keika=0
            end
        
         end

     end
end




function nokori()
    local byou=math.floor(1704034800-os.time())
    return byou
end  
```

それでは皆様。良いとしを！