目次

ふっとばシードさん2

対応バージョンは、UniVCI v0.35以降です。

タルにソードを突き刺し、当たった場合はシードさんが飛び出るVCIです。

サンプルデータ

https://virtualcast.jp/products/b4e760bf6af8ccf7295b24138c1f1dfe72907d61a3a528710a3767d6a675ef68

Unitypackage

gameapi_huttobaseedsan2.zip

コンポーネント設定

VCI Object

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

VCI Sub Item 2 : kanban_root

VCI Sub Item 3 : bullet_seed

VCI Sub Item 4 : button x 4

VCI Sub Item 5 : sward_root x 4

VCIスクリプト

main.lua
local localPlayer =  vci.vc.room.GetLocalPlayer()
-- ローカルプレイヤー以外では nil が返る
local playerController = localPlayer.GetRoomPlayerController() 
 
local player_pos = localPlayer.GetPosition()
local player_rot = localPlayer.GetRotation()
 
--樽--刺さり剣の所有権としても使用
local barrel_root = vci.assets.GetTransform("barrel_root")
 
--ボタン
local button_reset = vci.assets.GetTransform("button_reset")
local button_check = vci.assets.GetTransform("button_check")
local button_bomb = vci.assets.GetTransform("button_bomb")
local button_iambullet = vci.assets.GetTransform("button_iambullet")
 
--持つ剣
local sward_root_ ={}
local sward_model_ = {}--表示非表示切り替えモデル
local sward_is_active_ = {}
local sward_is_grab_ = {}
local sward_reset_pos_ = {}--respawn
local time_lastaction_sward_ = {}
 
for i = 1,4 do-------------------------------------------運用によって変更
    sward_root_[i] = vci.assets.GetTransform("sward_root_"..tostring(i))
    sward_model_[i] = vci.assets.GetTransform("sward_model_"..tostring(i))
    sward_is_active_[i] = true
    sward_is_grab_[i] = false
    --spawn
    sward_reset_pos_[i] = vci.assets.GetTransform("sward_reset_pos_"..tostring(i))
    time_lastaction_sward_[i] = vci.me.Time.TotalMilliseconds
end
 
--------------------
--刺さってる剣
local hit_ ={}--root、座標判定対象
local hitsward_ = {}--表示非表示切り替えモデル
local hitsward_is_active_ = {}
--エフェクト
local hit_effect_ = {}
local flash_effect = vci.assets.GetEffekseerEmitter("flashEff")
 
for i = 1, 20 do------------------------------------------運用によって変更
    --ヒットダミー剣transform取得
    hit_[i] = vci.assets.GetTransform("hit_"..tostring(i))
    hitsward_[i] = vci.assets.GetTransform("hitsward_"..tostring(i))
    hitsward_is_active_[i] = false
    --エフェクト
    hit_effect_[i] = vci.assets.GetEffekseerEmitter("effect_hit_"..tostring(i))
end
 
--animationSE用
local s = vci.assets.GetTransform("sounds_root")--1 open 2 close
local sounds_obj = s.GetAudioSources()
--シードさん位置3D音声
s = vci.assets.GetTransform("se_seedsan")--1 open 2 close
local se_seedsanpos = s.GetAudioSources()
 
 
--ランダム
math.randomseed(os.time())--シードリセット
local bomb_index = nil
 
--弾丸シードさん
local bullet_seed = vci.assets.GetTransform("bullet_seed")
local is_bullet_ready = false--モデルが樽にセットされているか
local ShotPoint = vci.assets.GetTransform("ShotPoint")
--弾丸ローカルアバター
local is_bullet_local_ready = false--自身のローカルアバターが樽にセットされているか
 
 
 
---時間管理用
local time_base = vci.me.Time.TotalMilliseconds
for i=1,#sward_root_ do
    time_lastaction_sward_[i] = vci.me.Time.TotalMilliseconds
end
local timespan = 3000--ミリ秒--手を離してから戻るまでの猶予
---------------------------------------------------------------------
--初期化
--一度目のみupdateから呼ばれる
local is_firsttimedone = false
function StartFirsttime()
    --持つ剣
    for i = 1, #sward_root_ do
        --部屋に入ってきたタイミングで自分が所有権持ち、初期化を行う
        if sward_root_[i].IsMine == true then
            --持てる剣をすべて表示する
            vci.state.Set("IS_SWARD_ACTIVE_"..tostring(i), true)
        end
    end
    --刺さってる剣
    for i = 1, #hit_ do
        --部屋に入ってきたタイミングで自分が所有権持ち、初期化を行う
        if hit_[i].IsMine == true then
            --刺さっている剣を非表示にする
            vci.state.Set("IS_HITDONE_"..tostring(i), false)
        end
    end
    --爆発初期位置
    --部屋に入ってきたタイミングで自分が所有権持ち、初期化を行う
    if barrel_root.IsMine == true then
        --爆発位置ランダム
        bomb_index = math.random(#hit_)
        vci.state.Set("HIT_INDEX_BOMB",bomb_index)
    end
end
 
 
--持つ剣の表示状態の更新
function UpdateSwards()
    --所有権持ち以外は共有変数から状態を更新
    for i = 1, #sward_root_ do
        if sward_root_[i].IsMine == true then
        --所有権持ちかつ一定時間たった時
            if sward_is_grab_[i] == true then
                time_lastaction_sward_[i] = vci.me.Time.TotalMilliseconds
            end
            if time_base - time_lastaction_sward_[i] > timespan then
                --リスポーンへ移動処理
                local pos_a = sward_root_[i].GetPosition()
                local pos_b = sward_reset_pos_[i].GetPosition()
                local rot_a = sward_root_[i].GetRotation()
                local rot_b = sward_reset_pos_[i].GetRotation()
                sward_root_[i].SetRotation(Quaternion.Lerp(rot_a,rot_b,0.1))
                sward_root_[i].SetPosition(Vector3.Lerp(pos_a,pos_b,0.1))
            end
        else--所有権ないとき
            local s_a = vci.state.Get("IS_SWARD_ACTIVE_"..tostring(i))
            if s_a ~= nil then
                sward_is_active_[i] = s_a
            end
        end
        --共通、表示処理--nilも考慮
        --ターゲットの一定距離内に近づいたら向きをターゲットに合わせる
 
        --表示非表示
        if sward_is_active_[i] == true then
            sward_model_[i].SetActive(true)
        elseif sward_is_active_[i] == false then
            sward_model_[i].SetActive(false)
        end
    end
    --手持ち剣が初期位置に戻ったら表示状態にする
    for i=1,#sward_root_ do
        local distance = Vector3.Distance(sward_root_[i].GetPosition(),sward_reset_pos_[i].GetPosition())
        if distance < 0.2 then
            sward_model_[i].SetActive(true)
            if sward_root_[i].IsMine == true then
                sward_model_[i].SetActive(true)
                sward_is_active_[i] = true
                vci.state.Set("IS_SWARD_ACTIVE_"..tostring(i), true)
            end
        end
    end
end
 
--刺さっている剣の表示状態の更新
function UpdateHits()
    --所有権持ち以外は共有変数から状態を更新
    for i = 1, #hit_ do
        if barrel_root.IsMine == true then
            --
        else--所有権なし
            --刺さっている剣
            local h = vci.state.Get("IS_HITDONE_"..tostring(i))
            if h ~= nil then
                hitsward_is_active_[i] = h
            end
        end
        --共通、表示処理--nilも考慮
        if hitsward_is_active_[i] == true then
            hitsward_[i].SetActive(true)
        elseif hitsward_is_active_[i] == false then
            hitsward_[i].SetActive(false)
        end
    end
end
 
--衝突判定
function UpdateDistance()
    --剣ターゲットの範囲に入ったか
    for i=1,#hit_ do--全刺さり剣インデックス
        for j=1,#sward_root_ do--全手持ち剣インデックス
            local distance = Vector3.Distance(sward_root_[j].GetPosition(),hit_[i].GetPosition())
            if distance < 0.1 and sward_root_[j].IsMine == true then--自身が所有権持ってる時しかクリアしない
                if hitsward_is_active_[i] == false then --まだクリアしてない
                    if sward_is_active_[j] == true then--手持ち剣が消えてるときは処理しない
                        --クリアしたときの処理
                        hitsward_is_active_[i] = true --クリア判定に
                        sward_is_active_[j] = false--手持ち剣フラグオフ
                        sward_model_[j].SetActive(false)--手持ち剣を見えなくする
                        hitsward_[i].SetActive(true)--刺さり剣を表示
                        vci.state.Set("IS_SWARD_ACTIVE_"..tostring(j), false)
                        vci.state.Set("IS_HITDONE_"..tostring(i), true)
                        sounds_obj[3]._ALL_PlayOneShot(1)--効果音
                        vci.assets.HapticPulseForceToRightController(3000, 0.1)
                        vci.assets.HapticPulseForceToLeftController(3000, 0.1)
                        hit_effect_[i]._ALL_PlayOneShot()
                        --爆発判定
                        if bomb_index == i then
                            vci.message.Emit("HUTTOBASEEDSAN2", "bomb")--メッセージ送信
                        end
                    end
                end
            end
        end
    end
end
 
function UpdateBombIndex()
    --爆発位置
    if barrel_root.IsMine == true then
        --
    else--所有権なし
        local b = vci.state.Get("HIT_INDEX_BOMB")
        if b ~= nil then
            bomb_index = b
        end
    end
end
 
function Reset()
    --刺さってる剣の状態リセット
    if barrel_root.IsMine == true then
        for i = 1, #hit_ do
            --刺さっている剣を非表示にする
            hitsward_[i].SetActive(false)--刺さってる剣を見えなくする
            hitsward_is_active_[i] = false --未クリア状態に
            vci.state.Set("IS_HITDONE_"..tostring(i), false)
        end
    end
    --乱数リセット
    --爆発位置ランダム
    if barrel_root.IsMine == true then
        bomb_index = math.random(#hit_)
        vci.state.Set("HIT_INDEX_BOMB",bomb_index)
    end
    --シードさんの位置リセット
    if bullet_seed.IsMine == true then
        bullet_seed.SetPosition(ShotPoint.GetPosition())
        bullet_seed.SetRotation(ShotPoint.GetRotation())
        bullet_seed.SetVelocity(Vector3.__new(0,0,0))
        bullet_seed.SetAngularVelocity(Vector3.__new(0,0,0))
    end
end
 
function BombEvent()
    --爆発した
    --メッセージで全員ローカル呼び出し
    flash_effect.PlayOneShot()------------------------
    sounds_obj[1].PlayOneShot(1)--効果音
    --弾が飛ぶかどうか
    if is_bullet_ready == true then
        --シードさんを飛ばす
        ShotBulletSEED()
    end
    if is_bullet_local_ready == true then
        ShotBulletLocal()
    end
end
 
function UpdateBullet()
    --弾丸シードさんが樽に入っているかどうか
    if bullet_seed.IsMine == true then
        local distance = Vector3.Distance(bullet_seed.GetPosition(),ShotPoint.GetPosition())
        if distance < 0.5 then--範囲内にいる
            is_bullet_ready = true
        else
            is_bullet_ready = false
        end
    end
end
 
 
--シードさんを発射する場合
function ShotBulletSEED()
    se_seedsanpos[1].PlayOneShot(1)--効果音--ローカルで鳴らす
    --引っかからないようにやや上に移動させてから加速
    if bullet_seed.IsMine == true then
        bullet_seed.SetPosition(ShotPoint.GetPosition()+ShotPoint.GetUp())
        --bullet_seed.SetRotation(ShotPoint.GetRotation())
        bullet_seed.SetVelocity(ShotPoint.GetUp())
        --bullet_seed.SetAngularVelocity(Vector3.__new(0,0,0))
        bullet_seed.AddForce(1200*ShotPoint.GetUp())
    end
end
 
--ローカルアバターが樽に入っているかどうか-------gameAPI
function UpdateBulletLocal()
    --各々ローカルで自分の位置について処理
    local distance = Vector3.Distance(player_pos,ShotPoint.GetPosition())
    if distance < 0.8 then--範囲内にいる
        is_bullet_local_ready = true
    else
        is_bullet_local_ready = false
    end
end
 
--ローカルアバターを発射する場合
function ShotBulletLocal()
    playerController.TeleportTo(ShotPoint.GetPosition()+ShotPoint.GetUp(), player_rot)
    --bullet_seed.SetRotation(ShotPoint.GetRotation())
    playerController.SetVelocity(ShotPoint.GetUp())
    --bullet_seed.SetAngularVelocity(Vector3.__new(0,0,0))
    playerController.AddForce(1200*ShotPoint.GetUp(), vci.forceMode.Impulse) 
end
 
--自分を弾にする
function MyselfBullet()
    local nowseed_pos = ShotPoint.GetPosition()
    local nowseed_rot = ShotPoint.GetRotation()
    local nowlocalavaterpos = localPlayer.GetPosition()
    local nowlocalavaterrot = localPlayer.GetRotation()
 
    --シードさんの移動
    if bullet_seed.IsMine == true then
        bullet_seed.SetPosition(nowlocalavaterpos+ShotPoint.GetUp())
        bullet_seed.SetRotation(nowlocalavaterrot)
        --bullet_seed.SetRotation(ShotPoint.GetRotation())
        bullet_seed.SetVelocity(Vector3.__new(0,0,0))
        bullet_seed.AddForce(Vector3.__new(0,0,0))
    end
    --ローカルアバターの移動
    if button_iambullet.IsMine == true then
        playerController.TeleportTo(nowseed_pos+ShotPoint.GetUp(), player_rot)
    end
end
 
 
---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    --リセットボタン
    if use == "button_reset" then
        vci.message.Emit("HUTTOBASEEDSAN2", "reset")--メッセージ送信
    end
    --当たり位置チェックボタン
    if use == "button_check" then
        if bomb_index ~= nil then
            hit_effect_[bomb_index]._ALL_PlayOneShot()
        end
    end
    --自爆ボタン
    if use == "button_bomb" then
        vci.message.Emit("HUTTOBASEEDSAN2", "bomb")--メッセージ送信
    end
    --自分が弾になるボタン
    if use == "button_iambullet" then
        vci.message.Emit("HUTTOBASEEDSAN2", "myselfbullet")--メッセージ送信
    end
end
 
--メッセージが来たら起動--------------------------------------------------------------
function onMessage(sender, name, message)   
    if name == "HUTTOBASEEDSAN2" then
        if message == "reset" then
            print("----------------Reset--------------------")
            Reset()
        end
        if message == "bomb" then
            print("----------------bomb--------------------")
            BombEvent()
        end
        if message == "myselfbullet" then
            print("----------------myselfbullet--------------------")
            MyselfBullet()
        end
    end
end
------------------------------------------------------------------------------------
 
---全ユーザーで毎フレーム呼ばれる
function updateAll()
    time_base = vci.me.Time.TotalMilliseconds--現在時間の更新
    --ローカルユーザの位置更新
    player_pos = localPlayer.GetPosition()
    player_rot = localPlayer.GetRotation()
 
    --初期化処理
    if is_firsttimedone == false then
        is_firsttimedone = true--完了フラグ
        StartFirsttime()
    end
    --所有権持ちのみ
 
    --全員呼ばれる
    UpdateDistance()
    UpdateSwards()
    UpdateHits()
    UpdateBombIndex()
    UpdateBullet()
    UpdateBulletLocal()--gameAPI
end
 
 
---[SubItemの所有権&Grab状態]アイテムをGrabしたときに呼ばれる。
---@param target string @GrabされたSubItem名
function onGrab(target)
    local t = string.sub(target,1,11)
    local i = tonumber(string.sub(target,12))
    if t == "sward_root_" then
        if i ~= nil then
            sward_is_grab_[i] = true
        end
    end
end
 
---[not SubItemの所有権&Grab状態]アイテムをUngrabしたときに呼ばれる。
---@param target string @UngrabされたSubItem名
function onUngrab(target)
    local t = string.sub(target,1,11)
    local i = tonumber(string.sub(target,12))
    if t == "sward_root_" then
        if i ~= nil then
            sward_is_grab_[i] = false
        end
    end
end
 
 
 
-- vciアイテムのメッセージを受け取る場合
vci.message.On("HUTTOBASEEDSAN2", onMessage)