using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactables; public enum BlockColor { Red, Blue, Green } public enum BlockShapeType { TShape, CShape } public class PuzzleBlock : MonoBehaviour { [field: SerializeField] public BlockColor Color { get; private set; } [field: SerializeField] public BlockShapeType ShapeType { get; private set; } [SerializeField] private Transform snapPoint; public static event Action OnPuzzleCompleted; private static readonly HashSet allBlocks = new HashSet(); private PuzzleBlock nearbyPartner; private bool isConnected; // ★ワールド情報ではなく「親階層」と「ローカル情報」を記憶 private Transform initialParent; private Vector3 initialLocalPosition; private Quaternion initialLocalRotation; private Vector3 initialLocalScale; private Rigidbody rb; private XRGrabInteractable grabInteractable; private void Awake() { rb = GetComponent(); grabInteractable = GetComponent(); // ★開始時の「親」と「ローカル座標・回転・スケール」を保持 initialParent = transform.parent; initialLocalPosition = transform.localPosition; initialLocalRotation = transform.localRotation; initialLocalScale = transform.localScale; } private void OnEnable() { allBlocks.Add(this); } private void OnDisable() { allBlocks.Remove(this); } private void OnTriggerEnter(Collider other) { if (isConnected) return; PuzzleBlock partner = other.GetComponentInParent(); if (partner != null && IsValidPartner(partner)) { nearbyPartner = partner; } } private void OnTriggerExit(Collider other) { PuzzleBlock partner = other.GetComponentInParent(); if (partner == nearbyPartner) { nearbyPartner = null; } } private bool IsValidPartner(PuzzleBlock partner) { if (partner == null || partner.isConnected) return false; return partner.Color == Color && partner.ShapeType != ShapeType; } public void OnReleased() { if (isConnected) return; if (nearbyPartner != null) { ConnectToPartner(nearbyPartner); } } private void ConnectToPartner(PuzzleBlock partner) { isConnected = true; partner.isConnected = true; if (snapPoint != null && partner.snapPoint != null) { transform.position = partner.snapPoint.position; transform.rotation = partner.snapPoint.rotation; } else { transform.position = partner.transform.position; transform.rotation = partner.transform.rotation; } LockBlockState(); partner.LockBlockState(); OnPuzzleCompleted?.Invoke(Color); BuffType buff = ConvertColorToBuffType(Color); if (PlayerStatusManager.Instance != null) { PlayerStatusManager.Instance.ApplyBuffsAndResume(buff, buff); } } private void LockBlockState() { if (rb != null) { rb.linearVelocity = Vector3.zero; rb.angularVelocity = Vector3.zero; rb.isKinematic = true; } if (grabInteractable != null) grabInteractable.enabled = false; } public void ResetBlock() { // 1. 手で掴んでいる場合は手離し処理 ForceDrop(); isConnected = false; nearbyPartner = null; // 2. ★親オブジェクト(Canvas)の子へ戻す if (initialParent != null && transform.parent != initialParent) { transform.SetParent(initialParent, false); } // 3. ★Canvas内のローカル位置・回転・スケールを完全復元 transform.localPosition = initialLocalPosition; transform.localRotation = initialLocalRotation; transform.localScale = initialLocalScale; // 4. 物理状態の再初期化 if (rb != null) { rb.isKinematic = false; rb.linearVelocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } // 5. 掴み機能の再有効化 if (grabInteractable != null) { grabInteractable.enabled = true; } } private void ForceDrop() { if (grabInteractable == null || !grabInteractable.isSelected) return; if (grabInteractable.interactionManager != null && grabInteractable.firstInteractorSelecting != null) { grabInteractable.interactionManager.SelectExit(grabInteractable.firstInteractorSelecting, grabInteractable); } } public static void ResetAllBlocksInScene() { foreach (var block in allBlocks) { if (block != null) { block.ResetBlock(); } } } private BuffType ConvertColorToBuffType(BlockColor color) { return color switch { BlockColor.Red => BuffType.AttackUp, BlockColor.Blue => BuffType.DefenseUp, BlockColor.Green => BuffType.Heal, _ => BuffType.AttackUp }; } }