using System.Collections.Generic; using UnityEngine; public enum PlaneAxis { XZ = 0, // Horizontal plane (Y = 0) XY = 1, // Front plane (Z = 0) YZ = 2 // Side plane (X = 0) } [System.Serializable] public class VirtualPlane { public PlaneAxis planeAxis; public GameObject canvasGrid; // The Canvas with grid visual [HideInInspector] public GameObject colliderObject; // The collider (generated dynamically) } public class CanvasVirtualPlatformManager : MonoBehaviour { private static CanvasVirtualPlatformManager instance; [Header("Plane Configurations")] [SerializeField] private VirtualPlane[] virtualPlanes; [Header("Collider Settings")] [SerializeField] private float colliderThickness = 0.1f; [SerializeField] private string colliderLayer = "World"; [Header("Settings")] [SerializeField] private PlaneAxis currentPlane = PlaneAxis.XZ; [SerializeField] private bool platformVisible = true; private void Awake() { if (instance == null) instance = this; else Destroy(this.gameObject); } public static CanvasVirtualPlatformManager getInstance() => instance; private void Start() { GenerateColliders(); UpdatePlaneVisibility(); } /// /// Generate colliders for all planes based on their canvas sizes /// private void GenerateColliders() { foreach (var plane in virtualPlanes) { if (plane.canvasGrid == null) continue; // Get the canvas component Canvas canvas = plane.canvasGrid.GetComponent(); if (canvas == null) continue; RectTransform rectTransform = canvas.GetComponent(); if (rectTransform == null) continue; // Create collider object if (plane.colliderObject != null) { Destroy(plane.colliderObject); } plane.colliderObject = new GameObject($"Collider_{plane.planeAxis}"); plane.colliderObject.transform.SetParent(transform); // Match canvas position plane.colliderObject.transform.position = plane.canvasGrid.transform.position; // Set rotation based on plane axis (not canvas rotation) plane.colliderObject.transform.rotation = GetColliderRotation(plane.planeAxis); // Add box collider BoxCollider boxCollider = plane.colliderObject.AddComponent(); // Calculate collider size based on canvas size and scale float width = rectTransform.rect.width * canvas.transform.localScale.x; float height = rectTransform.rect.height * canvas.transform.localScale.y; // All colliders use the same size format (width, thickness, height) // Rotation handles the orientation boxCollider.size = new Vector3(width, colliderThickness, height); // Set layer int layer = LayerMask.NameToLayer(colliderLayer); if (layer != -1) { plane.colliderObject.layer = layer; } else { Debug.LogWarning($"Layer '{colliderLayer}' not found. Using default layer."); } Debug.Log($"Generated collider for {plane.planeAxis}: Size = {new Vector3(width, colliderThickness, height)}, Rotation = {GetColliderRotation(plane.planeAxis).eulerAngles}"); } } /// /// Get the rotation for the collider based on plane axis /// private Quaternion GetColliderRotation(PlaneAxis axis) { switch (axis) { case PlaneAxis.XZ: // Horizontal plane (rotate to lay flat) return Quaternion.Euler(0, 0, 0); case PlaneAxis.XY: // Front plane (vertical, facing forward) return Quaternion.Euler(90, 0, 0); case PlaneAxis.YZ: // Side plane (vertical, facing sideways) return Quaternion.Euler(0, 0, 90); default: return Quaternion.identity; } } /// /// Regenerate colliders (useful if canvas size changes at runtime) /// public void RegenerateColliders() { GenerateColliders(); UpdatePlaneVisibility(); } /// /// Switch to a different plane axis /// public void SwitchToPlane(PlaneAxis plane) { currentPlane = plane; UpdatePlaneVisibility(); } /// /// Toggle platform visibility (canvas and collider) /// public void TogglePlatformVisibility(bool visible) { platformVisible = visible; UpdatePlaneVisibility(); } private void UpdatePlaneVisibility() { foreach (var plane in virtualPlanes) { bool isActive = (plane.planeAxis == currentPlane) && platformVisible; if (plane.canvasGrid != null) { plane.canvasGrid.SetActive(isActive); } if (plane.colliderObject != null) { plane.colliderObject.SetActive(isActive); } } } /// /// Toggle only the canvas grid visibility, keep colliders active /// public void ToggleGridVisibility(bool visible) { foreach (var plane in virtualPlanes) { if (plane.planeAxis == currentPlane && plane.canvasGrid != null) { plane.canvasGrid.SetActive(visible && platformVisible); } } } /// /// Get the current active plane /// public PlaneAxis GetCurrentPlane() => currentPlane; /// /// Check if platform is currently visible /// public bool IsPlatformVisible() => platformVisible; /// /// Get the current active plane's collider object /// public GameObject GetCurrentCollider() { foreach (var plane in virtualPlanes) { if (plane.planeAxis == currentPlane) { return plane.colliderObject; } } return null; } /// /// Get the current active plane's canvas /// public GameObject GetCurrentCanvas() { foreach (var plane in virtualPlanes) { if (plane.planeAxis == currentPlane) { return plane.canvasGrid; } } return null; } /// /// Set the canvas size for a specific plane and regenerate its collider /// public void SetCanvasSize(PlaneAxis planeAxis, float width, float height) { foreach (var plane in virtualPlanes) { if (plane.planeAxis == planeAxis && plane.canvasGrid != null) { Canvas canvas = plane.canvasGrid.GetComponent(); RectTransform rectTransform = canvas.GetComponent(); if (rectTransform != null) { rectTransform.sizeDelta = new Vector2(width, height); // Regenerate only this plane's collider RegenerateSingleCollider(plane); } break; } } } /// /// Set the canvas size for the current active plane /// public void SetCurrentCanvasSize(float width, float height) { SetCanvasSize(currentPlane, width, height); } /// /// Regenerate collider for a single plane /// private void RegenerateSingleCollider(VirtualPlane plane) { if (plane.canvasGrid == null) return; Canvas canvas = plane.canvasGrid.GetComponent(); if (canvas == null) return; RectTransform rectTransform = canvas.GetComponent(); if (rectTransform == null) return; // Destroy old collider if exists if (plane.colliderObject != null) { Destroy(plane.colliderObject); } // Create new collider plane.colliderObject = new GameObject($"Collider_{plane.planeAxis}"); plane.colliderObject.transform.SetParent(transform); // Match canvas position plane.colliderObject.transform.position = plane.canvasGrid.transform.position; // Set rotation based on plane axis (not canvas rotation) plane.colliderObject.transform.rotation = GetColliderRotation(plane.planeAxis); BoxCollider boxCollider = plane.colliderObject.AddComponent(); float width = rectTransform.rect.width * canvas.transform.localScale.x; float height = rectTransform.rect.height * canvas.transform.localScale.y; // All colliders use the same size format (width, thickness, height) // Rotation handles the orientation boxCollider.size = new Vector3(width, colliderThickness, height); int layer = LayerMask.NameToLayer(colliderLayer); if (layer != -1) { plane.colliderObject.layer = layer; } // Update visibility based on current state plane.colliderObject.SetActive((plane.planeAxis == currentPlane) && platformVisible); Debug.Log($"Regenerated collider for {plane.planeAxis}: Size = {new Vector3(width, colliderThickness, height)}, Rotation = {GetColliderRotation(plane.planeAxis).eulerAngles}"); } /// /// Get the current canvas size /// public Vector2 GetCurrentCanvasSize() { GameObject canvas = GetCurrentCanvas(); if (canvas != null) { RectTransform rectTransform = canvas.GetComponent(); if (rectTransform != null) { return rectTransform.sizeDelta; } } return Vector2.zero; } }