cytanb.luaのススメ 2.同一VCIのmessage競合対策

#はじめに
この記事は、[VCI Advent Calendar 2019](https://qiita.com/advent-calendar/2019/vci)の11日目の記事です。

#使用方法について
[cytanb.luaのススメ 1.導入編](https://qiita.com/feath/items/dd15a9e8c57d67f26d02)
こちらにあります。

#同一VCIのmessage競合問題
VCIのメッセージの仕様についてははちらに記載があります。
[vci.message(VCI間の通信)](https://virtualcast.jp/wiki/doku.php?id=vci:script:reference:message)
>関数一覧
Emit()
メッセージを送信します。
メッセージは、自分自身を含む全ユーザーの全VCIに送信されます。
On()
メッセージを受信します。
メッセージを送信したのと同じVCIでも、別のVCIでもOK。
Onの第１引数と Emitの第1引数 が一致したものを受け取ります。
受け取ったメッセージを第２引数の関数に渡します。
Onは第２引数で指定した受信関数よりも後に定義してください。受信関数よりも前に定義するとエラーとなります。

問題は、Emitが**全ユーザーの全VCIにメッセージを送る**点です。
つまり、同じVCIが２つあって、片方が「右へ動け」というメッセージを送った場合、**２つ両方とも「右へ動け」というメッセージを受信**してしまいます。
#画像による解説
###二人いて2つの同名VCIがある画像。luaファイルは4つ動いています
![Untitled Diagram (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/eb753f90-1754-10f9-8df1-d5b341536375.png)

###自身のVCIのlua全部にメッセージを送りたい！！
![Untitled Diagram (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/368905ae-86d7-0327-77a5-e323c35652c8.png)


###現実(なんでやぁ・・・)
![Untitled Diagram (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/780cc39c-7574-e9e0-3d7a-f3c55eb3d59b.png)


#cytanbのmessage関数を使おう
そこで、cytanbのmessage関数である、

 - cytanb.EmitMessage()
 - cytanb.OnInstanceMessage()

を使います。

#cytanb.EmitMessage()
cytanb_annotations.luaにはこのように記載されています。
>EmitMessage fun (name: string, parameterMap: table<string, any>) @パラメーターを JSON シリアライズして `vci.message.Emit` する。`name` はメッセージ名を指定する。`parameterMap` は送信するパラメーターのテーブルを指定する(省略可能)。また、`InstanceID` がパラメーターフィールド `__CYTANB_INSTANCE_ID` として付加されて送信される。

cytanb.luaを導入すると、自動的に`__CYTANB_INSTANCE_ID`が生成され、`vci.state`に保存されます。
これはUUIDで、簡単に言うとこのVCIだけのIDが付与されます。この値が被ることは理論上無いです。
`cytanb.EmitMessage`では、この`__CYTANB_INSTANCE_ID`をメッセージに追加してメッセージを送信します。

#cytanb.OnInstanceMessage()
cytanb_annotations.luaにはこのように記載されています。
>---@field OnInstanceMessage fun (name: string, callback: fun(sender: table, name: string, parameterMap: table)) @自身のインスタンスから送信されたメッセージを受信するコールバック関数を登録する。パラメーターフィールド `__CYTANB_INSTANCE_ID` を利用してインスタンスの判定を行う。その他の事項については `OnMessage` を参照。

cytanb.EmitMessage()で送信したメッセージはこの関数で受信します。
cytanb.EmitMessage()で送信したメッセージをこの関数で受信すると、`__CYTANB_INSTANCE_ID`を照合して**メッセージの送受信が同じVCI(同名なだけじゃ駄目)のメッセージを送信した時だけOnInstanceMessage()関数内のコードを実行**します。

##つまり画像で表すと
###UUIDがあるおかげで
![Untitled Diagram (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/e6187ebb-0500-5cee-abaa-7b930986752c.png)

###メッセージは届くけど破棄される
![Untitled Diagram (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/0084d6bf-f6f7-58d1-196d-301960a302f7.png)

#テンプレート
[所有権を貫通する銃弾について](https://qiita.com/johnny-um/items/4483019560670a4cb9ae)
にあるコードをcytanb.luaのEmitMessageとOnInstanceMessageに変えて記載しました。このように使います。

```lua:messageTemplate.lua

---銃弾
local bullet = vci.assets.GetSubItem('Bullet')
---銃口
local bullet_out = vci.assets.GetSubItem('BulletOut')

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    ---SubItem名'Gun'をuseしたらメッセージ全員に送信
    if use == 'Gun' then
        cytanb.EmitMessage('GUN_SHOT', true)
    end
end

---射撃メッセージ処理(全員に処理)
function onShot(sender, name, message)
    ---銃弾が銃口に移動
    bullet.SetPosition(bullet_out.GetPosition())
    ---銃弾の速度を消す
    bullet.SetVelocity(Vector3.zero)
    bullet.SetAngularVelocity(Vector3.zero)
    ---銃弾を銃口のz軸方向に飛ばす
    bullet.AddForce(bullet_out.GetForward() * 1500)
end
cytanb.OnInstanceMessage('GUN_SHOT', onShot)