Загрузка данных
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)
{
if (count <= 0)
return new List<DataFragment>();
var config = mainConfigHolder.AgentInDepartmentConfig;
var fragmentBounds = ObjectToFrustumFitter.GetBoundsWithChildren(prefab.gameObject);
var width = fragmentBounds.size.x;
var normalizedWidth = width / prefab.transform.lossyScale.x;
var fragments = new List<DataFragment>(count);
var rows = Mathf.FloorToInt(Mathf.Sqrt(count));
rows = Mathf.Max(1, rows);
var columns = Mathf.CeilToInt((float)count / rows);
var scale = 1f;
prefab.transform.localScale = Vector3.one * scale;
var rowLength = CalculateAgentRowLength(columns, scale, normalizedWidth);
scale = platformWidth / rowLength;
scale = Mathf.Clamp(scale, config.MinScale, config.MaxScale);
prefab.transform.localScale = Vector3.one * scale;
var startPosition = CalculateAgentStartPosition(columns, rows, normalizedWidth, scale, spawnPlane);
var fragmentWidth = normalizedWidth * scale;
var positions = CalculateAgentPositions(count, rows, columns, startPosition, fragmentWidth, config);
CreateAgents(count, rows, columns, ids, positions, scale, fragments, prefab, spawnPlane);
SetupAgentsScales(fragments, rows, columns, config, scale);
AddRandomToAgentPosition(fragments, config);
return fragments;
}
private static Vector3[,] CalculateAgentPositions(
int agentCount,
int rows,
int columns,
Vector3 startPosition,
float fragmentWidth,
AgentInDepartmentConfig config)
{
var positions = new Vector3[rows, columns];
var placedElementsCount = 0;
var firstPositionX = startPosition.x;
var currentPositionZ = startPosition.z;
for (var rowIndex = 0; rowIndex < rows && placedElementsCount < agentCount; rowIndex++)
{
if (rowIndex != 0)
{
currentPositionZ -= fragmentWidth + fragmentWidth * config.SpacingVertical;
}
var elementsInCurrentRow = Math.Min(columns - 1, agentCount - 1 - placedElementsCount);
for (var columnIndex = 0; columnIndex < columns && placedElementsCount < agentCount; columnIndex++)
{
var currentPositionX = 0f;
if (columnIndex == 0 && rowIndex == 0)
{
currentPositionX = firstPositionX;
}
else
{
if (rowIndex == 0)
{
currentPositionX = positions[rowIndex, columnIndex - 1].x +
fragmentWidth +
fragmentWidth * config.SpacingHorizontal;
}
else
{
currentPositionX = Mathf.Lerp(
positions[0, 0].x,
positions[0, columns - 1].x,
(float)columnIndex / elementsInCurrentRow);
}
}
positions[rowIndex, columnIndex] = new Vector3(currentPositionX, startPosition.y, currentPositionZ);
placedElementsCount++;
}
}
return positions;
}
private void CreateAgents(
int agentCount,
int rows,
int columns,
List<long> sortedIds,
Vector3[,] positions,
float scale,
List<DataFragment> fragments,
DataFragment prefab,
Transform spawnPlane)
{
var spawnedElementsCount = 0;
for (var rowIndex = 0; rowIndex < rows && spawnedElementsCount < agentCount; rowIndex++)
{
for (var columnIndex = 0; columnIndex < columns && spawnedElementsCount < agentCount; columnIndex++)
{
var fragmentId = sortedIds[spawnedElementsCount];
var fragment = fragmentFactory.CreateAgent(
prefab,
positions[rowIndex, columnIndex],
spawnPlane.rotation,
Vector3.one * scale,
prefab.transform.parent,
fragmentId);
var data = dataManager.GetData<AsDataModel>(fragmentId);
fragment.SetAgent(data.AgentCount > 0);
fragments.Add(fragment);
spawnedElementsCount++;
}
}
}
private void SetupAgentsScales(
List<DataFragment> fragments,
int rows,
int columns,
AgentInDepartmentConfig config,
float maxScale)
{
var minScale = maxScale * config.MinScaleAspect;
var scaleStep = config.RowScaleStep;
for (var fragmentIndex = 0; fragmentIndex < fragments.Count; fragmentIndex++)
{
var fragment = fragments[fragmentIndex];
var rowIndex = fragmentIndex / columns;
var rowsFromBottom = rows - 1 - rowIndex;
var scale = minScale + rowsFromBottom * scaleStep;
scale = Mathf.Clamp(scale, minScale, maxScale);
fragment.transform.localScale = Vector3.one * scale;
}
}
private float CalculateAgentRowLength(int columns, float scale, float normalizedWidth)
{
var config = mainConfigHolder.AsInDepartmentConfig;
var rowLength = 0f;
var previousWidth = 0f;
for (var columnIndex = 0; columnIndex < columns; columnIndex++)
{
var currentWidth = normalizedWidth * scale;
rowLength += previousWidth / 2 + currentWidth / 2;
if (columnIndex < columns - 1)
rowLength += currentWidth / 2 * config.AsSpacingHorizontal;
previousWidth = currentWidth;
}
return rowLength;
}
private float CalculateAgentColumnLength(int rows, float scale, float normalizedWidth)
{
var config = mainConfigHolder.AsInDepartmentConfig;
var columnLength = 0f;
var previousWidth = 0f;
for (var rowIndex = 0; rowIndex < rows; rowIndex++)
{
var currentWidth = normalizedWidth * scale;
columnLength += previousWidth / 2 + currentWidth / 2;
if (rowIndex < rows - 1)
columnLength += currentWidth / 2 * config.AsSpacingVertical;
previousWidth = currentWidth;
}
return columnLength;
}
private Vector3 CalculateAgentStartPosition(
int columns,
int rows,
float normalizedWidth,
float scale,
Transform spawnPlane)
{
var startPosition = spawnPlane.position;
var startPositionX = startPosition.x - CalculateAgentRowLength(columns, scale, normalizedWidth) / 2;
var startPositionZ = startPosition.z + CalculateAgentColumnLength(rows, scale, normalizedWidth) / 2;
return new Vector3(startPositionX, startPosition.y, startPositionZ);
}
private static void AddRandomToAgentPosition(IList<DataFragment> fragments, AgentInDepartmentConfig config)
{
if (fragments == null || fragments.Count == 0)
return;
var finalPositions = new Vector3[fragments.Count];
var fragmentHalfSizes = new Vector2[fragments.Count];
for (var fragmentIndex = 0; fragmentIndex < fragments.Count; fragmentIndex++)
{
var bounds = ObjectToFrustumFitter.GetBoundsWithChildren(fragments[fragmentIndex].gameObject);
finalPositions[fragmentIndex] = fragments[fragmentIndex].transform.position;
fragmentHalfSizes[fragmentIndex] = new Vector2(bounds.size.x * 0.5f, bounds.size.z * 0.5f);
}
for (var fragmentIndex = 0; fragmentIndex < fragments.Count; fragmentIndex++)
{
var basePosition = finalPositions[fragmentIndex];
var halfExtents = fragmentHalfSizes[fragmentIndex];
var maxOffsetX = Mathf.Abs(config.AdditionalRandomPositionRangeX) * halfExtents.x * 2f;
var maxOffsetZ = Mathf.Abs(config.AdditionalRandomPositionRangeZ) * halfExtents.y * 2f;
var foundValidPosition = false;
for (var attemptIndex = 0;
attemptIndex < config.MaxPlacementIteration && !foundValidPosition;
attemptIndex++)
{
var candidatePosition = basePosition + new Vector3(
Random.Range(-maxOffsetX, maxOffsetX),
0f,
Random.Range(-maxOffsetZ, maxOffsetZ));
var overlaps = IsOverlaps(
fragmentIndex,
fragmentHalfSizes,
candidatePosition,
finalPositions,
halfExtents);
if (!overlaps)
{
finalPositions[fragmentIndex] = candidatePosition;
foundValidPosition = true;
}
else if ((attemptIndex + 1) % config.IterationBeforeClampOffset == 0)
{
maxOffsetX *= 0.85f;
maxOffsetZ *= 0.85f;
}
}
}
for (var fragmentIndex = 0; fragmentIndex < fragments.Count; fragmentIndex++)
fragments[fragmentIndex].transform.position = finalPositions[fragmentIndex];
}
private static bool IsOverlaps(
int currentIndex,
Vector2[] fragmentHalfSizes,
Vector3 candidatePosition,
Vector3[] finalPositions,
Vector2 halfExtents)
{
for (var otherIndex = 0; otherIndex < currentIndex; otherIndex++)
{
var otherHalfExtents = fragmentHalfSizes[otherIndex];
var delta = candidatePosition - finalPositions[otherIndex];
delta.y = 0f;
if (Mathf.Abs(delta.x) < halfExtents.x + otherHalfExtents.x &&
Mathf.Abs(delta.z) < halfExtents.y + otherHalfExtents.y)
{
return true;
}
}
return false;
}
}
}