Search Unity

Weird issue with method in OnEpisodeBegin()

Discussion in 'ML-Agents' started by Moonwilles, Apr 20, 2020.

  1. Moonwilles

    Moonwilles

    Joined:
    Apr 4, 2019
    Posts:
    10
    Hello :)

    I have just come across an extremely weird and frustrating issue.
    I have a project with two scripts: CubeLearningArea and CubeAgent.

    CubeLearningArea contains the following code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CubeLearningArea : MonoBehaviour
    6. {
    7.     public GameObject Floor;
    8.     private Mesh floorMesh;
    9.     private Bounds floorBounds;
    10.  
    11.     //[HideInInspector]
    12.     public float MaxSpawnX, MaxSpawnZ;
    13.  
    14.     public GameObject Linecaster;
    15.     public GameObject Cube;
    16.     public bool FoundPosition;
    17.  
    18.     public List<GameObject> Obstacles = new List<GameObject>();
    19.  
    20.     void Awake()
    21.     {
    22.         floorMesh = Floor.GetComponent<MeshFilter>().mesh;
    23.         floorBounds = floorMesh.bounds;
    24.  
    25.         MaxSpawnX = ((Floor.transform.localScale.x * floorMesh.bounds.size.x) / 2) - 0.75f;
    26.         MaxSpawnZ = ((Floor.transform.localScale.z * floorMesh.bounds.size.z) / 2) - 0.75f;
    27.  
    28.         //Linecaster = transform.Find("Linecaster").gameObject;
    29.  
    30.         // This works here but behaves wierdly in CubeAgent.cs (it doesn't spawn the obstacles).
    31.         //SpawnObstacles(10);
    32.     }
    33.  
    34.     public void Test()
    35.     {
    36.         Debug.LogWarning("Hello world!");
    37.     }
    38.  
    39.     // Start is called before the first frame update
    40.     void Start()
    41.     {
    42.         //for (int i = 0; i < 10; i++)
    43.         //{
    44.         //    LinecastCheck();
    45.         //}
    46.  
    47.         //LinecastCheck();
    48.     }
    49.  
    50.     public void ClearObstacles()
    51.     {
    52.         foreach (GameObject obstacle in Obstacles)
    53.         {
    54.             Destroy(obstacle);
    55.         }
    56.  
    57.         Obstacles.Clear();
    58.         Obstacles.TrimExcess();
    59.     }
    60.  
    61.     public void SpawnObstacles(int amount)
    62.     {
    63.         // The below code causes the editor to hang on a 2 by 2 map but works fine on a bigger level.
    64.         foreach (GameObject obstacle in Obstacles)
    65.         {
    66.             Destroy(obstacle);
    67.         }
    68.  
    69.         Obstacles.Clear();
    70.         Obstacles.TrimExcess();
    71.         //
    72.  
    73.         Debug.LogWarning("Starting loop!");
    74.  
    75.         for (int i = 0; i < amount; i++)
    76.         {
    77.             FoundPosition = false;
    78.             while (!FoundPosition)
    79.             {
    80.                 if (Physics.Linecast(Linecaster.transform.localPosition, this.gameObject.transform.position + new Vector3(Random.Range(-MaxSpawnX, MaxSpawnX), -2f, Random.Range(-MaxSpawnZ, MaxSpawnZ)), out RaycastHit info))
    81.                 {
    82.                     //Debug.LogWarning(info.collider.gameObject.name);
    83.                     if (info.collider.gameObject.name == "Floor")
    84.                     {
    85.                         //GameObject obstacle = Instantiate(Cube, info.point, Quaternion.identity);
    86.                         Obstacles.Add(Instantiate(Cube, info.point, Quaternion.identity));
    87.  
    88.                         FoundPosition = true;
    89.                     }
    90.                 }
    91.             }
    92.         }
    93.  
    94.         foreach (GameObject obstacle in Obstacles)
    95.         {
    96.             obstacle.name = "Obstacle";
    97.             obstacle.transform.SetParent(this.gameObject.transform);
    98.             obstacle.transform.Translate(Vector3.up * 1f);
    99.         }
    100.     }
    101.  
    102.     // Update is called once per frame
    103.     void Update()
    104.     {
    105.         MaxSpawnX = ((Floor.transform.localScale.x * floorMesh.bounds.size.x) / 2) - 0.75f;
    106.         MaxSpawnZ = ((Floor.transform.localScale.z * floorMesh.bounds.size.z) / 2) - 0.75f;
    107.     }
    108. }
    109.  


    CubeAgent contains the following code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using MLAgents;
    5. using MLAgents.Sensors;
    6.  
    7. public class CubeAgent : Agent
    8. {
    9.     Rigidbody rBody;
    10.     public Transform Target;
    11.     public float speed = 3;
    12.     public bool FoundPosition;
    13.     public bool IsColliding;
    14.  
    15.     public GameObject CubeLearningArea;
    16.     public bool FoundTargetPosition;
    17.  
    18.     public Camera Camera;
    19.     public GameObject Linecaster;
    20.     public GameObject TargetManager;
    21.  
    22.     void Awake()
    23.     {
    24.         // The below two lines are causing the editor to freeze, probably due to an infinite loop.
    25.         //CubeLearningArea = transform.parent.parent.gameObject;
    26.         //CubeLearningArea.GetComponent<CubeLearningArea>().SpawnObstacles(10);
    27.     }
    28.  
    29.     // Start is called before the first frame update
    30.     void Start()
    31.     {
    32.         CubeLearningArea = transform.parent.parent.gameObject;
    33.         Camera = transform.parent.parent.Find("Camera").GetComponent<Camera>();
    34.         Linecaster = transform.parent.parent.Find("Linecaster").gameObject;
    35.         rBody = GetComponent<Rigidbody>();
    36.         //this.transform.localPosition = new Vector3(Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX), 0.5f, Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ));
    37.         this.transform.position = LinecastCheck();
    38.         this.transform.Translate(Vector3.up * 1f);
    39.  
    40.         CubeLearningArea.GetComponent<CubeLearningArea>().SpawnObstacles(10);
    41.         //ObstacleSpawner.instance.HelloWorld();
    42.     }
    43.  
    44.     public override void OnEpisodeBegin()
    45.     {
    46.         //CubeLearningArea.GetComponent<CubeLearningArea>().ClearObstacles();
    47.  
    48.         if (this.transform.localPosition.y < -1f)
    49.         {
    50.             // If the agent fell, zero its momentum
    51.             this.rBody.angularVelocity = Vector3.zero;
    52.             this.rBody.velocity = Vector3.zero;
    53.             //this.transform.localPosition = new Vector3(Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX), 0.5f, Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ));
    54.             this.transform.position = LinecastCheck();
    55.             this.transform.Translate(Vector3.up * 1f);
    56.  
    57.             // The below line is causing the editor to hang for some reason.
    58.             CubeLearningArea.GetComponent<CubeLearningArea>().SpawnObstacles(10);
    59.         }
    60.  
    61.         // Move the target to a new spot
    62.         //Target.localPosition = new Vector3(Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX), 0.5f, Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ));
    63.         Target.position = LinecastCheck();
    64.         Target.Translate(Vector3.up * 0.3f);
    65.     }
    66.  
    67.     public override void CollectObservations(VectorSensor sensor)
    68.     {
    69.         // Target and agent positions
    70.         sensor.AddObservation(Target.localPosition);
    71.         sensor.AddObservation(this.transform.localPosition);
    72.  
    73.         // Agent velocity
    74.         sensor.AddObservation(rBody.velocity.x);
    75.         sensor.AddObservation(rBody.velocity.z);
    76.  
    77.         // Collision
    78.         sensor.AddObservation(IsColliding);
    79.     }
    80.  
    81.     public override void OnActionReceived(float[] vectorAction)
    82.     {
    83.         AddReward(-0.0002f);
    84.  
    85.         // Actions, size = 2
    86.         Vector3 controlSignal = Vector3.zero;
    87.         controlSignal.x = vectorAction[0];
    88.         controlSignal.z = vectorAction[1];
    89.         rBody.velocity = new Vector3(controlSignal.x * speed, rBody.velocity.y, controlSignal.z * speed);
    90.  
    91.         // Rewards
    92.         float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition);
    93.  
    94.         // Check for collisions
    95.         if (IsColliding)
    96.         {
    97.             AddReward(-1f / 4000);
    98.         }
    99.  
    100.         // Reached target
    101.         if (distanceToTarget < 1f)
    102.         {
    103.             SetReward(1.0f);
    104.             EndEpisode();
    105.         }
    106.  
    107.         // Fell off platform
    108.         if (this.transform.localPosition.y < -1f)
    109.         {
    110.             EndEpisode();
    111.         }
    112.     }
    113.  
    114.     public override float[] Heuristic()
    115.     {
    116.         var action = new float[2];
    117.         action[0] = Input.GetAxis("Horizontal");
    118.         action[1] = Input.GetAxis("Vertical");
    119.         return action;
    120.     }
    121.  
    122.     public void OnCollisionStay(Collision other)
    123.     {
    124.         if (!other.gameObject.CompareTag("Floor"))
    125.         {
    126.             //Debug.LogWarning("Colliding with something!");
    127.             IsColliding = true;
    128.         }
    129.     }
    130.  
    131.     public void OnCollisionExit(Collision other)
    132.     {
    133.         if (!other.gameObject.CompareTag("Floor"))
    134.         {
    135.             IsColliding = false;
    136.         }
    137.     }
    138.  
    139.     Vector3 LinecastCheck()
    140.     {
    141.         int layerMask = 1 << 8;
    142.         layerMask = ~layerMask;
    143.  
    144.         FoundPosition = false;
    145.         while (!FoundPosition)
    146.         {
    147.             //if (Physics.Linecast(Linecaster.transform.localPosition, new Vector3(Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX), 0f, Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ)), out RaycastHit info, layerMask))
    148.             if (Physics.Linecast(Linecaster.transform.localPosition, transform.parent.parent.gameObject.transform.position + new Vector3(Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnX), -2f, Random.Range(-CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ, CubeLearningArea.GetComponent<CubeLearningArea>().MaxSpawnZ)), out RaycastHit info, layerMask))
    149.             {
    150.                 //Debug.LogWarning("Object: " + info.collider.gameObject);
    151.                 if (info.collider.gameObject.tag == "Floor")
    152.                 {
    153.                     //Debug.LogWarning("Point: " + info.point);
    154.                     FoundPosition = true;
    155.                     return info.point;
    156.                 }
    157.             }
    158.         }
    159.         return Vector3.zero;
    160.     }
    161. }
    162.  


    Basically, the SpawnObstacles method in CubeLearningArea uses linecasts to spawn cubes in random positions on a plane. When called once, the script works fine. When it has to be called more than once, such as in OnEpisodeBegin, for example, the editor freezes as if there's an infinite loop when the agent falls off the map or reaches its target. But why is this happening if I am destroying every GameObject in the list before spawning new ones?? I initially thought it could have to do with the fact that I have some 40 learning areas, but this happens even with just one active area.

    I want to be able to destroy every spawned cube and respawn another 10 in different positions when the episode ends.

    Also, I'm sorry if I posted in the wrong forum. Since there's ML-Agents involved, I thought I'd post here as the people on here might be more familiar with how it works and would be able to provide better help.
     
  2. TreyK-47

    TreyK-47

    Unity Technologies

    Joined:
    Oct 22, 2019
    Posts:
    1,822
    I'll flag this for the team to have a look, but they may end up sending you to another Forum if it's not an ML Agents issue.

    Which version of C# and Python are you using?
     
  3. vincentpierre

    vincentpierre

    Joined:
    May 5, 2017
    Posts:
    160
    Calling Agent.EndEpisode() will terminate the episode and start a new one immediately. (Calling OnEpisodeBegin) If on top of that you destroy and spawn the agent again, the OnEpisodeBegin method will be called again.
    If you destroy your Agent (or disable it) it will end its episode automatically. I think you could either :
    - Destroy the Agent when it fails its task and spawn a new one
    - End the AgentEpisode and use OnEpisodeBegin to re-place it (no destruction, no respawn)
    - Create a flag to ensure SpawnObstacles cannot be called multiple times in a row
    Note that it is always good to make sure that OnEpisodeBegin can be called several times per fixed update because Python might request a new episode from all agents during training.
     
  4. Moonwilles

    Moonwilles

    Joined:
    Apr 4, 2019
    Posts:
    10
    Hello, sorry for taking so long to reply. I did manage to fix it in the end.