対応バージョンは、UniVCI v0.35以降です。
手か頭に装着して高所から飛び降りると、ふわふわ降下できるVCIです。
Useすると開閉します。
-- ローカルプレイヤー取得 local localPlayer = vci.vc.room.GetLocalPlayer() -- 重力 local gravity = Vector3.Magnitude(vci.vc.room.GetGravity()) -- ローカルプレイヤーのみ取得可能 local playerController = localPlayer.GetRoomPlayerController() -- 持つところ local handle = vci.assets.GetTransform("Parasol") local openPart = vci.assets.GetTransform("Palasol_Open") local closePart = vci.assets.GetTransform("Parasol_Close") local fallStartedY = 0 local grounded = false local prevTime = os.time() local isGrabbed = false local isOpen = false function change_parasol_state(sender, name, message) if message == true then open(true) else close(true) end end vci.message.On("change_parasol_state", change_parasol_state) function open(useSfx) isOpen = true openPart.SetActive(true) closePart.SetActive(false) if useSfx then -- 効果音がある場合はここで再生 -- vci.assets.PlayAudioFromName("OpenParasol") end end function close(useSfx) isOpen = false openPart.SetActive(false) closePart.SetActive(true) if useSfx then -- 効果音がある場合はここで再生 -- vci.assets.PlayAudioFromName("CloseParasol") end end -- 初期化処理 if vci.assets.IsMine then -- 初期化時に所有権があるなら、傘を閉じて初期化 vci.state.Set('is_open', 0) close(false) else -- 所有権を持ってない場合は、今の状態に追従して初期化 if vci.state.Get('is_open') == 1 then open(false) else close(false) end end function updateAll() local time = os.time() local deltaTime = time - prevTime; prevTime = time local currentV = playerController.GetVelocity() local mass = playerController.GetMass() -- 所有権があり、持ってるか手にくっついてるときのみ動作 if handle.IsMine == false then return end if handle.IsAttached == false and isGrabbed == false then return end -- 傘を閉じているなら何もしない if isOpen == false then return end -- 地面から離れたらセットアップ if grounded == true and playerController.IsGrounded() == false then initalize() end grounded = playerController.IsGrounded() -- 地面についたら終了 if grounded == true then return end -- ひっくり返してるなら何もしない if Vector3.Dot(Vector3.down, handle.GetUp()) > 0 then return end -- 上に向かって飛んでる時は何もしない if currentV.y > 0 then return end local y = localPlayer.GetPosition().y local deltaY = fallStartedY - y -- 現在位置よりも上昇したら再計算 if deltaY < 0 then initalize() return end -- 落下開始地点を徐々に現在の位置に追従させる fallStartedY = lerp(fallStartedY, y, 5.0 * deltaTime ) -- 落下した分を速度に変換する(グライダーと同じロジック) local next = handle.GetUp().normalized * math.sqrt(2.0 * gravity * deltaY) -- 速度の更新にlerpを挟んでふわふわ感を出す next = Vector3.lerp(currentV, next, 8 * deltaTime) -- SetVelocityだと水平移動が上書きされてしまうため、AddForceで速度を上書きすることで水平移動もできるようにする -- F = m * a = m * (v / s) playerController.AddForce(mass * (next - currentV) * (1.0/deltaTime), vci.forceMode.Force) end function onGrab(target) if target == "Parasol" then isGrabbed=true end end function onUngrab(target) if target == "Parasol" then isGrabbed = false end end function onUse(use) if use == "Parasol" then if isOpen == true then vci.message.EmitWithId("change_parasol_state",false,vci.assets.GetInstanceId()) vci.state.Set('is_open', 0) else vci.message.EmitWithId("change_parasol_state",true,vci.assets.GetInstanceId()) vci.state.Set('is_open', 1) end end end function initalize() -- 落下開始地点 fallStartedY = localPlayer.GetPosition().y end function lerp(a,b,t) return a + ( b - a ) * clamp01(t) end function clamp01(x) if x < 0 then return 0 end if x > 1 then return 1 end return x end