using UnityEngine; public class VRInputPoseDetector : MonoBehaviour { [Header("必須コンポーネント登録")] [SerializeField] private Transform headTransform; [SerializeField] private Transform rightHandTransform; [SerializeField] private Transform leftHandTransform; [Header("構えの判定範囲設定")] [SerializeField] private float minForwardDistance = 0.15f; [SerializeField] private float maxForwardDistance = 0.5f; [SerializeField] private float minHeightOffset = -0.35f; [SerializeField] private float maxHeightOffset = -0.1f; [Header("タイマー・バッファ設定")] [SerializeField] private float requiredPoseTime = 0.3f; [SerializeField] private float poseExitDelay = 0.15f; [Header("視覚フィードバック(ガイドUI設定)")] [SerializeField] private Renderer guideRenderer; [SerializeField] private Color defaultColor = new Color(0.5f, 0.5f, 0.5f, 0.05f); // 普段(超薄いグレー) [SerializeField] private Color chargingColor = new Color(1f, 0.8f, 0f, 0.2f); // 溜め中(うっすら黄色) [SerializeField] private Color successColor = new Color(0f, 0.6f, 1f, 0.4f); // 構え完了(綺麗な青シールド) private float poseTimer = 0f; private float exitTimer = 0f; private bool isPoseMaintained = false; public bool IsPosing => isPoseMaintained; void Update() { if (headTransform == null || rightHandTransform == null || leftHandTransform == null) return; // 両手の世界座標を頭を中心としたローカル座標に変換 Vector3 localRightHand = headTransform.InverseTransformPoint(rightHandTransform.position); Vector3 localLeftHand = headTransform.InverseTransformPoint(leftHandTransform.position); // 両手が構え範囲に入っているかチェック bool isRightHandGuarding = CheckHandPosition(localRightHand); bool isLeftHandGuarding = CheckHandPosition(localLeftHand); bool isBothHandsGuarding = isRightHandGuarding && isLeftHandGuarding; // ─── 🛡️ チャタリング防止付き・構え判定ロジック ─── if (isBothHandsGuarding) { // 範囲内に手が戻ったので、解除カウントダウンを即座にリセット exitTimer = 0f; if (!isPoseMaintained) { poseTimer += Time.deltaTime; if (poseTimer >= requiredPoseTime) { isPoseMaintained = true; Debug.Log("🛡️ 【構え成功】 パリィ受付がアクティブになりました。"); } } } else { // 範囲外に出た場合 poseTimer = 0f; // まだ構えが完了していない段階(溜め中)なら即リセット if (isPoseMaintained) { // すでに構えが成功している状態なら、猶予タイマーを開始(ここで即解除しない!) exitTimer += Time.deltaTime; if (exitTimer >= poseExitDelay) { isPoseMaintained = false; exitTimer = 0f; Debug.Log("❌ 【構え解除】 猶予時間を超えたためガードが解けました。"); } } } UpdateVisualFeedback(isBothHandsGuarding); } private bool CheckHandPosition(Vector3 localHandPos) { bool inForwardRange = localHandPos.z >= minForwardDistance && localHandPos.z <= maxForwardDistance; // Z軸:頭より前に手があるか bool inHeightRange = localHandPos.y >= minHeightOffset && localHandPos.y <= maxHeightOffset; // Y軸:手の高さが適切か return inForwardRange && inHeightRange; } private void UpdateVisualFeedback(bool isBothHandsGuarding) { if (guideRenderer == null) return; // ボスの攻撃を見やすくするため、現在の状態に応じてガイドシールドのカラーを3段階可変させる if (isPoseMaintained) { guideRenderer.material.color = successColor; // 成功:青シールド } else if (isBothHandsGuarding) { guideRenderer.material.color = chargingColor; // 溜め中:黄色 } else { guideRenderer.material.color = defaultColor; // 構えなし:目立たない透明グレー } } }