При выравнивании по оси X
При выравнивании по оси Y
У меня есть сборный круг. Этот круг представляет собой средство рендеринга линий в форме круга. Вот что делает мой скрипт, прикрепленный к этим префабам:
Найти ближайшие круги
Сортировка соседних кругов по углу: используйте Mathf.Atan2(), чтобы вычислить угол между каждым ближайшим кругом(ами) и текущий круг
Отбраковывать «сжатые» круги: для каждого круга в отсортированном списке ближайших флагов мы проверяем, есть ли он втиснут между двумя другими путем сравнения расстояний между кругами.
Найти пересечения между кругами: вычислить пересечения между каждый круг с текущим кругом
Сгладить кривую между точками пересечения: изменяет круг путем линейной интерполяции между ними. точек пересечения, создавая между ними прямой край.
Повторите процесс для многократного пересечения
Как вы можете видеть на изображениях, я сглаживаю изнаночную сторону только тогда, когда они имеют общую ось Z. И это не то и другое, всегда тот, у которого меньшее значение X.
Небольшой фрагмент связанного кода:
using System.Collections.Generic;
using UnityEngine;
public class FlagController : MonoBehaviour
{
public float AOI = 25f; // Max Area of Influence (radius)
public int segments = 50; // Number of segments for the circle
public LineRenderer lineRenderer;
// Unique identifier for each flag
public string flagID;
private static int flagCounter = 0; // Static counter to generate unique IDs
private void Start()
{
// Assign a unique ID based on the flagCounter
flagID = "Flag_" + flagCounter++;
// Setup the LineRenderer component
lineRenderer = GetComponent();
if (lineRenderer == null)
{
lineRenderer = gameObject.AddComponent();
}
// Set up the basic circle
lineRenderer.positionCount = segments + 1;
lineRenderer.useWorldSpace = false;
lineRenderer.loop = true;
lineRenderer.widthMultiplier = 0.2f;
// Generate the initial circular boundary
GenerateAOIBoundary();
// Find nearby flags automatically
List nearbyFlags = FindNearbyFlags();
// Sort by angle
SortByAngle(nearbyFlags);
// Cull squeezed flags
CullSqueezedFlags(ref nearbyFlags);
// Now calculate the Voronoi AOI based on nearby flags
if (nearbyFlags.Count > 0)
{
CalculateVoronoiAOI(nearbyFlags);
}
}
// Generates the initial circular boundary for the flag
public void GenerateAOIBoundary()
{
float angleStep = 360f / segments;
for (int i = 0; i
{
float angleA = Mathf.Atan2(a.transform.position.z - transform.position.z, a.transform.position.x - transform.position.x);
float angleB = Mathf.Atan2(b.transform.position.z - transform.position.z, b.transform.position.x - transform.position.x);
return angleA.CompareTo(angleB);
});
}
// Cull flags that are squeezed out by others
private void CullSqueezedFlags(ref List nearbyFlags)
{
bool finishedCulling = false;
while (!finishedCulling && nearbyFlags.Count >= 3)
{
finishedCulling = true;
for (int i = 0; i < nearbyFlags.Count; i++)
{
FlagController flagA = GetLooped(nearbyFlags, i - 1);
FlagController flagB = GetLooped(nearbyFlags, i);
FlagController flagC = GetLooped(nearbyFlags, i + 1);
if (Vector3.Distance(transform.position, flagB.transform.position) > Vector3.Distance(flagA.transform.position, flagC.transform.position) &&
Vector3.Distance(flagA.transform.position, flagC.transform.position) > Vector3.Distance(flagA.transform.position, flagB.transform.position) &&
Vector3.Distance(flagA.transform.position, flagC.transform.position) > Vector3.Distance(flagB.transform.position, flagC.transform.position))
{
nearbyFlags.RemoveAt(i);
finishedCulling = false;
break;
}
}
}
}
// Calculate the Voronoi area of influence, considering nearby flags
public void CalculateVoronoiAOI(List nearbyFlags)
{
List intersections = new List();
foreach (FlagController nearbyFlag in nearbyFlags)
{
Vector3 intersection1, intersection2;
if (FindIntersections(this, nearbyFlag, out intersection1, out intersection2))
{
intersections.Add(intersection1);
intersections.Add(intersection2);
}
}
if (intersections.Count == 0)
{
GenerateAOIBoundary(); // No intersections, keep the full circle
}
else
{
FlattenCurveForIntersections(intersections);
}
}
// Find intersections between two flags' AOIs
private bool FindIntersections(FlagController flag1, FlagController flag2, out Vector3 intersection1, out Vector3 intersection2)
{
intersection1 = Vector3.zero;
intersection2 = Vector3.zero;
float d = Vector3.Distance(flag1.transform.position, flag2.transform.position);
if (d > flag1.AOI + flag2.AOI || d < Mathf.Abs(flag1.AOI - flag2.AOI))
{
return false; // No valid intersection
}
float a = (flag1.AOI * flag1.AOI - flag2.AOI * flag2.AOI + d * d) / (2 * d);
float h = Mathf.Sqrt(flag1.AOI * flag1.AOI - a * a);
Vector3 direction = (flag2.transform.position - flag1.transform.position).normalized;
Vector3 p2 = flag1.transform.position + a * direction;
intersection1 = new Vector3(p2.x + h * direction.z, flag1.transform.position.y, p2.z - h * direction.x);
intersection2 = new Vector3(p2.x - h * direction.z, flag1.transform.position.y, p2.z + h * direction.x);
return true;
}
// Flatten the curve between two intersection points
private void FlattenCurveForIntersections(List intersections)
{
intersections.Sort((a, b) => GetAngleFromCenter(a).CompareTo(GetAngleFromCenter(b)));
for (int i = 0; i < intersections.Count; i += 2)
{
Vector3 intersection1 = intersections[i];
Vector3 intersection2 = intersections[i + 1];
FlattenBetweenIntersections(intersection1, intersection2);
}
}
// Helper function to get the angle from the center of the current flag
private float GetAngleFromCenter(Vector3 point)
{
Vector3 relativePoint = point - transform.position;
float angle = Mathf.Atan2(relativePoint.z, relativePoint.x) * Mathf.Rad2Deg;
if (angle < 0)
{
angle += 360f;
}
return angle;
}
// Helper function to flatten the curve between two intersection points
private void FlattenBetweenIntersections(Vector3 intersection1, Vector3 intersection2)
{
float angle1 = GetAngleFromCenter(intersection1);
float angle2 = GetAngleFromCenter(intersection2);
if (angle1 > angle2)
{
float temp = angle1;
angle1 = angle2;
angle2 = temp;
}
FlattenCurveBetweenAngles(angle1, angle2);
}
// Main function to flatten a curve between two angles
private void FlattenCurveBetweenAngles(float angle1, float angle2)
{
Debug.Log("{flagID} {angle1} {angle2}");
float angleStep = 360f / segments;
int startIndex = Mathf.RoundToInt(angle1 / angleStep);
int endIndex = Mathf.RoundToInt(angle2 / angleStep);
Vector3 startPos = lineRenderer.GetPosition(startIndex);
Vector3 endPos = lineRenderer.GetPosition(endIndex);
for (int i = startIndex + 1; i < endIndex; i++)
{
float t = (float)(i - startIndex) / (endIndex - startIndex);
Vector3 newPos = Vector3.Lerp(startPos, endPos, t);
lineRenderer.SetPosition(i, newPos);
}
}
// Helper function to get a looped element from the list
private FlagController GetLooped(List list, int index)
{
return list[(index + list.Count) % list.Count];
}
}
Моя последняя попытка взяла центральную точку круга, а также получила позиции, в которые, по его мнению, он собирался переместить круг (рендеринг линий), и проверила, находится ли центральная точка в если нет, то мы поменяли местами отправленные углы, но это ничего не изменило.
При выравнивании по оси X При выравнивании по оси Y У меня есть сборный круг. Этот круг представляет собой средство рендеринга линий в форме круга. Вот что делает мой скрипт, прикрепленный к этим префабам: [list] [*][b]Найти ближайшие круги[/b]
[*][b]Сортировка соседних кругов по углу:[/b] используйте Mathf.Atan2(), чтобы вычислить угол между каждым ближайшим кругом(ами) и текущий круг
[*][b]Отбраковывать «сжатые» круги[/b]: для каждого круга в отсортированном списке ближайших флагов мы проверяем, есть ли он втиснут между двумя другими путем сравнения расстояний между кругами.
[*][b]Найти пересечения между кругами[/b]: вычислить пересечения между каждый круг с текущим кругом
[*][b]Сгладить кривую между точками пересечения[/b]: изменяет круг путем линейной интерполяции между ними. точек пересечения, создавая между ними прямой край.
[*][b]Повторите процесс для многократного пересечения[/b] [/list] Как вы можете видеть на изображениях, я сглаживаю изнаночную сторону только тогда, когда они имеют общую ось Z. И это не то и другое, всегда тот, у которого меньшее значение X. Небольшой фрагмент связанного кода: [code]private void FlattenBetweenIntersections(Vector3 intersection1, Vector3 intersection2) { float angle1 = GetAngleFromCenter(intersection1); float angle2 = GetAngleFromCenter(intersection2);
for (int i = startIndex + 1; i < endIndex; i++) { float t = (float)(i - startIndex) / (endIndex - startIndex); Vector3 newPos = Vector3.Lerp(startPos, endPos, t); lineRenderer.SetPosition(i, newPos); } } [/code] Полный код ниже: [code]using System.Collections.Generic; using UnityEngine;
public class FlagController : MonoBehaviour { public float AOI = 25f; // Max Area of Influence (radius) public int segments = 50; // Number of segments for the circle public LineRenderer lineRenderer;
// Unique identifier for each flag public string flagID;
private static int flagCounter = 0; // Static counter to generate unique IDs
private void Start() { // Assign a unique ID based on the flagCounter flagID = "Flag_" + flagCounter++;
// Setup the LineRenderer component lineRenderer = GetComponent(); if (lineRenderer == null) { lineRenderer = gameObject.AddComponent(); }
// Set up the basic circle lineRenderer.positionCount = segments + 1; lineRenderer.useWorldSpace = false; lineRenderer.loop = true; lineRenderer.widthMultiplier = 0.2f;
// Generate the initial circular boundary GenerateAOIBoundary();
// Find nearby flags automatically List nearbyFlags = FindNearbyFlags();
// Calculate the Voronoi area of influence, considering nearby flags public void CalculateVoronoiAOI(List nearbyFlags) { List intersections = new List();
foreach (FlagController nearbyFlag in nearbyFlags) { Vector3 intersection1, intersection2; if (FindIntersections(this, nearbyFlag, out intersection1, out intersection2)) { intersections.Add(intersection1); intersections.Add(intersection2); } }
if (intersections.Count == 0) { GenerateAOIBoundary(); // No intersections, keep the full circle } else { FlattenCurveForIntersections(intersections); } }
// Find intersections between two flags' AOIs private bool FindIntersections(FlagController flag1, FlagController flag2, out Vector3 intersection1, out Vector3 intersection2) { intersection1 = Vector3.zero; intersection2 = Vector3.zero;
float d = Vector3.Distance(flag1.transform.position, flag2.transform.position); if (d > flag1.AOI + flag2.AOI || d < Mathf.Abs(flag1.AOI - flag2.AOI)) { return false; // No valid intersection }
float a = (flag1.AOI * flag1.AOI - flag2.AOI * flag2.AOI + d * d) / (2 * d); float h = Mathf.Sqrt(flag1.AOI * flag1.AOI - a * a);
Vector3 direction = (flag2.transform.position - flag1.transform.position).normalized; Vector3 p2 = flag1.transform.position + a * direction;
intersection1 = new Vector3(p2.x + h * direction.z, flag1.transform.position.y, p2.z - h * direction.x); intersection2 = new Vector3(p2.x - h * direction.z, flag1.transform.position.y, p2.z + h * direction.x);
return true; }
// Flatten the curve between two intersection points private void FlattenCurveForIntersections(List intersections) { intersections.Sort((a, b) => GetAngleFromCenter(a).CompareTo(GetAngleFromCenter(b)));
for (int i = 0; i < intersections.Count; i += 2) { Vector3 intersection1 = intersections[i]; Vector3 intersection2 = intersections[i + 1]; FlattenBetweenIntersections(intersection1, intersection2); } }
// Helper function to get the angle from the center of the current flag private float GetAngleFromCenter(Vector3 point) { Vector3 relativePoint = point - transform.position; float angle = Mathf.Atan2(relativePoint.z, relativePoint.x) * Mathf.Rad2Deg;
if (angle < 0) { angle += 360f; }
return angle; }
// Helper function to flatten the curve between two intersection points private void FlattenBetweenIntersections(Vector3 intersection1, Vector3 intersection2) { float angle1 = GetAngleFromCenter(intersection1); float angle2 = GetAngleFromCenter(intersection2);
// Main function to flatten a curve between two angles private void FlattenCurveBetweenAngles(float angle1, float angle2) { Debug.Log("{flagID} {angle1} {angle2}"); float angleStep = 360f / segments; int startIndex = Mathf.RoundToInt(angle1 / angleStep); int endIndex = Mathf.RoundToInt(angle2 / angleStep);
for (int i = startIndex + 1; i < endIndex; i++) { float t = (float)(i - startIndex) / (endIndex - startIndex); Vector3 newPos = Vector3.Lerp(startPos, endPos, t); lineRenderer.SetPosition(i, newPos); } }
// Helper function to get a looped element from the list private FlagController GetLooped(List list, int index) { return list[(index + list.Count) % list.Count]; } } [/code] Моя последняя попытка взяла центральную точку круга, а также получила позиции, в которые, по его мнению, он собирался переместить круг (рендеринг линий), и проверила, находится ли центральная точка в если нет, то мы поменяли местами отправленные углы, но это ничего не изменило.