Files
JuegoSim/Assets/_Project/Scripts/Code/Validators/PlacementValidator.cs
T
2026-07-21 08:56:10 +03:00

129 lines
5.4 KiB
C#

using UnityEngine;
using System.Collections.Generic;
public class PlacementValidator : MonoBehaviour, IValidator
{
[SerializeField] private LayerMask collisionLayerMask;
[SerializeField] private float toleranceSkin = 0.05f; // Shrinks the check box slightly
public void SetLayerMask(LayerMask cLayerMask) => collisionLayerMask = cLayerMask;
public bool IsValidPlacement(GameObject objToPlace)
{
Collider objCollider = objToPlace.GetComponent<Collider>();
if (objCollider == null) return true;
if (objCollider.isTrigger) return true;
if (objCollider.gameObject.layer == LayerMask.NameToLayer("Gizmos")) return true;
return CheckCollider(objCollider, objToPlace.transform.root);
}
private bool CheckCollider(Collider objCollider, Transform root)
{
Collider[] hits = new Collider[0];
if (objCollider is BoxCollider box)
{
Vector3 center = objCollider.transform.TransformPoint(box.center);
Vector3 lossyScale = objCollider.transform.lossyScale;
Vector3 scale = new Vector3(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z));
Vector3 size = Vector3.Scale(box.size, scale);
// Apply tolerance (shrink on both sides of each axis), but never shrink below 80% of original size
Vector3 overlapSize = new Vector3(
Mathf.Max(size.x - toleranceSkin * 2f, size.x * 0.8f),
Mathf.Max(size.y - toleranceSkin * 2f, size.y * 0.8f),
Mathf.Max(size.z - toleranceSkin * 2f, size.z * 0.8f)
);
hits = Physics.OverlapBox(center, overlapSize * 0.5f, objCollider.transform.rotation, collisionLayerMask);
}
else if (objCollider is SphereCollider sphere)
{
Vector3 center = objCollider.transform.TransformPoint(sphere.center);
Vector3 lossyScale = objCollider.transform.lossyScale;
float maxScale = Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Max(Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z)));
float radius = sphere.radius * maxScale;
float overlapRadius = Mathf.Max(0f, radius - toleranceSkin);
hits = Physics.OverlapSphere(center, overlapRadius, collisionLayerMask);
}
else if (objCollider is CapsuleCollider capsule)
{
Vector3 center = objCollider.transform.TransformPoint(capsule.center);
Vector3 lossyScale = objCollider.transform.lossyScale;
float scaleX = Mathf.Abs(lossyScale.x);
float scaleY = Mathf.Abs(lossyScale.y);
float scaleZ = Mathf.Abs(lossyScale.z);
float radiusScale = Mathf.Max(scaleX, scaleZ);
float heightScale = scaleY;
Vector3 dir = objCollider.transform.up;
if (capsule.direction == 0) // X-axis
{
radiusScale = Mathf.Max(scaleY, scaleZ);
heightScale = scaleX;
dir = objCollider.transform.right;
}
else if (capsule.direction == 2) // Z-axis
{
radiusScale = Mathf.Max(scaleX, scaleY);
heightScale = scaleZ;
dir = objCollider.transform.forward;
}
float radius = capsule.radius * radiusScale;
float height = capsule.height * heightScale;
float overlapRadius = Mathf.Max(0f, radius - toleranceSkin);
float overlapHeight = Mathf.Max(0f, height - (toleranceSkin * 2f));
float pointOffset = Mathf.Max(0f, (overlapHeight * 0.5f) - overlapRadius);
Vector3 point0 = center + dir * pointOffset;
Vector3 point1 = center - dir * pointOffset;
hits = Physics.OverlapCapsule(point0, point1, overlapRadius, collisionLayerMask);
}
else if (objCollider is MeshCollider mc && mc.sharedMesh != null)
{
Bounds localBounds = mc.sharedMesh.bounds;
Vector3 center = objCollider.transform.TransformPoint(localBounds.center);
Vector3 lossyScale = objCollider.transform.lossyScale;
Vector3 scale = new Vector3(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z));
Vector3 size = Vector3.Scale(localBounds.size, scale);
Vector3 overlapSize = new Vector3(
Mathf.Max(size.x - toleranceSkin * 2f, size.x * 0.8f),
Mathf.Max(size.y - toleranceSkin * 2f, size.y * 0.8f),
Mathf.Max(size.z - toleranceSkin * 2f, size.z * 0.8f)
);
hits = Physics.OverlapBox(center, overlapSize * 0.5f, objCollider.transform.rotation, collisionLayerMask);
}
else
{
// Fallback: AABB check
Vector3 center = objCollider.bounds.center;
Vector3 size = objCollider.bounds.size;
Vector3 overlapSize = new Vector3(
Mathf.Max(size.x - toleranceSkin * 2f, size.x * 0.8f),
Mathf.Max(size.y - toleranceSkin * 2f, size.y * 0.8f),
Mathf.Max(size.z - toleranceSkin * 2f, size.z * 0.8f)
);
hits = Physics.OverlapBox(center, overlapSize * 0.5f, Quaternion.identity, collisionLayerMask);
}
foreach (Collider hit in hits)
{
if (hit.transform.root == root) continue;
if (hit.isTrigger) continue;
return false;
}
return true;
}
}