Vキャスでネームプレートを並べてみよう

#はじめに

バーチャルキャスト（以下、Vキャス）でネームプレートの並べ替えができるVCIバージョンアップが出ましたね。
これは、VCIでいろいろな方が並べ替えを作ってくれる！
と思っていたのですが、なかなか作られていないようです。
いろいろな方が作るたくさんのパターンが有った方が楽しいと思うのですよ。

**でも、いろいろと難しいんでしょ？**

と思われているようなので、超シンプルなネームプレート並べ替えVCIのLuaサンプルを2種類説明します。
説明が分からない方は、そのままwikiのVCIチュートリアルで作ったVCIにコピペすれば動作しますので遊んでみてください。

それでもダメならTSOに公開されているこちらのVCIを使ってみてください。
[Vキャス用コメビュ](https://seed.online/items/d748edf4f8703e964e41865b8034416fdc9619fff996126be558249127d17449)

#名前の並べ替えが難しい？

・3Dでの座標演算とか意味が分からない？
　　分からなくてもできますから大丈夫
・ベクトルとか角度とかもういや！
　　面倒なのでやりません、角度計算うまく行ったら教えてください
・所有権とかVCIオーナーとかどうやればいいか分からん！
　　今回は部屋主でVCIオーナーしか動かせないので問題ないです

そんなわけで知らなくても簡単に作れますので、サンプルを参考にして遊んでください。

#仕様を決める

簡単に何を作るか決めておきます。
今回はすごく簡単にしたかったので、使い方はこれだけです。

**キューブをグリップするとネームプレートの並べ替えを実行する**

並べ方は2種類
1. ウィンドウモニターに対して位置固定で並ぶ（右下）
2. 部屋主さんの名前の上に並ぶ、位置や角度は部屋主さんのネームプレート次第

できたらこんな感じで並びますよ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545584/2c281463-9fcb-2dff-e8b4-eaf98163ca51.png)


Luaサンプルは2種類ありますので、お好みでお試しください。
説明も書いておきます、Luaの中にもコメントを入れておきますのでご参考に。
そして、入れ替えたり改造して好みの置き方を作って自分だけのVCIをTSOにアップしてみてくださるとうれしいです。

※ネームプレートの並べ替えはVCIの仕様により、枠主さんが自分で出したVCIでしか動作しませんのでご注意ください。
※本Luaサンプルの改変・機能追加・変更などはどんどんやって、VCIを作ってみてください。
※スクリプト改変によりなんらかの問題が発生した場合は自己責任でお願いします。

#チュートリアルのキューブを使う

なにかグリップできるオブジェクトが必要なので作りましょう。
Unityで何かのオブジェクトを1個作れば良いのですが、作り方や名前なども有るのでwikiのこちらを参照してキューブを作りましょう。
[Vキャスwiki：初めてVCIを作成する](https://virtualcast.jp/wiki/doku.php?id=vci:beginner)

チュートリアルが終わったら保存しておいてください。

#ウィンドウモニターに対して中から見て右下に並べるLuaサンプル
それではソースコードです。

```Lua:NameSort01.lua
------------------------------------------------------------
-- 名札並べ替えサンプル：右下に並べてみよう
-- 2020.01.22
------------------------------------------------------------
local Cube01 = vci.assets.GetSubItem("Subitem1")
--wikiで作るとキューブの名前がSubitem1になっています

-- UseできるのはVCIオーナーに限定する
function onUse(target)
	if vci.assets.IsMine then 
		name_sort()
	end
end

function name_sort() --並べ替え関数
	local i
	local position = Vector3.__new(0, 0, 0) --WindowCameraの座標用
	local position00 = Vector3.__new(0, 0, 0) --座標の保存用
	local rotate = Quaternion.identity --角度用
	local forward = Vector3.__new(0, 0, 0) --前方単位ベクトル用
	local right = Vector3.__new(0, 0, 0) --右向き単位ベクトル用
	local up = Vector3.__new(0, 0, 0) --上向き単位ベクトル用
	local Local_id
	local testName
	local WinCam

	-- WindowCameraが取れないときは実行しないように判定
	if vci.studio.HasWindowCamera() == false then
		-- WindowCameraをとれないときはリターン
		return
	end
	--座標とか角度とか取り込み
	WinCam = vci.studio.GetWindowCamera()
	position = WinCam.GetPosition()	--カメラ座標
	rotate = WinCam.GetRotation()	--カメラ角度
	forward = WinCam.GetForward()		--カメラ前単位ベクトル
	right = WinCam.GetRight()		--カメラ右単位ベクトル
	up = WinCam.GetUp()				--カメラ上単位ベクトル
	position00 = position			--現在位置を保存
	position = position + forward *0.5 --WinCamの前方に出す、離して名札のサイズ調整

	-- プレートの初期位置を作成
	-- 画面右下
	position = position - right * 0.9 - up*0.61

	local _avaters = vci.studio.GetAvatars() -- アバター情報取得のため

	for i = 1, #_avaters do -- 取得したアバターの人数分のループ
		Local_id=_avaters[i].GetId() -- アバターiのID取得
		if vci.studio.HasNameBoard(Local_id) ~= false then -- 名札が取り込めた時に実行
			testName = vci.studio.GetNameBoard(Local_id) -- 名札情報を取得
			testName.SetPosition(position) --名前を移動
			testName.SetRotation(rotate) --名前の角度調整
		end
		-- 次の座標を計算する
		-- 今出したネームプレート上ちょっとディスプレイ側に表示
		position = position + up*0.08 - forward*0.005
	end
end	
```

##NameSort01.luaの解説

**onUseのところ**
キューブを握られたら並べ替える関数を呼び出します

ここでonUse内の処理をIsMineで判定して実行しています
IsMineはVCIオーナーさんだけが実行したい場合に使用します
並べ替えは部屋主さんでVCIオーナーさんだけしか使えないので、VCIオーナーの判定を入れています
この場合はオーナーさん以外使用できません、使えるようにするときは説明が大変なので今回はこれくらいでゆるしてください

**name_sort()：並べ替え関数**
並べ替えを行う時に必要なのは、1枚目の名前を置く位置情報、2枚目以降のずらす量、名前の角度が必要になります

一枚目の名前はWindowCameraの位置と角度から求めます。
WindowCameraの位置、角度、3方向の単位ベクトルをGetします。

WindowCameraの座標に名前を出すとWindowCameraに重なって見えないので、少し前にだしましょう。
前に出すことで、名前の大きさの調整することができます。

WindowCamera座標＋単位ベクトルforward 

を計算すると、WindowCameraの前方1mの座標を求める事ができます。
（拡大縮小できるアイテムは単位ベクトルも大きさが変わるみたい）
1mだと遠いのでもう少し近づけましょう

WindowCamera座標＋単位ベクトルFront*0.5

これでWindowCameraから50cm離れた座標が計算できました。
単位ベクトル使うと相対位置計算が簡単になりますね。

同じように右下を指定します。
適当な値を入れて調整しました。

モニター座標前方50cm ー 単位ベクトルRight*0.9 ー 単位ベクトルUp*0.3

この座標はVキャスで使ってみてお好みの値に調整してみてください。

**次は、面倒そうな角度です。**
**角度は計算しません、する必要はありません！**

ウィンドウモニターで取得した角度をそのままネームプレートに使います。
この角度を使うと、ウィンドウモニターに対して平行に表示されます。
ウィンドウモニターを傾けると、名前も傾いて表示されるので放送画面上では常に同じ位置に表示されます。

これでネームプレートの座標と角度が計算できましたので、Setをしてみましょう。
一枚目が右下に表示されるようになりました。

##2枚目以降の名前の位置
2枚目以降をテストするためには自分で部屋を立てて凸にだれか来てもらう必要が有ります。
凸して動かそうとすると、部屋主さんじゃないのでネームプレートは動かせません。
ネームプレート関係で一番問題になるのは、誰かに凸してもらわないと試験できないところかも。

2枚目以降の表示位置はいい感じの差分でずらして行けば良いだけです。
サンプルではこんな式でネームプレートを上にずらして表示します。

一枚目の座標 + 単位ベクトル上*0.08 - 単位ベクトル前*0.005

ここで、前もちょっとずらしていますが、これはネームプレートが重なるとチラチラするので、わざとずらしています。

同じようにループを使って人数分の座標と角度を指定すると終了です。

##動作テスト
さっき作ったチュートリアルのキューブのLuaを書き換えてTSOにアップしてください。

動作確認すると、たまに並ばないネームプレートが出ます。
直前に誰かがネームプレートを移動したりすると、ネームプレートの情報取得に失敗するようです。
ネームプレートの情報取得に失敗したときは、座標計算だけを行い次のネームプレートを並べます。
この時にネームプレートが1枚取り残されたり、並ばなかったりしますが仕様です。
もう一度グリップしてください。

#部屋主さんの名前の上に並べるLuaサンプル
さっきのサンプルではモニター位置から最初の名前の位置を計算していました。
部屋主さんのネームプレートを初期位置にすればそのまま上に並べる事ができそうです。

ということで1枚目の名前の位置を初期値にするように変更してみたのがこちらのサンプルです。

```Lua:NameSort02.lua
------------------------------------------------------------
-- 名札並べ替えサンプル：枠主の名前の上に並べてみる
-- 2020.01.22
------------------------------------------------------------

local Cube01 = vci.assets.GetSubItem("Subitem1")

function name_sort()
	--local test
	local i
	local position = Vector3.__new(0, 0, 0)
	local position00 = Vector3.__new(0, 0, 0)
	local rotate = Quaternion.identity
	local forward = Vector3.__new(0, 0, 0)
	local right = Vector3.__new(0, 0, 0)
	local up = Vector3.__new(0, 0, 0)
	local Local_id
	local testName
	local WinCam

	if vci.studio.HasWindowCamera() == false then
		-- WindowCameraをとれないときはリターン
		return
	end

	WinCam = vci.studio.GetWindowCamera()
	position = WinCam.GetPosition()	--カメラ座標
	rotate = WinCam.GetRotation()	--カメラ角度
	forward = WinCam.GetForward()		--カメラ前
	right = WinCam.GetRight()		--カメラ右
	up = WinCam.GetUp()				--カメラ上
	position00 = position			--現在位置を保存
	position = position + foard *0.5 --カメラを前方に出す、名札のサイズ調整

	-- プレートの初期位置を作成
	-- 画面右
	position = position - right * 0.92 + up*0.61

	local _avaters = vci.studio.GetAvatars() -- アバター情報取得のため

	for i = 1, #_avaters do -- 取得したアバターの人数分のループ
		Local_id=_avaters[i].GetId() -- アバターiのID取得
		if vci.studio.HasNameBoard(Local_id) ~= false then -- 名札が取り込めた時に実行
			testName = vci.studio.GetNameBoard(Local_id) -- 名札情報を取得

			if i == 1 then
				-- 一人目のときは移動せずに座標を取得する
				position = testName.GetPosition()	--名札座標
				rotate = testName.GetRotation()		--名札角度
				forward = testName.GetForward()		--名札前 単位ベクトル
				--right = testName.GetRight()		--名札右 単位ベクトル(今回は使わない）
				up = testName.GetUp()				--名札上 単位ベクトル
			end	

			testName.SetPosition(position)
			testName.SetRotation(rotate)
		end
		-- 次の座標を計算する ここはさっきと同じ
		position = position + up*0.08 - forward *0.005
	end
end	

-- UseできるのはVCIオーナーに限定する
function onUse(target)
	if vci.assets.IsMine then 
		name_sort()
	end
end
```

##Lua解説
先ほどのLuaとほとんど同じですが、一枚目の計算方法がちょっと違っていますので注意してください。
サンプル用に先ほどのプログラムを書き換えているため、かなり無駄がありますがゆるしてください。

一枚目の置く位置を一枚目の座標と角度で計算して、あとは前と同じです。
簡単ですね。

#注意点
ネームプレートの並べ替えは部屋主さんが対象となるVCIを出した時にしか動作しませんので、ご注意ください。
TSOに公開しているコメビュの並べ替え機能は定期的に自動で並べ替えを行います、このスクリプトをテストするときはコメビュを消すか、”NameOff”コマンドでコメビュの並べ替え機能をOFFにしてからお試しください。

名前の位置は計算するよりも実際に出してみて確認した方が早いです、パラメータを変更してお好みの位置を探してみてください

#おわりに
分からないところがありましたら、ツイッターか枠でお気軽に質問してください。

今回のサンプルは並べ替えの基本的な部分だけなので、並べ方の切替や自動並べ替えなんかは全部省略しています。
どんどん改造してお好みの並べ替え機能を作ってみてください。

そしてやっぱり分からないって方は、コメビュとセットになったこちらのVCIを使ってください。
[Vキャス用コメビュ](https://seed.online/items/d748edf4f8703e964e41865b8034416fdc9619fff996126be558249127d17449)

[今回説明した名札を並べるキューブ](https://seed.online/items/d96c0b4ba6a5cc3b2c3eea042d5ad7147b1db13f9f2269bece1e76c573e2d209)

おわりますノシ