350 lines
14 KiB
C#
350 lines
14 KiB
C#
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
|
||
public class FurnitureController : MonoBehaviour
|
||
{
|
||
public enum EditTool { Move, Rotate }
|
||
|
||
[Header("Настройки")]
|
||
public GameObject currentPrefab;
|
||
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;
|
||
|
||
private float _lastClickTime = 0f; // Хранит время последнего клика
|
||
|
||
private void Start()
|
||
{
|
||
_cam = Camera.main;
|
||
if (cameraController == null && _cam != null)
|
||
cameraController = _cam.GetComponent("CameraController") as MonoBehaviour;
|
||
|
||
currentPrefab = null;
|
||
editPanel.SetActive(false);
|
||
if (rotate90Button != null) rotate90Button.SetActive(false);
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
if (_activeFurniture != null)
|
||
{
|
||
HandleEditing();
|
||
}
|
||
else if (currentPrefab != 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;
|
||
|
||
// Настенные предметы крутим вокруг своей оси Z (по циферблату), а напольные - вокруг мировой Y
|
||
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);
|
||
|
||
_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);
|
||
|
||
// Определяем, куда пускать луч при установке в зависимости от типа предмета
|
||
FurnitureObject prefabScript = currentPrefab.GetComponent<FurnitureObject>();
|
||
LayerMask targetLayer = prefabScript.isWallObject ? wallLayer : floorLayer;
|
||
|
||
if (Physics.Raycast(ray, out RaycastHit hit, 100f, targetLayer))
|
||
{
|
||
_activeFurniture = Instantiate(currentPrefab, hit.point, Quaternion.identity);
|
||
|
||
// Если это стена - сразу поворачиваем спиной к ней
|
||
if (prefabScript.isWallObject)
|
||
{
|
||
_activeFurniture.transform.rotation = Quaternion.LookRotation(hit.normal);
|
||
}
|
||
|
||
_activeFurnitureScript = _activeFurniture.GetComponent<FurnitureObject>();
|
||
_activeFurnitureScript.SetEditMode(true);
|
||
|
||
_currentTool = EditTool.Move;
|
||
if (rotate90Button != null) rotate90Button.SetActive(false);
|
||
|
||
currentPrefab = null;
|
||
_isInteracting = false;
|
||
LockCamera(false);
|
||
UpdateUIPosition();
|
||
}
|
||
}
|
||
|
||
public void Rotate90Degrees()
|
||
{
|
||
if (_activeFurniture != null)
|
||
{
|
||
if (_activeFurnitureScript.isWallObject)
|
||
{
|
||
// Настенные объекты поворачиваем по их локальной оси Z (как часы)
|
||
_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);
|
||
LockCamera(false);
|
||
}
|
||
}
|
||
|
||
public void CancelPlacement()
|
||
{
|
||
if (_activeFurniture != null)
|
||
{
|
||
Destroy(_activeFurniture);
|
||
_activeFurniture = null;
|
||
_activeFurnitureScript = null;
|
||
editPanel.SetActive(false);
|
||
LockCamera(false);
|
||
}
|
||
}
|
||
|
||
public void SelectFurnitureAndCloseShop(GameObject prefabToSelect)
|
||
{
|
||
currentPrefab = prefabToSelect;
|
||
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;
|
||
}
|
||
} |