目次

お題箱(わんコメ対応)

VirtualCast公式わんコメOSCプラグインでYouTube・ニコニコ生放送のコメントを受信してアクションを起こすVCIのサンプルです。


サンプルデータ

https://virtualcast.jp/products/e17351daaf1ec3e338eeff197018e1939338dcbda5fb58ea78c72cb96173926f

Unitypackage

odaibako.unitypackage

コンポーネント設定

VCI Object

Sub Item 設定一覧 (クリックで展開)
VCI Sub Item : OdaibakoDummy
EffekseerEmitter

VCI Sub Item : OdaiPaperDummy1

VCI Sub Item : OdaiPaperDummy2

VCI Sub Item : OffEffect1
EffekseerEmitter

VCI Sub Item : OffEffect2
EffekseerEmitter

VCIスクリプト

main.lua
----------------------------------------------------------------
--わんコメで先頭に【お題箱】というコメントが入ったコメントを受信したら
--ストックし、お題箱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