using System; using Unity.Mathematics; using UnityEngine; namespace Unity.MLAgents.Areas { /// /// The Training Ares Replicator allows for a training area object group to be replicated dynamically during runtime. /// [DefaultExecutionOrder(-5)] public class TrainingAreaReplicator : MonoBehaviour { /// /// The base training area to be replicated. /// public GameObject baseArea; /// /// The number of training areas to replicate. /// public int numAreas = 1; /// /// The separation between each training area. /// public float separation = 10f; int3 m_GridSize = new int3(1, 1, 1); int m_areaCount = 0; string m_TrainingAreaName; /// /// The size of the computed grid to pack the training areas into. /// public int3 GridSize => m_GridSize; /// /// The name of the training area. /// public string TrainingAreaName => m_TrainingAreaName; /// /// Called before the simulation begins to computed the grid size for distributing /// the replicated training areas and set the area name. /// public void Awake() { // Computes the Grid Size on Awake ComputeGridSize(); // Sets the TrainingArea name to the name of the base area. m_TrainingAreaName = baseArea.name; } /// /// Called after Awake and before the simulation begins and adds the training areas before /// the Academy begins. /// public void OnEnable() { // Adds the training are replicas during OnEnable to ensure they are added before the Academy begins its work. AddEnvironments(); } /// /// Computes the Grid Size for replicating the training area. /// void ComputeGridSize() { var rootNumAreas = Mathf.Pow(numAreas, 1.0f / 2.0f); m_GridSize.x = Mathf.CeilToInt(rootNumAreas); var zSize = Mathf.CeilToInt((float)numAreas / (m_GridSize.x * m_GridSize.y)); m_GridSize.z = zSize == 0 ? 1 : zSize; } /// /// Adds replicas of the training area to the scene. /// /// void AddEnvironments() { if (numAreas > m_GridSize.x * m_GridSize.y * m_GridSize.z) { throw new UnityAgentsException("The number of training areas that you have specified exceeds the size of the grid."); } for (int z = 0; z < m_GridSize.z; z++) { for (int x = 0; x < m_GridSize.x; x++) { if (m_areaCount == 0) { // Skip this first area since it already exists. m_areaCount = 1; } else if (m_areaCount < numAreas) { m_areaCount++; var area = Instantiate(baseArea, new Vector3(x * separation, 0, z * separation), Quaternion.identity); area.name = m_TrainingAreaName; } } } } } }