VirtualCast公式わんコメOSCプラグインでYouTube・ニコニコ生放送のコメントを受信してアクションを起こすVCIのサンプルです。
---------------------------------------------------------------- --わんコメで先頭に【お題箱】というコメントが入ったコメントを受信したら --ストックし、お題箱ObjをUSEしたら、ストックしたお題が出てくるVCI --ストック方法に関しては、VCIオーナーのローカルにて保存 --VCIオーナーが落ちたり、再凸した場合にはリセットされる仕様 --(↑保存性を高めると、データ上限を考慮した実装が必要となるため) ---------------------------------------------------------------- local capNum = 15 --1行あたりの文字数 local extend = 0.134 --紙を伸ばす際の1行あたりの伸び量 local spawnPos = vci.assets.GetTransform("SpawnPos") local odaiPaper = {} local paperSize = {} local commentText = {} for i = 1, 2 do odaiPaper[i] = vci.assets.GetTransform("OdaiPaperDummy"..i) paperSize[i] = 100 commentText[i] = nil end local odaibakoTbl = {} local keyWord = "【お題箱】" local roomFlag local preUseTime = 0 local useBuffer = 0.2 --お題箱の中身から、コメントを取り出す処理 local function odaiTextSet(num) --コメントの改行処理 local odaiName = odaibakoTbl[num][1] local odaiComment = odaibakoTbl[num][2] local displayText = "<align=flush>" local lineLimitNum = 5 while #odaiComment ~= 0 do if #odaiComment <= capNum then displayText = displayText.."<align=justified>"..string.sub(odaiComment,1,capNum).."<br>" else displayText = displayText..string.sub(odaiComment,1,capNum).."<br>" end odaiComment = string.sub(odaiComment, capNum+1, -1) lineLimitNum = lineLimitNum - 1 end --頭に名前を付ける処理 displayText = "<line-height=0.6em><size=0.45em><b><align=left>"..odaiName.."<br>"..displayText lineLimitNum = lineLimitNum - 1 --コメントが短い時にしたの改行を入れて行数を整える処理 while lineLimitNum > 0 do displayText = displayText.."<br>" lineLimitNum = lineLimitNum - 1 end --紙の移動処理 --所有権がない場合は、メッセージで所有権を持つ人に移動処理を依頼する --紙サイズをstateにて共有 local paperNum = vci.state.Get("PaperNum") if lineLimitNum < 0 then if odaiPaper[paperNum].IsMine then odaiPaper[paperNum].SetActive(true) vci.state.Set("PaperActive"..paperNum, true) odaiPaper[paperNum].SetPosition(spawnPos.GetPosition()) odaiPaper[paperNum].SetRotation(spawnPos.GetRotation()) odaiPaper[paperNum].SetLocalScale(Vector3.one) vci.assets.GetTransform("OdaiPaper"..paperNum).SetLocalScale(Vector3.__new(100,100,100*(1+extend*(-lineLimitNum)))) else vci.message.EmitToSelf("OdaiPaperTransformPlz", {paperNum}) end vci.state.Set("PaperSize"..paperNum, 100*(1+extend*(-lineLimitNum))) else if odaiPaper[paperNum].IsMine then odaiPaper[paperNum].SetActive(true) vci.state.Set("PaperActive"..paperNum, true) odaiPaper[paperNum].SetPosition(spawnPos.GetPosition()) odaiPaper[paperNum].SetRotation(spawnPos.GetRotation()) odaiPaper[paperNum].SetLocalScale(Vector3.one) vci.assets.GetTransform("OdaiPaper"..paperNum).SetLocalScale(Vector3.one*100) else vci.message.EmitToSelf("OdaiPaperTransformPlz", {paperNum}) end vci.state.Set("PaperSize"..paperNum, 100) end --vci.assets.GetTransform("OdaibakoDummy").GetAudioSources()[1]._ALL_PlayOneShot(0.7) print("お題出現SE") vci.assets.GetEffekseerEmitter("OdaibakoDummy")._ALL_PlayOneShot() --コメントの書き込み処理(stateだと遅い場合があったので、ここはALLで実行) --コメントの共有処理 vci.assets._ALL_SetText("Text"..paperNum, displayText) vci.state.Set("Text"..paperNum, displayText) --表示したコメントの除外処理 table.remove(odaibakoTbl, num) --次の紙処理を行うPaperNumの更新処理 if paperNum == 1 then paperNum = 2 elseif paperNum == 2 then paperNum = 1 end vci.state.Set("PaperNum", paperNum) end --お題箱がUSEされた際の処理 --オーナーにて、お題箱にコメントがあった際に実行 local function odaiOpenProcess() if vci.state.Get("OwnerID") == vci.vc.room.GetLocalPlayer().GetId() then if #odaibakoTbl ~= 0 then odaiTextSet(math.random(#odaibakoTbl)) end end end --紙の所有権がオーナー以外にあった際の、紙のスポーン処理 local function odaiPaperTrn(sender, name, message) local num = message[1] if odaiPaper[num].IsMine then odaiPaper[num].SetActive(true) vci.state.Set("PaperActive"..num, true) odaiPaper[num].SetPosition(spawnPos.GetPosition()) odaiPaper[num].SetRotation(spawnPos.GetRotation()) odaiPaper[num].SetLocalScale(Vector3.one) end end vci.message.On("OdaiPaperTransformPlz", odaiPaperTrn) local youtubeAdress1 = "/vc-official/onecomme/youtube/comment" local youtubeAdress2 = "/vc-official/onecomme/youtube/super" local niconicoAdress1 = "/vc-official/onecomme/niconico/comment" local niconicoAdress2 = "/vc-official/onecomme/niconico/gift" --VCIオーナーにてコメントを受信 --keyWord(【お題箱】)が頭にあった場合、お題箱Tblに名前とコメントを保存 local function wankomeOdaibako(content) if roomFlag then if vci.state.Get("OwnerID") == vci.vc.room.GetLocalPlayer().GetId() then local oscData = json.parse(content) if string.sub(oscData.comment, 1, #keyWord) == keyWord then --print(oscData.comment) table.insert(odaibakoTbl, {oscData.name, string.sub(oscData.comment, #keyWord+1)}) end end end end vci.osc.RegisterMethod(youtubeAdress1, wankomeOdaibako, {ExportOscType.BlobAsUtf8}) vci.osc.RegisterMethod(youtubeAdress2, wankomeOdaibako, {ExportOscType.BlobAsUtf8}) vci.osc.RegisterMethod(niconicoAdress1, wankomeOdaibako, {ExportOscType.BlobAsUtf8}) --vci.osc.RegisterMethod(niconicoAdress2, wankomeHakushu, {ExportOscType.BlobAsUtf8}) --ルームのみ動作を保証するため、ルームチェック if vci.vc.GetSpaceType() == ExportVcSpaceType.studio then roomFlag = false else roomFlag = true end --お題の紙の非アクティブ化 for i = 1, 2 do odaiPaper[i].SetActive(false) end --初期化 --VCIの本来のオーナーのみで実行 --stateの初期化を行う local function initialize() if roomFlag and vci.assets.IsMine then --VCIオーナー読み込み時にStateの初期化 --(お題箱コメントの収集はオーナーにて行うため) if vci.state.Get("OwnerID") == nil then vci.state.Set("OwnerID", vci.vc.room.GetLocalPlayer().GetId()) vci.state.Set("PaperNum", 1) vci.state.Set("Switch", false) for i = 1, 2 do vci.state.Set("PaperActive"..i, false) vci.state.Set("PaperSize"..i, 100) vci.state.Set("Text"..i, " ") end elseif vci.state.Get("OwnerID") == vci.vc.room.GetLocalPlayer().GetId() then vci.state.Set("PaperNum", 1) vci.state.Set("Switch", false) for i = 1, 2 do vci.state.Set("PaperActive"..i, false) vci.state.Set("PaperSize"..i, 100) vci.state.Set("Text"..i, " ") end end end end initialize() function update() if roomFlag then --スイッチがONとなった際、お題箱にコメントがあれば、出力処理 if vci.state.Get("Switch") then if #odaibakoTbl ~= 0 then odaiOpenProcess() end vci.state.Set("Switch", false) end end end function updateAll() for i = 1, 2 do --紙のアクティブの共有 if vci.state.Get("PaperActive"..i) ~= nil then odaiPaper[i].SetActive(vci.state.Get("PaperActive"..i)) end --紙のサイズの共有 if vci.state.Get("PaperSize"..i) ~= nil then if paperSize[i] ~= vci.state.Get("PaperSize"..i) then paperSize[i] = vci.state.Get("PaperSize"..i) vci.assets.GetTransform("OdaiPaper"..i).SetLocalScale(Vector3.__new(100,100,paperSize[i])) end end --コメントの共有 if vci.state.Get("Text"..i) ~= nil then if commentText[i] ~= vci.state.Get("Text"..i) then commentText[i] = vci.state.Get("Text"..i) vci.assets.SetText("Text"..i, commentText[i]) end end --エフェクトオブジェクトのTransform if odaiPaper[i].IsMine then vci.assets.GetTransform("OffEffect"..i).SetPosition(odaiPaper[i].GetPosition()) vci.assets.GetTransform("OffEffect"..i).SetRotation(odaiPaper[i].GetRotation()) vci.assets.GetTransform("OffEffect"..i).SetLocalScale(odaiPaper[i].GetLocalScale()) end end end function onUse(use) --お題箱をUSEした際、Stateをtrueとして、VCIオーナーに出力してもらう --オーナーが不在の場合、trueとしない if use == "OdaibakoDummy" then if os.time(sec)-preUseTime >= useBuffer then if vci.state.Get("OwnerID") ~= nil then local ownerCheck = false local players = vci.vc.room.GetAllPlayers() for i = 1, #players do if players[i].GetId() == vci.state.Get("OwnerID") then ownerCheck = true end end if ownerCheck then vci.state.Set("Switch", true) end end preUseTime = os.time(sec) end end --紙をUSEした際、紙を消す処理を行う --stateに書きこんで状態を共有 for i = 1, 2 do if use == "OdaiPaperDummy"..i then vci.state.Set("PaperActive"..i, false) vci.state.Set("PaperNum", i) vci.assets.GetEffekseerEmitter("OffEffect"..i)._ALL_PlayOneShot() end end end