Initial commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IValidator
|
||||
{
|
||||
bool IsValidPlacement(GameObject objToPlace);
|
||||
|
||||
void SetLayerMask(LayerMask cLayerMask);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba9fdb21db901e7448c9bcf92b1bf1e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,138 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class MultipleColliderValidator : 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)
|
||||
{
|
||||
// Get all colliders of the object we are moving
|
||||
Collider[] objColliders = objToPlace.GetComponentsInChildren<Collider>();
|
||||
if (objColliders.Length == 0) return true;
|
||||
|
||||
foreach (Collider objCollider in objColliders)
|
||||
{
|
||||
if (objCollider.isTrigger) continue;
|
||||
if (objCollider.gameObject.layer == LayerMask.NameToLayer("Gizmos")) continue;
|
||||
|
||||
if (!CheckCollider(objCollider, objToPlace.transform.root))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // Valid placement if all colliders are clear
|
||||
}
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0f0c40b7649c4b45a67e5876bbd3d9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,129 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8245c6e3d94e025439b7ccced75276db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "Scripts.Validators"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c16923da89c230b46a6c01482752a2ba
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user