using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.XR; // 振動機能のために必要 using System.Collections; public class ElevatorSceneTransition : MonoBehaviour { [Header("遷移先の設定")] [SerializeField] private string bossSceneName = "BossStage"; [SerializeField] private string playerTag = "Player"; [Header("ローディングUI設定")] [SerializeField] private GameObject loadingCanvas; [SerializeField] private Text progressText; [SerializeField] private Slider progressSlider; // ─── ★ここから下降演出用の設定項目を追加 ─── [Header("エレベーター下降演出設定")] [Tooltip("エレベーターの部屋(壁や床、UIすべて)をまとめている一番親のTransformを指定してください")] [SerializeField] private Transform elevatorCageTransform; [Tooltip("ガタガタ揺れる激しさ(ミリ単位。0.01~0.03あたりがVR酔いしにくく自然です)")] [SerializeField] private float shakeMagnitude = 0.015f; [Tooltip("下降中のコントローラーの振動の強さ(0.0 ~ 1.0)")] [SerializeField] private float hapticAmplitude = 0.15f; // ───────────────────────────────────────── private bool isLoadingStarted = false; private Vector3 originalCagePosition; // 揺らす前のエレベーターの初期位置を記憶する用 private void Start() { if (loadingCanvas != null) { loadingCanvas.SetActive(false); } // 部屋の初期位置を保存 if (elevatorCageTransform != null) { originalCagePosition = elevatorCageTransform.localPosition; } } private void OnTriggerEnter(Collider other) { if (isLoadingStarted) return; if (other.CompareTag(playerTag)) { isLoadingStarted = true; Debug.Log("🛗 プレイヤー乗車:下降演出と非同期ロードを開始します。"); StartCoroutine(LoadSceneAsyncRoutine()); } } private IEnumerator LoadSceneAsyncRoutine() { if (loadingCanvas != null) loadingCanvas.SetActive(true); // 次のシーンの非同期ロードを開始(画面切り替えはまだロックする) AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(bossSceneName); asyncLoad.allowSceneActivation = false; // コントローラーの振動のタイミングを計るためのタイマー float hapticTimer = 0f; // ロード中(progressが0.9未満の間)、ずっとループして演出を実行 while (asyncLoad.progress < 0.9f) { // 1. UIの進捗率(%)を更新 float currentProgress = Mathf.Clamp01(asyncLoad.progress / 0.9f); if (progressText != null) progressText.text = $"LOADING: {(currentProgress * 100f):F0}%"; if (progressSlider != null) progressSlider.value = currentProgress; // 2. 【演出:部屋をガタガタ揺らす】 // 初期位置をベースに、毎フレームごくわずかなランダム座標を計算して部屋を揺らす if (elevatorCageTransform != null) { Vector3 randomOffset = Random.insideUnitSphere * shakeMagnitude; // ※左右前後のブレ(X, Z)のみを揺らし、Y(上下)はあえて固定すると「ガタガタ感」が出ます randomOffset.y = 0; elevatorCageTransform.localPosition = originalCagePosition + randomOffset; } // 3. 【演出:両手のコントローラーを定期的に微振動させる】 hapticTimer += Time.deltaTime; if (hapticTimer >= 0.1f) // 0.1秒ごとにブルブルと脈打つような振動を送る { TriggerBothHandHaptics(hapticAmplitude, 0.08f); hapticTimer = 0f; } yield return null; } // ─── ロード完了後の処理 ─── // 4. 【演出の停止】エレベーターの揺れをピタッと止め、位置を綺麗にリセットする if (elevatorCageTransform != null) { elevatorCageTransform.localPosition = originalCagePosition; } if (progressText != null) progressText.text = "ARRIVED"; if (progressSlider != null) progressSlider.value = 1f; // 到着後の余韻(ガタガタが止まってから、扉が開いてシーンが変わるまでの静寂1秒) yield return new WaitForSeconds(1.0f); Debug.Log("🎉 ボスステージに到着。シーンを切り替えます。"); asyncLoad.allowSceneActivation = true; } /// /// 左右両方のコントローラーを同時に微振動させるヘルパーメソッド /// private void TriggerBothHandHaptics(float amplitude, float duration) { // 左手 InputDevice leftDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand); if (leftDevice.isValid) leftDevice.SendHapticImpulse(0, amplitude, duration); // 右手 InputDevice rightDevice = InputDevices.GetDeviceAtXRNode(XRNode.RightHand); if (rightDevice.isValid) rightDevice.SendHapticImpulse(0, amplitude, duration); } }