using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public class FurnitureController : MonoBehaviour { public enum EditTool { Move, Rotate } [Header("Настройки")] public FurnitureItemSO currentItemData; // Заменили GameObject на SO public LayerMask floorLayer; public LayerMask wallLayer; public LayerMask furnitureLayer; [SerializeField] private GameObject shopPanel; [Header("Режим редактирования (UI)")] [SerializeField] private GameObject editPanel; [SerializeField] private GameObject rotate90Button; public float rotationSpeed = 5f; [Header("Настройки двойного клика")] public float doubleClickThreshold = 0.3f; [Header("Связь с камерой")] public MonoBehaviour cameraController; private GameObject _activeFurniture; private FurnitureObject _activeFurnitureScript; private EditTool _currentTool = EditTool.Move; private Camera _cam; private bool _isInteracting = false; private Vector3 _lastInputPos; private Vector3 _dragOffset; [Header("Связь с UI Цветов")] public FurniturePaletteUI paletteUI; // Ссылка на новый скрипт private float _lastClickTime = 0f; private void Start() { _cam = Camera.main; if (cameraController == null && _cam != null) cameraController = _cam.GetComponent("CameraController") as MonoBehaviour; currentItemData = null; editPanel.SetActive(false); if (rotate90Button != null) rotate90Button.SetActive(false); } void Update() { if (_activeFurniture != null) { HandleEditing(); } else if (currentItemData != null) { if (Input.GetMouseButtonDown(0) && !shopPanel.activeSelf && !IsPointerOverUI()) TryPlaceFurniture(); } else { if (Input.GetMouseButtonDown(0) && !shopPanel.activeSelf && !IsPointerOverUI()) { float timeSinceLastClick = Time.time - _lastClickTime; if (timeSinceLastClick <= doubleClickThreshold) { TrySelectPlacedFurniture(); } _lastClickTime = Time.time; } } } private void HandleEditing() { if (Input.GetMouseButtonDown(0) && !IsPointerOverUI()) { _lastInputPos = Input.mousePosition; if (_currentTool == EditTool.Move) { Ray ray = _cam.ScreenPointToRay(Input.mousePosition); RaycastHit[] hits = Physics.SphereCastAll(ray, 0.5f, 100f); bool foundFurniture = false; foreach (var hit in hits) { if (hit.transform.IsChildOf(_activeFurniture.transform) || hit.transform == _activeFurniture.transform) { foundFurniture = true; break; } } if (foundFurniture) { if (!_activeFurnitureScript.isWallObject && Physics.Raycast(ray, out RaycastHit floorHit, 100f, floorLayer)) { _dragOffset = _activeFurniture.transform.position - floorHit.point; _dragOffset.y = 0; } else _dragOffset = Vector3.zero; StartInteraction(); } } else if (_currentTool == EditTool.Rotate) { StartInteraction(); } } if (Input.GetMouseButton(0) && _isInteracting) { Ray ray = _cam.ScreenPointToRay(Input.mousePosition); if (_currentTool == EditTool.Move) { if (_activeFurnitureScript.isWallObject) { if (Physics.Raycast(ray, out RaycastHit hit, 100f, wallLayer)) { _activeFurniture.transform.position = hit.point; _activeFurniture.transform.rotation = Quaternion.LookRotation(hit.normal); } } else { if (Physics.Raycast(ray, out RaycastHit hit, 100f, floorLayer)) { Vector3 currentPos = _activeFurniture.transform.position; Vector3 targetPos = hit.point + _dragOffset; targetPos.y = currentPos.y; Vector3 moveDir = targetPos - currentPos; float moveDist = moveDir.magnitude; if (moveDist > 0.001f) { BoxCollider box = _activeFurniture.GetComponent(); if (box != null) { Vector3 center = _activeFurniture.transform.TransformPoint(box.center); Vector3 halfExtents = Vector3.Scale(box.size, _activeFurniture.transform.lossyScale) * 0.49f; if (Physics.BoxCast(center, halfExtents, moveDir.normalized, out RaycastHit wallHit, _activeFurniture.transform.rotation, moveDist, wallLayer)) { float safeDist = Mathf.Max(0f, wallHit.distance - 0.01f); Vector3 remainingMove = moveDir - (moveDir.normalized * safeDist); Vector3 slideMove = Vector3.ProjectOnPlane(remainingMove, wallHit.normal); if (slideMove.magnitude > 0.001f) { Vector3 newCenter = center + moveDir.normalized * safeDist; if (Physics.BoxCast(newCenter, halfExtents, slideMove.normalized, out RaycastHit slideHit, _activeFurniture.transform.rotation, slideMove.magnitude, wallLayer)) { slideMove = slideMove.normalized * Mathf.Max(0f, slideHit.distance - 0.01f); } } targetPos = currentPos + (moveDir.normalized * safeDist) + slideMove; } } } targetPos.y = currentPos.y; _activeFurniture.transform.position = targetPos; } } } else if (_currentTool == EditTool.Rotate) { float deltaX = Input.mousePosition.x - _lastInputPos.x; if (_activeFurnitureScript.isWallObject) _activeFurniture.transform.Rotate(Vector3.forward, -deltaX * rotationSpeed * 0.2f, Space.Self); else _activeFurniture.transform.Rotate(Vector3.up, -deltaX * rotationSpeed * 0.2f, Space.World); } _lastInputPos = Input.mousePosition; } if (Input.GetMouseButtonUp(0) && _isInteracting) EndInteraction(); if (!_isInteracting) UpdateUIPosition(); } private void TrySelectPlacedFurniture() { Ray ray = _cam.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit, 1000f, furnitureLayer, QueryTriggerInteraction.Collide)) { FurnitureObject furnScript = hit.collider.GetComponentInParent(); if (furnScript != null) { _activeFurniture = furnScript.gameObject; _activeFurnitureScript = furnScript; _activeFurnitureScript.SetEditMode(true); _currentTool = EditTool.Move; if (rotate90Button != null) rotate90Button.SetActive(false); // Показываем панель цветов при выборе предмета if (paletteUI != null) paletteUI.ShowColors(_activeFurnitureScript); _isInteracting = false; LockCamera(false); UpdateUIPosition(); } } } private void StartInteraction() { _isInteracting = true; if (editPanel.activeSelf) editPanel.SetActive(false); if (_currentTool == EditTool.Move) LockCamera(true); } private void EndInteraction() { _isInteracting = false; UpdateUIPosition(); editPanel.SetActive(true); if (_currentTool == EditTool.Move) LockCamera(false); } private void UpdateUIPosition() { Vector3 screenPos = _cam.WorldToScreenPoint(_activeFurniture.transform.position); if (screenPos.z > 0) { if (!editPanel.activeSelf) editPanel.SetActive(true); float yOffset = Screen.height * 0.15f; float targetX = screenPos.x; float targetY = screenPos.y + yOffset; RectTransform rect = editPanel.GetComponent(); if (rect != null) { float paddingX = (rect.rect.width / 2f) * editPanel.transform.lossyScale.x; float paddingY = (rect.rect.height / 2f) * editPanel.transform.lossyScale.y; targetX = Mathf.Clamp(targetX, paddingX, Screen.width - paddingX); targetY = Mathf.Clamp(targetY, paddingY, Screen.height - paddingY); } editPanel.transform.position = new Vector3(targetX, targetY, editPanel.transform.position.z); } else if (editPanel.activeSelf) editPanel.SetActive(false); } void TryPlaceFurniture() { Ray ray = _cam.ScreenPointToRay(Input.mousePosition); // Берем слой для первичной установки из карточки товара SO! LayerMask targetLayer = currentItemData.isWallObject ? wallLayer : floorLayer; if (Physics.Raycast(ray, out RaycastHit hit, 100f, targetLayer)) { // Блокируем новые клики, пока грузится ассет _isInteracting = true; Quaternion spawnRot = Quaternion.identity; if (currentItemData.isWallObject) spawnRot = Quaternion.LookRotation(hit.normal); // Запускаем асинхронную загрузку из Addressables currentItemData.prefabRef.InstantiateAsync(hit.point, spawnRot).Completed += handle => { if (handle.Status == AsyncOperationStatus.Succeeded) { _activeFurniture = handle.Result; _activeFurnitureScript = _activeFurniture.GetComponent(); // --- МАГИЯ АРХИТЕКТУРЫ --- // Инъекция карточки товара в объект на сцене! _activeFurnitureScript.runtimeItemData = currentItemData; FurnitureVisuals visuals = _activeFurniture.GetComponent(); if (visuals != null && currentItemData.availablePresets != null && currentItemData.availablePresets.Count > 0) { visuals.ApplyPreset(currentItemData.availablePresets[0]); } // Показываем панель цветов для только что заспавненного объекта if (paletteUI != null) paletteUI.ShowColors(_activeFurnitureScript); // ------------------------- _activeFurnitureScript.SetEditMode(true); _currentTool = EditTool.Move; if (rotate90Button != null) rotate90Button.SetActive(false); currentItemData = null; // Сбрасываем карточку из буфера _isInteracting = false; LockCamera(false); UpdateUIPosition(); } else { Debug.LogError($"Не удалось загрузить {currentItemData.itemName}. Проверьте галочку Addressables на префабе!"); _isInteracting = false; } }; } } public void Rotate90Degrees() { if (_activeFurniture != null) { if (_activeFurnitureScript.isWallObject) { _activeFurniture.transform.Rotate(0, 0, 90f, Space.Self); } else { float currentY = _activeFurniture.transform.eulerAngles.y; float snappedY = Mathf.Round(currentY / 90f) * 90f; _activeFurniture.transform.rotation = Quaternion.Euler(0, snappedY + 90f, 0); } } } public void SelectMoveTool() { _currentTool = EditTool.Move; if (rotate90Button != null) rotate90Button.SetActive(false); LockCamera(false); } public void SelectRotateTool() { _currentTool = EditTool.Rotate; if (rotate90Button != null) rotate90Button.SetActive(true); LockCamera(true); } public void ConfirmPlacement() { if (_activeFurnitureScript != null && _activeFurnitureScript.IsPlacementValid) { _activeFurnitureScript.SetEditMode(false); _activeFurniture = null; _activeFurnitureScript = null; editPanel.SetActive(false); if (paletteUI != null) paletteUI.HideColors(); LockCamera(false); } } public void CancelPlacement() { if (_activeFurniture != null) { // ПРАВИЛЬНОЕ удаление объекта, заспавненного через Addressables Addressables.ReleaseInstance(_activeFurniture); _activeFurniture = null; _activeFurnitureScript = null; editPanel.SetActive(false); if (paletteUI != null) paletteUI.HideColors(); LockCamera(false); } } // Эта функция вызывается из ShopManager public void SelectFurnitureAndCloseShop(FurnitureItemSO itemToSelect) { currentItemData = itemToSelect; shopPanel.SetActive(false); } private void LockCamera(bool isLocked) { if (cameraController != null) cameraController.enabled = !isLocked; } private bool IsPointerOverUI() { if (EventSystem.current != null) { if (EventSystem.current.IsPointerOverGameObject()) return true; if (Input.touchCount > 0 && EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId)) return true; } return false; } }