---------------------------------------------------------- --- テレポートガン(ルーム限定) --- 弾丸が当たった2点間をテレポートできるゲートが設置されます ---------------------------------------------------------- ---ガンのオブジェクト名 local GUN_NAME = "Gun" ---弾丸のオブジェクト名 local BULLET_NAME = "GateBullet" ---弾丸変更ボタンのオブジェクト名 local CHANGE_GATE_BULLET_BUTTON = "ChangeGateBulletButton" ---撃ちだす弾丸のマテリアル local NOW_GATE_COLOR_MATERIAL = "NowBulletColor" ---最大ゲート数 local MAX_GATE = 2 ---ゲート2種の色定義 local GATE_COLOR = { Color.__new(1, 0.517647, 0.2392156) * 2, Color.__new(0.240566, 0.8085719, 1) * 2 } ---プレイヤー情報 local localPlayer = vci.vc.room.GetLocalPlayer() local playerController = localPlayer.GetRoomPlayerController() ---@type ExportTransform[] ゲートオブジェクト local gates = {} for gateID = 1, MAX_GATE do gates[gateID] = { root = vci.assets.GetTransform("GateRoot" .. gateID), screen = vci.assets.GetTransform("ScreenRoot" .. gateID) } gates[gateID].screen.SetActive(false) vci.assets.material.SetColor("GateColor" .. gateID, GATE_COLOR[gateID]) end ---ガン local gunRoot = vci.assets.GetTransform(GUN_NAME) ---発射位置 local shotPoint = vci.assets.GetTransform("ShotPoint") ---弾丸 local gateBullet = vci.assets.GetTransform(BULLET_NAME) ---現在設定されている弾丸のゲートID local nowGateBulletID = 1 ---弾丸のゲートID変更ボタン local changeGateBulletButton = vci.assets.GetTransform(CHANGE_GATE_BULLET_BUTTON) ---ボタンのスナップ位置 local snapChangeGateBulletButton = vci.assets.GetTransform("SnapChangeGateBulletButton") ---弾丸の発射した方向 local shotForward = Vector3.__new(0, 0, 1) vci.assets.material.SetColor(NOW_GATE_COLOR_MATERIAL, GATE_COLOR[nowGateBulletID]) ---反対ゲートの風景を映すカメラ local cameraRoot = vci.assets.GetTransform("CameraRoot") ---カメラAPIを適用するオブジェクト local camera = vci.assets.GetTransform("Camera") local photographyCamera = vci.cameraSystem.CreatePhotographyCamera(camera) -- プレビュー描画 local previewTextureId = photographyCamera.GetCameraPreviewTextureId() vci.assets.material.SetTexture("Screen", previewTextureId) ---ゲートが両方ともアクティブになっている local isGateBothActive = false ---現在自分プレイヤーと一番近いゲートID local closestGateID = 0 ---全ユーザーで毎フレーム呼ばれる function updateAll() --自分と各ゲートの距離を測定 local playerPos = localPlayer.GetPosition() local gateDistances = {} for gateID = 1, #gates do local gatePos = gates[gateID].root.GetPosition() local distanceForward = gatePos - playerPos distanceForward.y = distanceForward.y / 4 gateDistances[gateID] = Vector3.Distance(distanceForward, Vector3.zero) end --ゲートに近づいた場合、反対のゲートにテレポートする for gateID = 1, #gates do if gateDistances[gateID] < 0.5 then --反対のゲートに移動する local exitGateID = gateID + 1 if exitGateID > 2 then exitGateID = 1 end local exitGatePos = gates[exitGateID].root.GetPosition() local exitGateRot = gates[exitGateID].root.GetRotation() --反対ゲートへ移動後のプレイヤーの方向を計算 local playerRot = localPlayer.GetRotation() local enterGateRot = gates[gateID].root.GetRotation() local resultRot = Quaternion.Euler(0, (exitGateRot.eulerAngles.y - enterGateRot.eulerAngles.y) + playerRot.eulerAngles.y + 180, 0) playerController.TeleportTo(exitGatePos + (exitGateRot * Vector3.__new(0, 0, 1)), resultRot) end end --二つのゲートが出現している場合、近いゲートから反対ゲートの風景を映し出す(非同期) if isGateBothActive then if (gateDistances[1] < gateDistances[2]) and (closestGateID ~= 1) then cameraRoot.SetPosition(gates[2].root.GetPosition()) cameraRoot.SetRotation(gates[2].root.GetRotation()) gates[2].screen.SetActive(false) gates[1].screen.SetActive(true) closestGateID = 1 elseif (gateDistances[2] < gateDistances[1]) and (closestGateID ~= 2) then cameraRoot.SetPosition(gates[1].root.GetPosition()) cameraRoot.SetRotation(gates[1].root.GetRotation()) gates[1].screen.SetActive(false) gates[2].screen.SetActive(true) closestGateID = 2 end else --二つのゲートが出現しているかチェック(出現中はゲートのスケールが1になっている状態とする) if (gates[1].root.GetLocalScale().x > 0.5) and (gates[2].root.GetLocalScale().x > 0.5) then isGateBothActive = true end end --弾丸変更ボタンをスナップ if gunRoot.IsMine then changeGateBulletButton.SetPosition(snapChangeGateBulletButton.GetPosition()) end end ---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。 ---@param use string @押されたアイテムのSubItem名 function onUse(use) --ガンから弾丸を発射 if use == GUN_NAME then gateBullet._ALL_SetActive(true) gateBullet.SetPosition(shotPoint.GetPosition()) gateBullet.SetRotation(shotPoint.GetRotation()) shotForward = shotPoint.GetForward() gateBullet.SetVelocity(shotForward * 30) end --弾丸のゲートID変更(非同期) if use == CHANGE_GATE_BULLET_BUTTON then nowGateBulletID = nowGateBulletID + 1 if nowGateBulletID > MAX_GATE then nowGateBulletID = 1 end vci.assets.material.SetColor(NOW_GATE_COLOR_MATERIAL, GATE_COLOR[nowGateBulletID]) end end ---[SubItemの所有権]アイテムにCollider(not Trigger)が接触したときに呼ばれる。 ---@param item string @SubItem名 ---@param hit string @Collider名 function onCollisionEnter(item, hit) --弾丸が何かしらのコライダーに当たった場合、ゲート発生 if item == BULLET_NAME then --弾丸の発射方向から、逆方向かつ水平のベクトルを取得 local lookForward = shotForward * -1 lookForward.y = 0 lookForward = lookForward.normalized --弾丸のTransformからゲートの出現位置を設定 local bulletPos = gateBullet.GetPosition() local gatePos = bulletPos + (lookForward * 0.5) + Vector3.__new(0, -0.5, 0) local gateRot = Quaternion.LookRotation(lookForward) gates[nowGateBulletID].root.SetPosition(gatePos) gates[nowGateBulletID].root.SetRotation(gateRot) gates[nowGateBulletID].root.SetLocalScale(Vector3.__new(1, 1, 1)) --弾丸を非アクティブ化 gateBullet._ALL_SetActive(false) end end