#c# #unity3d
Вопрос:
как я могу найти объем пересечения двух кубов, выровненных по оси? Кубики могут иметь разные размеры и положения. (Я добавляю картинку, чтобы показать простой пример двух кубов)
После глубоких исследований я совершенно уверен, что в Unity не существует конкретной функции, которую можно было бы использовать для этой цели, единственный способ решить эту проблему-математическая логика. Например, моей первой идеей было:
- Чтобы найти 8 вершин куба»пересечение» (B на изображении).
- Попробуйте построить новый куб с этой вершиной.
- Найдите размер и объем куба «пересечение».
Единство позволяет найти:
- центр каждого первичного куба (A на изображении) с «Границами.центр»;
- размеры каждого первичного куба (A на изображении) от центра куба (половина размера границ) с помощью «Bounds.extents».
доступная документация here:https://docs.unity3d.com/ScriptReference/Bounds.html
У вас есть какая-нибудь помощь или предложение?
Комментарии:
1. Не лучше ли было бы задать этот вопрос на сайте обмена математическими стеками? Кроме того, вы, вероятно, захотите удалить любую болтовню из заголовка и текста вашего вопроса, поскольку это не помогает вопросу и на самом деле ему не принадлежит.
2. @HovercraftFullOfEels Готово! У вас есть какие-нибудь предложения по обмену математическими стеками? Я ничего об этом не знаю
3. math.stackexchange.com например?
4. @derpirscher хорошо, спасибо, я попробую и там
5. Вам нужно также для повернутых кубов? или только вращение всегда будет кватернионом.идентичность?
Ответ №1:
Если, как вы говорите, кубы всегда будут выровнены по оси с миром единства (=> границы = объем коллайдера/визуализатора), вы, вероятно, могли бы просто сделать что-то вроде
- возьмите
Bounds
обе коробки - возьмите
min
иmax
из них - => проверьте, насколько они перекрываются на каждой оси по отдельности, используя максимум минут и минимум максимумов
То, что вы получаете при этом, — это новая минимальная и максимальная точка перекрывающегося поля.
Этой информации достаточно
- для вычисления объема путем умножения отдельных компонентов вектора между этими минимальными и максимальными значениями.
- чтобы получить все вершины, получив все перестановки между min и max для каждой оси.
что-то вроде
public class OverlapArea
{
public readonly Vector3 min;
public readonly Vector3 max;
public readonly float volume;
public Vector3 frontBottomLeft => min;
public readonly Vector3 frontBottomRight;
public readonly Vector3 frontTopLeft;
public readonly Vector3 frontTopRight;
public readonly Vector3 backBottomLeft;
public readonly Vector3 backBottomRight;
public readonly Vector3 backTopLeft;
public Vector3 backTopRight => max;
public readonly Bounds bounds;
public OverlapArea(Bounds a, Bounds b)
{
// The min and max points
var minA = a.min;
var maxA = a.max;
var minB = b.min;
var maxB = b.max;
min.x = Mathf.Max(minA.x, minB.x);
min.y = Mathf.Max(minA.y, minB.y);
min.z = Mathf.Max(minA.z, minB.z);
max.x = Mathf.Min(maxA.x, maxB.x);
max.y = Mathf.Min(maxA.y, maxB.y);
max.z = Mathf.Min(maxA.z, maxB.z);
frontBottomRight = new Vector3(max.x, min.y, min.z);
frontTopLeft = new Vector3(min.x, max.y, min.z);
frontTopRight = new Vector3(max.x, max.y, min.z);
backBottomLeft = new Vector3(min.x, min.y, max.z);
backBottomRight = new Vector3(max.x, min.y, max.z);
backTopLeft = new Vector3(min.x, max.y, max.z);
// The diagonal of this overlap box itself
var diagonal = max - min;
volume = diagonal.x * diagonal.y * diagonal.z;
bounds.SetMinMax(min, max);
}
public static bool GetOverlapArea(Bounds a, Bounds b, out OverlapArea overlapArea)
{
overlapArea = defau<
// If they are not intersecting we can stop right away ;)
if (!a.Intersects(b)) return false;
overlapArea = new OverlapArea(a, b);
return true;
}
}
And so in order to get the overlap information you would do e.g.
// I intentionally used the Bounds as parameters for the method because you can
// either use the Renderer bounds
var boundsA = cubeA.GetComponent<Renderer>().bounds;
// or use Collider bounds in your case they should be equal
var boundsB = cubeB.GetComponent<Collider>().bounds;
if(GetOverlapArea(boundsA, boundsB, out var overlap))
{
// Now in overlap you have all the information you wanted
}
Теперь, чтобы на самом деле получить сетку перекрытия ( если вы собираетесь именно туда), у вас есть два варианта
Либо используйте заданные точки ребер и фактически создайте сетку самостоятельно (обратите внимание, что вершины находятся в мировом пространстве, поэтому объект не должен масштабироваться сам по себе или на каком-либо родителе).
...
var mesh = new Mesh
{
vertices = new[]
{
overlapArea.frontBottomLeft,
overlapArea.frontBottomRight,
overlapArea.frontTopLeft,
overlapArea.frontTopRight,
overlapArea.backBottomLeft,
overlapArea.backBottomRight,
overlapArea.backTopLeft,
overlapArea.backTopRight
},
triangles = new[]
{
// Front
0, 2, 1,
1, 2, 3,
// Back
5, 7, 4,
4, 7, 6,
// Left
4, 6, 2,
4, 2, 0,
// Right
1, 7, 5,
1, 3, 7,
// Top
2, 7, 3,
2, 6, 7,
// Bottom
0, 4, 1,
1, 4, 5
}
}
в качестве небольшой демонстрации
public class Example : MonoBehaviour
{
public Renderer CubeA;
public Renderer CubeB;
public Material overlapMaterial;
private MeshFilter overlap;
private readonly Vector3[] overlapVertices = new Vector3[8];
public void Awake()
{
overlap = new GameObject("Overlap", typeof(MeshRenderer)).AddComponent<MeshFilter>();
var overlapMesh = new Mesh
{
vertices = overlapVertices,
triangles = new[]
{
// Front
0, 2, 1,
1, 2, 3,
// Back
5, 7, 4,
4, 7, 6,
// Left
4, 6, 2,
4, 2, 0,
// Right
1, 7, 5,
1, 3, 7,
// Top
2, 7, 3,
2, 6, 7,
// Bottom
0, 4, 1,
1, 4, 5
}
};
overlap.mesh = overlapMesh;
overlap.GetComponent<Renderer>().material = overlapMaterial;
}
public void Update()
{
if (OverlapArea.GetOverlapArea(CubeA.bounds, CubeB.bounds, out var overlapArea))
{
overlap.gameObject.SetActive(true);
overlap.mesh.vertices = new[]
{
overlapArea.frontBottomLeft,
overlapArea.frontBottomRight,
overlapArea.frontTopLeft,
overlapArea.frontTopRight,
overlapArea.backBottomLeft,
overlapArea.backBottomRight,
overlapArea.backTopLeft,
overlapArea.backTopRight
};
overlap.mesh.RecalculateBounds();
}
else
{
overlap.gameObject.SetActive(false);
}
}
}
или вы можете вместо этого использовать уже существующий примитивный куб (куб единства по умолчанию), просто установить его в правильные координаты и масштабировать следующим образом
overlapVisualizer.transform.position = overlap.bounds.center;
// note we set the local scale so there should be no parent scaling
overlapVisualizer.transform.localScale = overlap.bounds.size;
снова небольшая демонстрация этого
public class Example : MonoBehaviour
{
public Renderer CubeA;
public Renderer CubeB;
public Transform overlap;
public void Update()
{
if (OverlapArea.GetOverlapArea(CubeA.bounds, CubeB.bounds, out var overlapArea))
{
overlap.gameObject.SetActive(true);
overlap.position = overlapArea.bounds.center;
overlap.localScale = overlapArea.bounds.size;
}
else
{
overlap.gameObject.SetActive(false);
}
}
}