Загрузка данных


using System;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Sbrt.Av.Gameplay
{
	public class AgentsInDepartmentGenerator
	{
		private readonly AgentsFragmentFactory fragmentFactory;
		private readonly MainConfigHolder mainConfigHolder;
		private readonly DataManager dataManager;

		public AgentsInDepartmentGenerator(AgentsFragmentFactory fragmentFactory, MainConfigHolder mainConfigHolder,
			DataManager dataManager)
		{
			this.fragmentFactory = fragmentFactory;
			this.mainConfigHolder = mainConfigHolder;
			this.dataManager = dataManager;
		}

		public List<DataFragment> SpawnAgents(int count, float platformWidth, List<long> ids, DataFragment prefab,
			Transform spawnPlane)
		{
			var config = mainConfigHolder.AgentInDepartmentConfig;
			var fragmentBounds = ObjectToFrustumFitter.GetBoundsWithChildren(prefab.gameObject);
			var width = fragmentBounds.size.x;
			var normalizeWidth = width / prefab.transform.lossyScale.x;
			var fragments = new List<DataFragment>(count);

			var rows = Mathf.FloorToInt(Mathf.Sqrt(count));
			var columns = Mathf.CeilToInt((float) count / rows);

			var scales = 1f;
			prefab.transform.localScale = Vector3.one * scales;

			var sumWidth = CalculateAgentRowLength(columns, scales, normalizeWidth);

			scales = platformWidth / sumWidth;

			scales = Mathf.Clamp(scales, config.MinScale, config.MaxScale);
			prefab.transform.localScale = Vector3.one * scales;

			var startPos = CalculateAgentStartPosition(columns, rows, normalizeWidth, scales, spawnPlane);

			var widths = normalizeWidth * scales;

			var positions = CalculateAgentPositions(count, rows, columns, startPos, widths, config);
			CreateAgent(count, rows, columns, ids, positions, scales, fragments, prefab, spawnPlane);
			SetupAgentsScales(fragments, config, scales);
			AddRandomToAgentPosition(fragments, config);

			return fragments;
		}

		private static Vector3[,] CalculateAgentPositions(int asCount, int rows, int columns, Vector3 startPos,
			float widths, AgentInDepartmentConfig config)
		{
			var positions = new Vector3[rows, columns];
			var posElements = 0;
			var firstX = startPos.x;
			var fpPosZ = startPos.z;
			for (var i = 0; i < rows && posElements < asCount; i++)
			{
				if (i != 0)
				{
					fpPosZ -= (widths / 2 + widths / 2 + widths * config.SpacingVertical);
				}

				var elementInColumns = Math.Min(columns - 1, asCount - 1 - posElements);
				for (var j = 0; j < columns && posElements < asCount; j++)
				{
					var fpPosX = 0f;

					if (j == 0 && i == 0)
						fpPosX = firstX;
					else
					{
						if (i == 0)
						{
							fpPosX = positions[i, j - 1].x + widths / 2 + widths / 2 +
							         widths * config.SpacingHorizontal;
						}
						else
						{
							fpPosX = Mathf.Lerp(positions[0, 0].x, positions[0, columns - 1].x,
								(float) j / elementInColumns);
						}
					}

					positions[i, j] = new Vector3(fpPosX, startPos.y, fpPosZ);
					posElements++;
				}
			}

			return positions;
		}

		private void CreateAgent(int asCount, int rows, int columns, List<long> sortedIds, Vector3[,] positions,
			float scales, List<DataFragment> asFragments, DataFragment asPrefab, Transform asPlane)
		{
			var spawnedElements = 0;
			for (var i = 0; i < rows && spawnedElements < asCount; i++)
			{
				for (var j = 0; j < columns && spawnedElements < asCount; j++)
				{
					var fragmentId = sortedIds[spawnedElements];
					var fragment = fragmentFactory.CreateAgent(asPrefab, positions[i, j], asPlane.rotation,
						Vector3.one * scales, asPrefab.transform.parent, fragmentId);

					var data = dataManager.GetData<AsDataModel>(fragmentId);
					fragment.SetAgent(data.AgentCount > 0);

					asFragments.Add(fragment);
					spawnedElements++;
				}
			}
		}

		private void SetupAgentsScales(List<DataFragment> fragments, AgentInDepartmentConfig config, float scales)
		{
			var maxScale = scales;
			var minScale = maxScale * config.MinScaleAspect;

			for (int i = 0; i < fragments.Count; i++)
			{
				var fragment = fragments[i];

				var t = 0f;
				if (i > 0)
					t = (float) i / fragments.Count;
				var scale = Mathf.Lerp(maxScale, minScale, t);
				fragment.transform.localScale = Vector3.one * scale;
			}
		}

		private float CalculateAgentRowLength(int columns, float asScale, float normalizeAsWidth)
		{
			var config = mainConfigHolder.AsInDepartmentConfig;
			var sumLength = 0f;
			var prevWidth = 0f;

			for (var j = 0; j < columns; j++)
			{
				var asWidth = normalizeAsWidth * asScale;
				sumLength += prevWidth / 2 + asWidth / 2;
				if (j < columns - 1)
					sumLength += asWidth / 2 * config.AsSpacingHorizontal;

				prevWidth = asWidth;
			}

			return sumLength;
		}

		private float CalculateAsColumnLength(int rows, float asScale, float normalizeAsWidth)
		{
			var config = mainConfigHolder.AsInDepartmentConfig;
			var sumLength = 0f;
			var prevWidth = 0f;

			for (var j = 0; j < rows; j++)
			{
				var asWidth = normalizeAsWidth * asScale;
				sumLength += (prevWidth / 2 + asWidth / 2);
				if (j < rows - 1)
					sumLength += asWidth / 2 * config.AsSpacingVertical;
				prevWidth = asWidth;
			}

			return sumLength;
		}

		private Vector3 CalculateAgentStartPosition(int columns, int rows, float normalizeWidth, float scale,
			Transform spawnPlane)
		{
			var startPos = spawnPlane.transform.position;
			var startPositionX =
				startPos.x - CalculateAgentRowLength(columns, scale, normalizeWidth) / 2;
			var startPositionZ =
				startPos.z + CalculateAsColumnLength(rows, scale, normalizeWidth) / 2;

			startPos = new Vector3(startPositionX, startPos.y, startPositionZ);
			return startPos;
		}

		private static void AddRandomToAgentPosition(IList<DataFragment> fragments, AgentInDepartmentConfig config)
		{
			if (fragments == null || fragments.Count == 0)
				return;

			var finalPositions = new Vector3[fragments.Count];
			var fragmentHalfSize = new Vector2[fragments.Count];

			for (int index = 0; index < fragments.Count; index++)
			{
				var bounds = ObjectToFrustumFitter.GetBoundsWithChildren(fragments[index].gameObject);
				finalPositions[index] = fragments[index].transform.position;
				fragmentHalfSize[index] = new Vector2(bounds.size.x * 0.5f, bounds.size.z * 0.5f);
			}

			for (int i = 0; i < fragments.Count; i++)
			{
				var basePosition = finalPositions[i];
				var halfExtents = fragmentHalfSize[i];
				var maxOffsetX = Mathf.Abs(config.AdditionalRandomPositionRangeX) * halfExtents.x * 2f;
				var maxOffsetZ = Mathf.Abs(config.AdditionalRandomPositionRangeZ) * halfExtents.y * 2f;

				var foundValidPosition = false;

				for (int attempt = 0; attempt < config.MaxPlacementIteration && !foundValidPosition; attempt++)
				{
					var candidatePosition = basePosition + new Vector3(
						Random.Range(-maxOffsetX, maxOffsetX), 0f,
						Random.Range(-maxOffsetZ, maxOffsetZ));

					var overlaps = IsOverlaps(i, fragmentHalfSize, candidatePosition, finalPositions, halfExtents);

					if (!overlaps)
					{
						finalPositions[i] = candidatePosition;
						foundValidPosition = true;
					}
					else if ((attempt + 1) % config.IterationBeforeClampOffset == 0)
					{
						maxOffsetX *= 0.85f;
						maxOffsetZ *= 0.85f;
					}
				}
			}

			for (int i = 0; i < fragments.Count; i++)
				fragments[i].transform.position = finalPositions[i];
		}

		private static bool IsOverlaps(int i, Vector2[] fragmentHalfSize, Vector3 candidatePosition,
			Vector3[] finalPositions,
			Vector2 halfExtents)
		{
			var overlaps = false;
			for (int j = 0; j < i; j++)
			{
				var otherHalfExtents = fragmentHalfSize[j];
				var delta = candidatePosition - finalPositions[j];
				delta.y = 0f;

				if (Mathf.Abs(delta.x) < (halfExtents.x + otherHalfExtents.x) &&
				    Mathf.Abs(delta.z) < (halfExtents.y + otherHalfExtents.y))
				{
					overlaps = true;
					break;
				}
			}

			return overlaps;
		}
	}
}