秘密作戦002:年末までの秒タイマーを作ってみよう

# 記事の内容
年末タイマー（秒数）を作り方のお話

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/74d3921b-b8ee-9dd7-0b27-01c7d2d1227e.png)

# 対象
VCIやってる方

# 記事作成の趣旨
VCI作成するためには、3DモデリングやLuaスクリプトの作成、その橋渡し等様々な要素技術が必要です。完成までの持って行き方は人によって色々あるかと思いますが、@mikekousyaku　の場合、プロジェクトも技術も要素要素に分けてやっていきたい派の人間です。下記の段取りで進めて行きたいと思っています。

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

@mikekousyaku　が➂まで辿りつけるのかというのもアドベントカレンダーのお楽しみということでお付き合いいただければ幸いです。

※2022年12月4日時点の進捗にて、➂までの到達を断念。VCIアドベントカレンダーでは➁の達成までとしました。穴埋めの記事を書いてくださった@shirohige様、マジすいません（＞人＜）

# 2022年末までの秒数どうやって計算する？
「カレンダー見て、12月31日24時から現在の時間の引き算すればいいんじゃないの？」という最もなご指摘が聞こえてきます。でも私みたいなプログラム初心者には計算の仕方がわからなかったんです。なので記事にさせて貰います。こんな感じで書きました。

:::note alert
このスクリプト、バグ持ちです。
皆様はどこがバグ持ちかお分かり頂けただろうか？


```lua:年末タイマー.lua
function updateAll()
    vci.assets._ALL_SetText("Text","2022年終了まで\n"..nokori().."秒")
end

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

関数、何かいいのないかなーとググっていると、ヤマハ様のページに行き着きました。LuaスクリプトはVCIの場合は3Dモデルの制御に使われますが、ヤマハ様の場合は、ルーターの制御に使えるようにしており、その情報提供として関数の説明をされておられるようです。

http://www.rtpro.yamaha.co.jp/RT/docs/lua/tutorial/library.html#os.time

os.timeという関数が使えそうです。

:::note info
os.time()
指定された時刻を表す数値で、「1980年1月1日 9時0分0秒」からの経過秒数を返します。
:::

多分ですが、UTCとJSTの誤差分が9時となっているのだと思います。じゃあちょっと今の時間計算してみようということで、
秘密作戦001でご紹介した、eclipseがとても強力なお試しツールとなります。

2022年11月25日15:12分くらいに実行
```os.time()
local function main()
print(os.time())
end
main()
```

:::note info
出力結果
1669529520
秒読み開始日を逆算すると、1970年1月1日

計算上の結果(秒読み開始日を1980年1月1日とした場合)
1353856320
:::

なんかズレてると思いまして、公式のLuaのドキュメントをひっくり返しますと

:::note info
os.time ([table])
引数なしで呼ぶと、 現在の時刻を返します。 
:::

ふむふむ。ヤマハさんとほぼ同じ・・・やな。

:::note alert
※注意点
戻り値はシステム依存の意味を持つ数値です。 POSIX、 Windows、 その他いくつかのシステムでは、 この数値は、 ある特定の開始時刻 (「エポック」) からの経過秒数です。 それ以外のシステムでは、 その意味は決まっていません。 time の戻り値は date および difftime への引数としてのみ使うことができます。 
:::

・・・・環境依存・・だと。
ヤマハさんのルーターのOSはわからないけど、環境の違いで10年差がでたということなのですね。プログラムでの時間の扱い、特に暦は沼であることは周知のことだと思われますが、ここでも見事沼にハマリました。

windowsからは2023年1月1日0:00はどう認識されるかといいますと、1672498800となります。
なので年が明けるまでの時間は

:::note info
 年が明けるまでの時間
 167298800-os.time()
:::

:::note alert
といつから思っていた！

UTCの9時間前にはJSTでは年明けなんよ
なので

(167298800-3600×9)-os.time()
=167298800-(os.time()+3600×9)
1672466400-os.time()

が正解。
:::

:::note info
と思っていた時期が私にもありました。

167298800-os.time() （JST誤差を考慮しない式）で動作させて
2022‎年‎10‎月‎30‎日‏‎23:16:25に撮影した写真がコチラ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/74d3921b-b8ee-9dd7-0b27-01c7d2d1227e.png)

撮影時刻から計算した残り時間が5359415と1秒差。
エクセルで計算してるので1秒は誤差やって。

こうなる要因として、os.time()がVirtualCastで動作する時は、
JSTを吐き出すということなのでしょう。

環境が2転3転するとズレの原因が環境依存になってくるとは
やはり沼ですね。
:::

# さて計算方法は分かったワケですが・・・・
VirtualCast上で一人スタジオでシコシコデバッグをしていたわけで、
「やっとｷﾀ━━━━(ﾟ∀ﾟ)━━━━!!」とハッスルして友人の下へ自慢顔でお披露目しにいくと下のような現象に見舞われます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/Gs138c89weE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

秒数カウントの下２桁が何か揺れているぞ！・・・なんだこの表示は！
ということで色々情報を集めていると、VCIの同期処理で失敗しているっぽい。
ということでもう一度コードを見てみよう。

```lua:年末タイマー.lua
function updateAll()
    vci.assets._ALL_SetText("Text","2022年終了まで\n"..nokori().."秒")
end
```

このコードのなかで、同期にかかわる文法は２つ
➀updateAll()
➁_ALL_SetText()



サーバーでの同期手法についてはイマイチ理解できていないのですが、仮説を立てて検証していきます。VCIスクリプトを出した段階で、そこに組み込まれた内容は会場にいるメンバー全員に共有される。

位置や中の情報等は同時にいるメンバーのマシン内で動作し、その中の設定が同期対象となる。updateAll()関数は、そのVCIスクリプトを持つメンバー全てが所有権等にかかわらず毎フレーム毎に全員が実行する。その中で、_ALL_とついたメソッドには、計算結果を全員に共有するという役割がある。という風にAPIを解釈していくと今動いている状態はこういう状態

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/ba0252b0-6edf-6676-2dbc-e5f630ef784c.png)


この中で_ALL_SetText()が使われるという意味は_ALL_(全員に結果を反映させる）という意味合いになってしまうのかな？

つまり3人のユーザーがいると、各人が毎秒90回(フレーム数）の頻度でタイマーの掲示板に270回書き込むというクソ重い処理を見せられていたわけです。しかも３人のフレームは環境依存なので、同期が取られるタイミングも不明です。

これを解消する方法としては、同期処理に関する関数を変更する必要があります。次の2つの方法があるかと思います。

## 方法➀updateAll()をupdate()に変える。

update()関数はVCIの所有者のマシンのみで実行される関数です。ですのでフレーム毎にアップデートする計算は所有者が行い、その変更結果を所有者が実施するということで、VCIに対して複数のメンバーからアクセスされるということがなくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/b7e6c1d7-7a0f-3b90-7cc5-d0f0e1424d04.png)

こうすることによって、_ALL_SetText()でVCIの所有者の計算した結果が、そこにいるメンバー全員に毎秒90回共有されることになるわけです。これは計算する主体が統一されるためカウンターなどの処理ではいいのかなと思っちょります。


## 方法➁に_ALL_SetText()をsetTexT()に変える。

個々の環境で、setTexT()が行われるため、その値は他の人に共有されることはないと思われます。よって見てる秒数が違ったりする可能性があります。しかし方法➀と同じく、異なるマシンから同じVCIに設定を書きに行くという動作がないため、グラつきが解消されると思われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/8c1cbcc2-6cf4-13f7-61b9-f56bf9a93a08.png)


私の好みとしては、私の見てるものとメンバーの見てるものを合わせたいので、方法➀で行きたいと思います。

それにしても、「90フレーム全部で書き換えするの重いよなぁ」と思っていたら、同じこと考えている人「@Nyaf0039V」様がいますね。

https://qiita.com/Nyaf0039V/items/d5ae5174a3ba59db9486

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16614/d5eb68a2-61da-4176-f6ae-6c1b902428dd.png)

すんません。マジすんません。めっちゃ重いコード書いてました。
m(。＿。)m

:::note info
と思っていた時期が私にもありました。

```lua:年末タイマー.lua

local LoopCount = 5 -- 指定した関数が設定したフレーム毎に1回動くための固定値。
local testlate = 0  -- updateの回数をカウントするだけの簡単なお仕事です。

function update()
    testlate = testlate + 1
    if LoopCount <= testlate then
        testlate=0
        vci.assets._ALL_SetText("Text","2022年終了まで\n"..nokori().."秒")
    end
end

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

間引きフレームを大きくすると、カクツキを感じました。秒カウンターだと29フレーム間引くと、ちょっと違和感を感じたので、4フレーム間引く形にすると違和感を感じなくしました。これでも表示処理は1/5なのでまぁいいのではなかろうか。
:::

さて、コードも書き終わったところで、動作確認を


<iframe width="560" height="315" src="https://www.youtube.com/embed/ahKr3_u7eKo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

やっと・・・やっと動いたのか・・お前。。。
でもこれからも環境が変わると動いたり動かなかったりするんか・・・・
大変そうや・・・

# 引用/参考文献
@sirohige　【こんにちは世界(Hello World)】
https://qiita.com/sirohige/items/90f1eef7a8f3a2081e14

@Nyaf0039V 重いVCIアイテムがちょっと軽くなると良いな
https://qiita.com/Nyaf0039V/items/d5ae5174a3ba59db9486