using System.Collections; using System.Collections.Generic; using Unity.MLAgents; using UnityEngine; public class PushBlockEnvController : MonoBehaviour { [System.Serializable] public class PlayerInfo { public PushAgentCollab Agent; [HideInInspector] public Vector3 StartingPos; [HideInInspector] public Quaternion StartingRot; [HideInInspector] public Rigidbody Rb; } [System.Serializable] public class BlockInfo { public Transform T; [HideInInspector] public Vector3 StartingPos; [HideInInspector] public Quaternion StartingRot; [HideInInspector] public Rigidbody Rb; } /// /// Max Academy steps before this platform resets /// /// [Header("Max Environment Steps")] public int MaxEnvironmentSteps = 25000; /// /// The area bounds. /// [HideInInspector] public Bounds areaBounds; /// /// The ground. The bounds are used to spawn the elements. /// public GameObject ground; public GameObject area; Material m_GroundMaterial; //cached on Awake() /// /// We will be changing the ground material based on success/failue /// Renderer m_GroundRenderer; //List of Agents On Platform public List AgentsList = new List(); //List of Blocks On Platform public List BlocksList = new List(); public bool UseRandomAgentRotation = true; public bool UseRandomAgentPosition = true; public bool UseRandomBlockRotation = true; public bool UseRandomBlockPosition = true; private PushBlockSettings m_PushBlockSettings; private int m_NumberOfRemainingBlocks; private SimpleMultiAgentGroup m_AgentGroup; private int m_ResetTimer; void Start() { // Get the ground's bounds areaBounds = ground.GetComponent().bounds; // Get the ground renderer so we can change the material when a goal is scored m_GroundRenderer = ground.GetComponent(); // Starting material m_GroundMaterial = m_GroundRenderer.material; m_PushBlockSettings = FindObjectOfType(); // Initialize Blocks foreach (var item in BlocksList) { item.StartingPos = item.T.transform.position; item.StartingRot = item.T.transform.rotation; item.Rb = item.T.GetComponent(); } // Initialize TeamManager m_AgentGroup = new SimpleMultiAgentGroup(); foreach (var item in AgentsList) { item.StartingPos = item.Agent.transform.position; item.StartingRot = item.Agent.transform.rotation; item.Rb = item.Agent.GetComponent(); m_AgentGroup.RegisterAgent(item.Agent); } ResetScene(); } void FixedUpdate() { m_ResetTimer += 1; if (m_ResetTimer >= MaxEnvironmentSteps && MaxEnvironmentSteps > 0) { m_AgentGroup.GroupEpisodeInterrupted(); ResetScene(); } //Hurry Up Penalty m_AgentGroup.AddGroupReward(-0.5f / MaxEnvironmentSteps); } /// /// Use the ground's bounds to pick a random spawn position. /// public Vector3 GetRandomSpawnPos() { var foundNewSpawnLocation = false; var randomSpawnPos = Vector3.zero; while (foundNewSpawnLocation == false) { var randomPosX = Random.Range(-areaBounds.extents.x * m_PushBlockSettings.spawnAreaMarginMultiplier, areaBounds.extents.x * m_PushBlockSettings.spawnAreaMarginMultiplier); var randomPosZ = Random.Range(-areaBounds.extents.z * m_PushBlockSettings.spawnAreaMarginMultiplier, areaBounds.extents.z * m_PushBlockSettings.spawnAreaMarginMultiplier); randomSpawnPos = ground.transform.position + new Vector3(randomPosX, 1f, randomPosZ); if (Physics.CheckBox(randomSpawnPos, new Vector3(1.5f, 0.01f, 1.5f)) == false) { foundNewSpawnLocation = true; } } return randomSpawnPos; } /// /// Resets the block position and velocities. /// void ResetBlock(BlockInfo block) { // Get a random position for the block. block.T.position = GetRandomSpawnPos(); // Reset block velocity back to zero. block.Rb.velocity = Vector3.zero; // Reset block angularVelocity back to zero. block.Rb.angularVelocity = Vector3.zero; } /// /// Swap ground material, wait time seconds, then swap back to the regular material. /// IEnumerator GoalScoredSwapGroundMaterial(Material mat, float time) { m_GroundRenderer.material = mat; yield return new WaitForSeconds(time); // Wait for 2 sec m_GroundRenderer.material = m_GroundMaterial; } /// /// Called when the agent moves the block into the goal. /// public void ScoredAGoal(Collider col, float score) { print($"Scored {score} on {gameObject.name}"); //Decrement the counter m_NumberOfRemainingBlocks--; //Are we done? bool done = m_NumberOfRemainingBlocks == 0; //Disable the block col.gameObject.SetActive(false); //Give Agent Rewards m_AgentGroup.AddGroupReward(score); // Swap ground material for a bit to indicate we scored. StartCoroutine(GoalScoredSwapGroundMaterial(m_PushBlockSettings.goalScoredMaterial, 0.5f)); if (done) { //Reset assets m_AgentGroup.EndGroupEpisode(); ResetScene(); } } Quaternion GetRandomRot() { return Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0); } public void ResetScene() { m_ResetTimer = 0; //Random platform rotation var rotation = Random.Range(0, 4); var rotationAngle = rotation * 90f; area.transform.Rotate(new Vector3(0f, rotationAngle, 0f)); //Reset Agents foreach (var item in AgentsList) { var pos = UseRandomAgentPosition ? GetRandomSpawnPos() : item.StartingPos; var rot = UseRandomAgentRotation ? GetRandomRot() : item.StartingRot; item.Agent.transform.SetPositionAndRotation(pos, rot); item.Rb.velocity = Vector3.zero; item.Rb.angularVelocity = Vector3.zero; } //Reset Blocks foreach (var item in BlocksList) { var pos = UseRandomBlockPosition ? GetRandomSpawnPos() : item.StartingPos; var rot = UseRandomBlockRotation ? GetRandomRot() : item.StartingRot; item.T.transform.SetPositionAndRotation(pos, rot); item.Rb.velocity = Vector3.zero; item.Rb.angularVelocity = Vector3.zero; item.T.gameObject.SetActive(true); } //Reset counter m_NumberOfRemainingBlocks = BlocksList.Count; } }