378 lines
15 KiB
C#
378 lines
15 KiB
C#
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<BoxCollider>();
|
||
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<FurnitureObject>();
|
||
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<RectTransform>();
|
||
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<FurnitureObject>();
|
||
|
||
// --- МАГИЯ АРХИТЕКТУРЫ ---
|
||
// Инъекция карточки товара в объект на сцене!
|
||
_activeFurnitureScript.runtimeItemData = currentItemData;
|
||
|
||
FurnitureVisuals visuals = _activeFurniture.GetComponent<FurnitureVisuals>();
|
||
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;
|
||
}
|
||
} |