Загрузка данных
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;
}
}
}